From e2eda444afbe82e9591fe198eef339227f90a616 Mon Sep 17 00:00:00 2001 From: Paul Wisbey Date: Mon, 3 Mar 2014 19:04:23 +0000 Subject: [PATCH] [SRUK] Initial copy from Tizen 2.2 version Signed-off-by: Paul Wisbey Change-Id: I1088d752201827b02c6b83015c00ff158f58e2ad --- .gitignore | 56 + LICENSE | 205 + automated-tests/.gitignore | 7 + automated-tests/_export_desktop.sh | 33 + automated-tests/_export_env.sh | 17 + automated-tests/_export_sbs.sh | 17 + automated-tests/_export_target_env.sh | 17 + automated-tests/build.sh | 70 + automated-tests/build_out.sh | 3 + automated-tests/coverage.mk | 1 + automated-tests/coverage.sh | 22 + .../dali-internal-test-suite/master-makefile.mk | 53 + automated-tests/dali-internal-test-suite/tc-gen.sh | 1 + .../dali-internal-test-suite/text-input/.gitignore | 1 + .../dali-internal-test-suite/text-input/Makefile | 1 + .../dali-internal-test-suite/text-input/file.list | 2 + .../dali-internal-test-suite/text-input/tslist | 1 + .../text-input/utc-Dali-TextInput.cpp | 145 + .../dali-internal-test-suite/text-view/.gitignore | 5 + .../dali-internal-test-suite/text-view/Makefile | 1 + .../dali-internal-test-suite/text-view/file.list | 6 + .../dali-internal-test-suite/text-view/tslist | 5 + .../text-view/utc-Dali-TextView-HelperAndDebug.cpp | 304 ++ .../utc-Dali-TextView-Processor-Types.cpp | 311 ++ .../text-view/utc-Dali-TextView-Processor.cpp | 428 ++ .../utc-Dali-TextView-Relayout-Utilities.cpp | 856 ++++ .../text-view/utc-Dali-TextView.cpp | 2917 +++++++++++ .../utc-MODULE-CLASS.cpp.in | 96 + automated-tests/dali-test-suite/.gitignore | 1 + .../dali-test-suite/alignment/.gitignore | 1 + automated-tests/dali-test-suite/alignment/Makefile | 1 + .../dali-test-suite/alignment/file.list | 2 + automated-tests/dali-test-suite/alignment/tslist | 1 + .../alignment/utc-Dali-Alignment.cpp | 1127 +++++ .../dali-test-suite/bubble-emitter/.gitignore | 1 + .../dali-test-suite/bubble-emitter/Makefile | 1 + .../dali-test-suite/bubble-emitter/file.list | 2 + .../dali-test-suite/bubble-emitter/tslist | 1 + .../bubble-emitter/utc-Dali-BubbleEmitter.cpp | 423 ++ automated-tests/dali-test-suite/builder/.gitignore | 2 + automated-tests/dali-test-suite/builder/Makefile | 1 + automated-tests/dali-test-suite/builder/file.list | 3 + automated-tests/dali-test-suite/builder/tslist | 2 + .../dali-test-suite/builder/utc-Dali-Builder.cpp | 393 ++ .../builder/utc-Dali-JsonParser.cpp | 640 +++ automated-tests/dali-test-suite/buttons/.gitignore | 3 + automated-tests/dali-test-suite/buttons/Makefile | 1 + automated-tests/dali-test-suite/buttons/file.list | 4 + automated-tests/dali-test-suite/buttons/tslist | 3 + .../dali-test-suite/buttons/utc-Dali-Button.cpp | 397 ++ .../buttons/utc-Dali-CheckBoxButton.cpp | 230 + .../buttons/utc-Dali-PushButton.cpp | 1263 +++++ automated-tests/dali-test-suite/cluster/.gitignore | 1 + automated-tests/dali-test-suite/cluster/Makefile | 1 + automated-tests/dali-test-suite/cluster/file.list | 2 + automated-tests/dali-test-suite/cluster/tslist | 1 + .../dali-test-suite/cluster/utc-Dali-Cluster.cpp | 224 + automated-tests/dali-test-suite/control/.gitignore | 2 + automated-tests/dali-test-suite/control/Makefile | 1 + .../dali-test-suite/control/dummy-control.h | 237 + automated-tests/dali-test-suite/control/file.list | 3 + automated-tests/dali-test-suite/control/tslist | 2 + .../dali-test-suite/control/utc-Dali-Control.cpp | 368 ++ .../control/utc-Dali-ControlImpl.cpp | 829 ++++ .../dali-test-suite/default-controls/.gitignore | 1 + .../dali-test-suite/default-controls/Makefile | 1 + .../dali-test-suite/default-controls/file.list | 2 + .../dali-test-suite/default-controls/tslist | 1 + .../default-controls/utc-Dali-DefaultControls.cpp | 258 + .../dali-test-suite/focus-manager/.gitignore | 3 + .../dali-test-suite/focus-manager/Makefile | 1 + .../dali-test-suite/focus-manager/file.list | 4 + .../dali-test-suite/focus-manager/tslist | 3 + .../focus-manager/utc-Dali-FocusManager.cpp | 1070 +++++ .../utc-Dali-KeyInputFocusManager.cpp | 336 ++ .../utc-Dali-KeyboardFocusManager.cpp | 738 +++ .../dali-test-suite/item-view/.gitignore | 8 + automated-tests/dali-test-suite/item-view/Makefile | 1 + .../dali-test-suite/item-view/file.list | 9 + automated-tests/dali-test-suite/item-view/tslist | 8 + .../item-view/utc-Dali-AlbumLayout.cpp | 402 ++ .../item-view/utc-Dali-DepthLayout.cpp | 658 +++ .../item-view/utc-Dali-GridLayout.cpp | 579 +++ .../item-view/utc-Dali-ItemLayout.cpp | 171 + .../item-view/utc-Dali-ItemView.cpp | 575 +++ .../item-view/utc-Dali-NavigationLayout.cpp | 616 +++ .../item-view/utc-Dali-RollLayout.cpp | 471 ++ .../item-view/utc-Dali-SpiralLayout.cpp | 606 +++ automated-tests/dali-test-suite/master-makefile.mk | 53 + .../dali-test-suite/navigation-frame/.gitignore | 2 + .../dali-test-suite/navigation-frame/Makefile | 1 + .../dali-test-suite/navigation-frame/file.list | 3 + .../dali-test-suite/navigation-frame/tslist | 2 + .../utc-Dali-NavigationControl.cpp | 388 ++ .../navigation-frame/utc-Dali-Page.cpp | 262 + .../dali-test-suite/page-turn-view/.gitignore | 1 + .../dali-test-suite/page-turn-view/Makefile | 1 + .../dali-test-suite/page-turn-view/file.list | 2 + .../dali-test-suite/page-turn-view/tslist | 1 + .../page-turn-view/utc-Dali-PageTurnView.cpp | 664 +++ automated-tests/dali-test-suite/popup/.gitignore | 1 + automated-tests/dali-test-suite/popup/Makefile | 1 + automated-tests/dali-test-suite/popup/file.list | 2 + automated-tests/dali-test-suite/popup/tslist | 1 + .../dali-test-suite/popup/utc-Dali-Popup.cpp | 470 ++ .../dali-test-suite/scroll-view/.gitignore | 1 + .../dali-test-suite/scroll-view/Makefile | 1 + .../dali-test-suite/scroll-view/file.list | 3 + automated-tests/dali-test-suite/scroll-view/tslist | 2 + .../scroll-view/utc-Dali-ScrollView.cpp | 1579 ++++++ .../scroll-view/utc-Dali-ScrollViewEffect.cpp | 934 ++++ .../dali-test-suite/selectors/.gitignore | 1 + automated-tests/dali-test-suite/selectors/Makefile | 1 + .../dali-test-suite/selectors/file.list | 2 + automated-tests/dali-test-suite/selectors/tslist | 1 + .../selectors/utc-Dali-RotatingSelector.cpp | 277 ++ .../dali-test-suite/shader-effects/.gitignore | 23 + .../dali-test-suite/shader-effects/Makefile | 1 + .../dali-test-suite/shader-effects/file.list | 23 + .../dali-test-suite/shader-effects/tslist | 22 + .../shader-effects/utc-Dali-BendyEffect.cpp | 193 + .../shader-effects/utc-Dali-BlindEffect.cpp | 168 + .../shader-effects/utc-Dali-BloomView.cpp | 194 + .../shader-effects/utc-Dali-CarouselEffect.cpp | 179 + .../shader-effects/utc-Dali-DisplacementEffect.cpp | 177 + .../shader-effects/utc-Dali-DissolveEffect.cpp | 197 + .../utc-Dali-DistanceFieldEffect.cpp | 202 + .../shader-effects/utc-Dali-GaussianBlurView.cpp | 232 + .../shader-effects/utc-Dali-IrisEffect.cpp | 179 + .../shader-effects/utc-Dali-MaskEffect.cpp | 101 + .../utc-Dali-NinePatchMaskEffect.cpp | 109 + .../shader-effects/utc-Dali-OverlayEffect.cpp | 124 + .../shader-effects/utc-Dali-PageTurnEffect.cpp | 132 + .../shader-effects/utc-Dali-Ripple2DEffect.cpp | 176 + .../shader-effects/utc-Dali-RippleEffect.cpp | 187 + .../shader-effects/utc-Dali-ShadowView.cpp | 190 + .../shader-effects/utc-Dali-ShearEffect.cpp | 196 + .../shader-effects/utc-Dali-SoftButtonEffect.cpp | 110 + .../shader-effects/utc-Dali-SpotEffect.cpp | 180 + .../utc-Dali-SquareDissolveEffect.cpp | 204 + .../shader-effects/utc-Dali-SwirlEffect.cpp | 192 + .../shader-effects/utc-Dali-WaterEffect.cpp | 378 ++ automated-tests/dali-test-suite/slider/.gitignore | 1 + automated-tests/dali-test-suite/slider/Makefile | 1 + automated-tests/dali-test-suite/slider/file.list | 2 + automated-tests/dali-test-suite/slider/tslist | 1 + .../dali-test-suite/slider/utc-Dali-Slider.cpp | 199 + .../dali-test-suite/super-blur-view/.gitignore | 1 + .../dali-test-suite/super-blur-view/Makefile | 1 + .../dali-test-suite/super-blur-view/file.list | 2 + .../dali-test-suite/super-blur-view/tslist | 1 + .../super-blur-view/utc-Dali-SuperBlurView.cpp | 240 + .../dali-test-suite/table-view/.gitignore | 1 + .../dali-test-suite/table-view/Makefile | 1 + .../dali-test-suite/table-view/file.list | 2 + automated-tests/dali-test-suite/table-view/tslist | 1 + .../table-view/utc-Dali-TableView.cpp | 575 +++ automated-tests/dali-test-suite/tc-gen.sh | 58 + .../dali-test-suite/text-input/.gitignore | 1 + .../dali-test-suite/text-input/Makefile | 1 + .../dali-test-suite/text-input/file.list | 2 + automated-tests/dali-test-suite/text-input/tslist | 1 + .../text-input/utc-Dali-TextInput.cpp | 531 ++ .../dali-test-suite/text-view/.gitignore | 2 + automated-tests/dali-test-suite/text-view/Makefile | 1 + .../dali-test-suite/text-view/file.list | 3 + automated-tests/dali-test-suite/text-view/tslist | 2 + .../text-view/utc-Dali-MarkupProcessor.cpp | 418 ++ .../text-view/utc-Dali-TextView.cpp | 839 ++++ automated-tests/dali-test-suite/toolbar/.gitignore | 2 + automated-tests/dali-test-suite/toolbar/Makefile | 1 + automated-tests/dali-test-suite/toolbar/file.list | 2 + automated-tests/dali-test-suite/toolbar/tslist | 1 + .../dali-test-suite/toolbar/utc-Dali-ToolBar.cpp | 288 ++ .../dali-test-suite/transition-effects/.gitignore | 3 + .../dali-test-suite/transition-effects/Makefile | 1 + .../dali-test-suite/transition-effects/file.list | 2 + .../dali-test-suite/transition-effects/tslist | 1 + .../utc-Dali-CubeTransitionEffect.cpp | 965 ++++ .../dali-test-suite/ui-builder/.gitignore | 1 + .../dali-test-suite/ui-builder/file.list | 2 + .../dali-test-suite/utc-MODULE-CLASS.cpp.in | 96 + automated-tests/dali-test-suite/view/.gitignore | 1 + automated-tests/dali-test-suite/view/Makefile | 1 + automated-tests/dali-test-suite/view/file.list | 2 + automated-tests/dali-test-suite/view/tslist | 1 + .../dali-test-suite/view/utc-Dali-View.cpp | 413 ++ .../dali-toolkit-test-suite-utils.h | 26 + .../toolkit-accessibility-manager.cpp | 212 + .../toolkit-accessibility-manager.h | 75 + .../dali-toolkit-test-utils/toolkit-adaptor.cpp | 249 + .../dali-toolkit-test-utils/toolkit-adaptor.h | 259 + .../toolkit-application.cpp | 103 + .../dali-toolkit-test-utils/toolkit-application.h | 91 + .../toolkit-clipboard-event-notifier.cpp | 106 + .../toolkit-clipboard-event-notifier.h | 68 + .../toolkit-orientation.cpp | 143 + .../dali-toolkit-test-utils/toolkit-orientation.h | 129 + .../toolkit-physical-keyboard.cpp | 119 + .../toolkit-physical-keyboard.h | 68 + .../toolkit-style-monitor.cpp | 138 + .../toolkit-style-monitor.h | 130 + .../toolkit-test-application.h | 69 + .../dali-toolkit-test-utils/toolkit-timer.cpp | 168 + .../dali-toolkit-test-utils/toolkit-timer.h | 69 + automated-tests/debug.sh | 53 + automated-tests/debug_target.sh | 52 + automated-tests/execute.sh | 42 + automated-tests/execute_target.sh | 39 + automated-tests/rules.mk.in | 11 + automated-tests/tbp.pl | 339 ++ automated-tests/tet_scen | 126 + automated-tests/tetbuild.cfg | 5 + automated-tests/tetclean.cfg | 5 + automated-tests/tetexec.cfg | 5 + build/slp/.gitignore | 19 + build/slp/Makefile.am | 55 + build/slp/README | 3 + build/slp/configure.ac | 96 + build/slp/dali-toolkit.pc.in | 12 + build/slp/dali-toolkit/Makefile.am | 215 + .../public-api/controls/alignment/alignment.h | 201 + .../controls/bubble-effect/bubble-emitter.h | 182 + .../public-api/controls/buttons/button.h | 156 + .../public-api/controls/buttons/push-button.h | 366 ++ .../public-api/controls/cluster/cluster-style.h | 174 + .../public-api/controls/control-impl.h | 592 +++ capi/dali-toolkit/public-api/controls/control.h | 349 ++ .../default-controls/push-button-factory.h | 90 + .../controls/default-controls/solid-color-actor.h | 55 + .../controls/image-view/masked-image-view.h | 270 ++ .../dali-toolkit/public-api/controls/popup/popup.h | 277 ++ .../controls/scroll-component/scroll-bar.h | 125 + .../controls/scroll-component/scroll-component.h | 101 + .../controls/scrollable/item-view/grid-layout.h | 288 ++ .../controls/scrollable/item-view/item-factory.h | 68 + .../controls/scrollable/item-view/item-layout.h | 367 ++ .../controls/scrollable/item-view/item-view.h | 280 ++ .../scroll-view/scroll-view-cube-effect.h | 139 + .../scroll-view/scroll-view-custom-effect.h | 433 ++ .../scrollable/scroll-view/scroll-view-effect.h | 88 + .../scroll-view/scroll-view-page-spiral-effect.h | 113 + .../scroll-view/scroll-view-slide-effect.h | 159 + .../scroll-view/scroll-view-twist-effect.h | 163 + .../controls/scrollable/scroll-view/scroll-view.h | 1055 ++++ .../public-api/controls/scrollable/scrollable.h | 205 + .../controls/super-blur-view/super-blur-view.h | 165 + .../public-api/controls/text-input/text-input.h | 584 +++ .../public-api/controls/text-view/text-view.h | 641 +++ .../public-api/dali-toolkit-capi-internal.h | 65 + capi/dali-toolkit/public-api/enums.h | 71 + .../public-api/factory/localized-control-factory.h | 100 + capi/dali-toolkit/public-api/file.list | 83 + .../public-api/focus-manager/focus-manager.h | 358 ++ .../focus-manager/keyboard-focus-manager.h | 245 + .../public-api/markup-processor/markup-processor.h | 167 + .../public-api/shader-effects/dissolve-effect.h | 94 + .../shader-effects/image-region-effect.h | 97 + .../public-api/shader-effects/iris-effect.h | 120 + .../public-api/shader-effects/mask-effect.h | 76 + .../shader-effects/nine-patch-mask-effect.h | 77 + .../shader-effects/page-turn-book-spine-effect.h | 99 + .../public-api/shader-effects/page-turn-effect.h | 167 + .../public-api/shader-effects/ripple-effect.h | 108 + .../public-api/shader-effects/ripple2d-effect.h | 95 + .../public-api/shader-effects/swirl-effect.h | 105 + dali-toolkit.manifest | 8 + dali-toolkit/dali-toolkit.h | 88 + dali-toolkit/images/00_popup_bg.png | Bin 0 -> 4323 bytes dali-toolkit/images/00_popup_bottom_bg.png | Bin 0 -> 2858 bytes dali-toolkit/images/00_popup_bubble_bg.png | Bin 0 -> 4235 bytes .../images/00_popup_bubble_tail_bottom.png | Bin 0 -> 3782 bytes dali-toolkit/images/00_popup_button_bg.png | Bin 0 -> 2975 bytes dali-toolkit/images/00_popup_button_pressed.png | Bin 0 -> 1480 bytes dali-toolkit/images/B16-8_TTS_focus.png | Bin 0 -> 3097 bytes dali-toolkit/images/copy_paste_icon_clipboard.png | Bin 0 -> 1374 bytes dali-toolkit/images/copy_paste_icon_copy.png | Bin 0 -> 1277 bytes dali-toolkit/images/copy_paste_icon_cut.png | Bin 0 -> 3745 bytes dali-toolkit/images/copy_paste_icon_paste.png | Bin 0 -> 1329 bytes dali-toolkit/images/copy_paste_icon_select.png | Bin 0 -> 1678 bytes dali-toolkit/images/copy_paste_icon_select_all.png | Bin 0 -> 1643 bytes dali-toolkit/images/copypanelLine.png | Bin 0 -> 154 bytes dali-toolkit/images/cursor.png | Bin 0 -> 2821 bytes dali-toolkit/images/cutCopyPastePopup_bg.png | Bin 0 -> 4264 bytes dali-toolkit/images/file.list | 4 + dali-toolkit/images/insertpoint-icon.png | Bin 0 -> 5136 bytes dali-toolkit/images/keyboard_focus.png | Bin 0 -> 3020 bytes dali-toolkit/images/magnifier-image-frame.png | Bin 0 -> 1222 bytes dali-toolkit/images/magnifier.png | Bin 0 -> 5191 bytes dali-toolkit/images/overshoot_ripple.png | Bin 0 -> 281234 bytes dali-toolkit/images/popup_bg.png | Bin 0 -> 3869 bytes dali-toolkit/images/popup_scroll.png | Bin 0 -> 1304 bytes dali-toolkit/images/popup_tail_down.png | Bin 0 -> 3765 bytes dali-toolkit/images/popup_tail_left.png | Bin 0 -> 3685 bytes dali-toolkit/images/popup_tail_right.png | Bin 0 -> 3731 bytes dali-toolkit/images/popup_tail_up.png | Bin 0 -> 3712 bytes dali-toolkit/images/scroll_overshoot.png | Bin 0 -> 2831 bytes dali-toolkit/images/slider-popup-arrow.png | Bin 0 -> 2916 bytes dali-toolkit/images/slider-popup.png | Bin 0 -> 233 bytes dali-toolkit/images/slider-skin-handle.png | Bin 0 -> 5721 bytes dali-toolkit/images/slider-skin-progress.png | Bin 0 -> 389 bytes dali-toolkit/images/slider-skin.png | Bin 0 -> 1718 bytes .../text-input-selection-handle-left-press.png | Bin 0 -> 4637 bytes .../images/text-input-selection-handle-left.png | Bin 0 -> 4637 bytes .../text-input-selection-handle-right-press.png | Bin 0 -> 4689 bytes .../images/text-input-selection-handle-right.png | Bin 0 -> 4689 bytes dali-toolkit/internal/builder/builder-actor.cpp | 230 + .../internal/builder/builder-animations.cpp | 336 ++ dali-toolkit/internal/builder/builder-control.cpp | 112 + .../internal/builder/builder-declarations.h | 43 + dali-toolkit/internal/builder/builder-filesystem.h | 48 + dali-toolkit/internal/builder/builder-get-is.inl.h | 388 ++ dali-toolkit/internal/builder/builder-impl.cpp | 693 +++ dali-toolkit/internal/builder/builder-impl.h | 196 + .../internal/builder/builder-set-property.cpp | 488 ++ dali-toolkit/internal/builder/builder-signals.cpp | 467 ++ dali-toolkit/internal/builder/json-parser-impl.cpp | 186 + dali-toolkit/internal/builder/json-parser-impl.h | 156 + .../internal/builder/json-parser-state.cpp | 966 ++++ dali-toolkit/internal/builder/json-parser-state.h | 313 ++ dali-toolkit/internal/builder/optional-value.h | 92 + .../internal/builder/tree-node-manipulator.cpp | 522 ++ .../internal/builder/tree-node-manipulator.h | 260 + .../internal/controls/alignment/alignment-impl.cpp | 642 +++ .../internal/controls/alignment/alignment-impl.h | 141 + .../controls/bloom-view/bloom-view-impl.cpp | 595 +++ .../internal/controls/bloom-view/bloom-view-impl.h | 191 + .../controls/bubble-effect/bubble-emitter-impl.cpp | 393 ++ .../controls/bubble-effect/bubble-emitter-impl.h | 241 + .../internal/controls/buttons/button-impl.cpp | 241 + .../internal/controls/buttons/button-impl.h | 243 + .../controls/buttons/button-painter-impl.h | 109 + .../check-box-button-default-painter-impl.cpp | 1051 ++++ .../check-box-button-default-painter-impl.h | 340 ++ .../controls/buttons/check-box-button-impl.cpp | 271 ++ .../controls/buttons/check-box-button-impl.h | 272 ++ .../buttons/check-box-button-painter-impl.h | 99 + .../buttons/push-button-default-painter-impl.cpp | 1304 +++++ .../buttons/push-button-default-painter-impl.h | 355 ++ .../internal/controls/buttons/push-button-impl.cpp | 553 +++ .../internal/controls/buttons/push-button-impl.h | 422 ++ .../controls/buttons/push-button-painter-impl.h | 131 + .../internal/controls/cluster/cluster-impl.cpp | 563 +++ .../internal/controls/cluster/cluster-impl.h | 316 ++ .../controls/cluster/cluster-style-impl.cpp | 641 +++ .../internal/controls/cluster/cluster-style-impl.h | 246 + .../controls/effects-view/effects-view-impl.cpp | 524 ++ .../controls/effects-view/effects-view-impl.h | 275 ++ .../gaussian-blur-view/gaussian-blur-view-impl.cpp | 661 +++ .../gaussian-blur-view/gaussian-blur-view-impl.h | 219 + .../controls/image-view/image-view-impl.cpp | 223 + .../internal/controls/image-view/image-view-impl.h | 208 + .../controls/image-view/masked-image-view-impl.cpp | 634 +++ .../controls/image-view/masked-image-view-impl.h | 272 ++ .../internal/controls/magnifier/magnifier-impl.cpp | 386 ++ .../internal/controls/magnifier/magnifier-impl.h | 162 + .../controls/navigation-frame/navigation-bar.cpp | 122 + .../controls/navigation-frame/navigation-bar.h | 139 + .../navigation-frame/navigation-control-impl.cpp | 454 ++ .../navigation-frame/navigation-control-impl.h | 267 ++ .../navigation-frame/navigation-title-bar.cpp | 173 + .../navigation-frame/navigation-title-bar.h | 95 + .../navigation-frame/navigation-tool-bar.cpp | 181 + .../navigation-frame/navigation-tool-bar.h | 101 + .../controls/navigation-frame/page-impl.cpp | 187 + .../internal/controls/navigation-frame/page-impl.h | 179 + .../page-turn-landscape-view-impl.cpp | 141 + .../page-turn-view/page-turn-landscape-view-impl.h | 102 + .../page-turn-portrait-view-impl.cpp | 165 + .../page-turn-view/page-turn-portrait-view-impl.h | 113 + .../page-turn-view/page-turn-view-impl.cpp | 1063 ++++ .../controls/page-turn-view/page-turn-view-impl.h | 386 ++ .../internal/controls/popup/popup-impl.cpp | 1048 ++++ dali-toolkit/internal/controls/popup/popup-impl.h | 354 ++ .../internal/controls/popup/popup-style-impl.cpp | 114 + .../internal/controls/popup/popup-style-impl.h | 113 + .../internal/controls/relayout-controller-impl.cpp | 233 + .../internal/controls/relayout-controller-impl.h | 109 + .../internal/controls/relayout-controller.cpp | 94 + .../internal/controls/relayout-controller.h | 74 + dali-toolkit/internal/controls/relayout-helper.cpp | 95 + dali-toolkit/internal/controls/relayout-helper.h | 54 + .../controls/scroll-component/scroll-bar-impl.cpp | 615 +++ .../controls/scroll-component/scroll-bar-impl.h | 169 + .../scroll-component/scroll-component-impl.cpp | 82 + .../scroll-component/scroll-component-impl.h | 111 + .../scrollable/item-view/item-view-impl.cpp | 1586 ++++++ .../controls/scrollable/item-view/item-view-impl.h | 595 +++ .../scrollable/scroll-view/scroll-base-impl.cpp | 158 + .../scrollable/scroll-view/scroll-base-impl.h | 230 + .../scroll-overshoot-indicator-impl.cpp | 629 +++ .../scroll-view/scroll-overshoot-indicator-impl.h | 388 ++ .../scroll-view-carousel-effect-impl.cpp | 307 ++ .../scroll-view/scroll-view-carousel-effect-impl.h | 115 + .../scroll-view/scroll-view-cube-effect-impl.cpp | 393 ++ .../scroll-view/scroll-view-cube-effect-impl.h | 127 + .../scroll-view/scroll-view-custom-effect-impl.cpp | 1295 +++++ .../scroll-view/scroll-view-custom-effect-impl.h | 418 ++ .../scroll-view/scroll-view-depth-effect-impl.cpp | 401 ++ .../scroll-view/scroll-view-depth-effect-impl.h | 119 + .../scroll-view/scroll-view-effect-impl.cpp | 70 + .../scroll-view/scroll-view-effect-impl.h | 136 + .../scroll-view/scroll-view-helper-functions.cpp | 62 + .../scroll-view/scroll-view-helper-functions.h | 69 + .../scrollable/scroll-view/scroll-view-impl.cpp | 2628 ++++++++++ .../scrollable/scroll-view/scroll-view-impl.h | 931 ++++ .../scroll-view-page-carousel-effect-impl.cpp | 246 + .../scroll-view-page-carousel-effect-impl.h | 113 + .../scroll-view-page-cube-effect-impl.cpp | 327 ++ .../scroll-view-page-cube-effect-impl.h | 114 + .../scroll-view-page-spiral-effect-impl.cpp | 424 ++ .../scroll-view-page-spiral-effect-impl.h | 113 + .../scroll-view/scroll-view-slide-effect-impl.cpp | 651 +++ .../scroll-view/scroll-view-slide-effect-impl.h | 223 + .../scroll-view/scroll-view-twist-effect-impl.cpp | 731 +++ .../scroll-view/scroll-view-twist-effect-impl.h | 230 + .../scroll-view/scroll-view-wobble-effect-impl.cpp | 314 ++ .../scroll-view/scroll-view-wobble-effect-impl.h | 160 + .../controls/scrollable/scrollable-impl.cpp | 198 + .../internal/controls/scrollable/scrollable-impl.h | 235 + .../controls/selectors/rotating-selector-impl.cpp | 273 ++ .../controls/selectors/rotating-selector-impl.h | 186 + .../controls/shadow-view/shadow-view-impl.cpp | 390 ++ .../controls/shadow-view/shadow-view-impl.h | 199 + .../internal/controls/slider/slider-impl.cpp | 1081 +++++ .../internal/controls/slider/slider-impl.h | 728 +++ .../internal/controls/style-change-processor.cpp | 122 + .../internal/controls/style-change-processor.h | 125 + .../super-blur-view/super-blur-view-impl.cpp | 258 + .../super-blur-view/super-blur-view-impl.h | 164 + .../internal/controls/table-view/array-2d.h | 274 ++ .../controls/table-view/table-view-impl.cpp | 1092 +++++ .../internal/controls/table-view/table-view-impl.h | 348 ++ .../controls/text-input/text-input-impl.cpp | 5072 ++++++++++++++++++++ .../internal/controls/text-input/text-input-impl.h | 1465 ++++++ .../controls/text-input/text-input-popup-impl.cpp | 561 +++ .../controls/text-input/text-input-popup-impl.h | 234 + .../controls/text-view/relayout-utilities.cpp | 1958 ++++++++ .../controls/text-view/relayout-utilities.h | 501 ++ .../controls/text-view/split-by-char-policies.cpp | 296 ++ .../controls/text-view/split-by-char-policies.h | 59 + .../text-view/split-by-new-line-char-policies.cpp | 352 ++ .../text-view/split-by-new-line-char-policies.h | 60 + .../controls/text-view/split-by-word-policies.cpp | 695 +++ .../controls/text-view/split-by-word-policies.h | 59 + .../controls/text-view/text-actor-cache.cpp | 71 + .../internal/controls/text-view/text-actor-cache.h | 82 + .../internal/controls/text-view/text-processor.cpp | 358 ++ .../internal/controls/text-view/text-processor.h | 118 + .../text-view/text-view-character-processor.cpp | 113 + .../internal/controls/text-view/text-view-impl.cpp | 2087 ++++++++ .../internal/controls/text-view/text-view-impl.h | 735 +++ .../text-view/text-view-line-processor.cpp | 427 ++ .../controls/text-view/text-view-line-processor.h | 144 + .../controls/text-view/text-view-processor-dbg.cpp | 172 + .../controls/text-view/text-view-processor-dbg.h | 53 + .../text-view-processor-helper-functions.cpp | 196 + .../text-view-processor-helper-functions.h | 113 + .../controls/text-view/text-view-processor-types.h | 273 ++ .../controls/text-view/text-view-processor.cpp | 1208 +++++ .../controls/text-view/text-view-processor.h | 133 + .../text-view/text-view-word-group-processor.cpp | 550 +++ .../text-view/text-view-word-group-processor.h | 138 + .../text-view/text-view-word-processor.cpp | 415 ++ .../controls/text-view/text-view-word-processor.h | 162 + .../internal/controls/tool-bar/tool-bar-impl.cpp | 357 ++ .../internal/controls/tool-bar/tool-bar-impl.h | 168 + dali-toolkit/internal/controls/view/view-impl.cpp | 342 ++ dali-toolkit/internal/controls/view/view-impl.h | 205 + .../factory/localized-control-factory-impl.cpp | 120 + .../factory/localized-control-factory-impl.h | 138 + dali-toolkit/internal/file.list | 97 + .../internal/filters/blur-two-pass-filter.cpp | 327 ++ .../internal/filters/blur-two-pass-filter.h | 121 + dali-toolkit/internal/filters/emboss-filter.cpp | 293 ++ dali-toolkit/internal/filters/emboss-filter.h | 98 + dali-toolkit/internal/filters/image-filter.cpp | 127 + dali-toolkit/internal/filters/image-filter.h | 164 + dali-toolkit/internal/filters/spread-filter.cpp | 223 + dali-toolkit/internal/filters/spread-filter.h | 105 + .../internal/focus-manager/focus-manager-impl.cpp | 917 ++++ .../internal/focus-manager/focus-manager-impl.h | 406 ++ .../focus-manager/keyboard-focus-manager-impl.cpp | 720 +++ .../focus-manager/keyboard-focus-manager-impl.h | 285 ++ .../focus-manager/keyinput-focus-manager-impl.cpp | 234 + .../focus-manager/keyinput-focus-manager-impl.h | 165 + .../shader-effects/page-turn-effect-impl.cpp | 471 ++ .../shader-effects/page-turn-effect-impl.h | 143 + .../internal/shader-effects/water-effect-impl.cpp | 198 + .../internal/shader-effects/water-effect-impl.h | 146 + .../cube-transition-cross-effect-impl.cpp | 139 + .../cube-transition-cross-effect-impl.h | 118 + .../cube-transition-effect-impl.cpp | 346 ++ .../cube-transition-effect-impl.h | 343 ++ .../cube-transition-fold-effect-impl.cpp | 138 + .../cube-transition-fold-effect-impl.h | 116 + .../cube-transition-wave-effect-impl.cpp | 201 + .../cube-transition-wave-effect-impl.h | 132 + dali-toolkit/public-api/builder/builder.cpp | 129 + dali-toolkit/public-api/builder/builder.h | 270 ++ dali-toolkit/public-api/builder/json-parser.cpp | 114 + dali-toolkit/public-api/builder/json-parser.h | 146 + dali-toolkit/public-api/builder/tree-node.cpp | 197 + dali-toolkit/public-api/builder/tree-node.h | 239 + .../public-api/controls/alignment/alignment.cpp | 109 + .../public-api/controls/bloom-view/bloom-view.cpp | 131 + .../public-api/controls/bloom-view/bloom-view.h | 253 + .../controls/bubble-effect/bubble-emitter.cpp | 122 + .../public-api/controls/buttons/button.cpp | 98 + .../controls/buttons/check-box-button.cpp | 154 + .../public-api/controls/buttons/check-box-button.h | 206 + .../public-api/controls/buttons/push-button.cpp | 238 + .../public-api/controls/cluster/cluster-style.cpp | 97 + .../public-api/controls/cluster/cluster.cpp | 176 + dali-toolkit/public-api/controls/cluster/cluster.h | 256 + dali-toolkit/public-api/controls/control-impl.cpp | 840 ++++ dali-toolkit/public-api/controls/control.cpp | 174 + .../default-controls/check-button-factory.cpp | 144 + .../default-controls/check-button-factory.h | 79 + .../default-controls/push-button-factory.cpp | 145 + .../default-controls/solid-color-actor.cpp | 99 + .../controls/effects-view/effects-view.cpp | 153 + .../controls/effects-view/effects-view.h | 227 + .../gaussian-blur-view/gaussian-blur-view.cpp | 138 + .../gaussian-blur-view/gaussian-blur-view.h | 274 ++ .../public-api/controls/image-view/image-view.cpp | 121 + .../public-api/controls/image-view/image-view.h | 196 + .../controls/image-view/masked-image-view.cpp | 167 + .../public-api/controls/magnifier/magnifier.cpp | 110 + .../public-api/controls/magnifier/magnifier.h | 151 + .../navigation-frame/navigation-bar-style.h | 150 + .../navigation-frame/navigation-control.cpp | 140 + .../controls/navigation-frame/navigation-control.h | 246 + .../public-api/controls/navigation-frame/page.cpp | 135 + .../public-api/controls/navigation-frame/page.h | 209 + .../controls/page-turn-view/page-factory.cpp | 85 + .../controls/page-turn-view/page-factory.h | 122 + .../page-turn-view/page-turn-landscape-view.cpp | 75 + .../page-turn-view/page-turn-landscape-view.h | 100 + .../page-turn-view/page-turn-portrait-view.cpp | 75 + .../page-turn-view/page-turn-portrait-view.h | 98 + .../controls/page-turn-view/page-turn-view.cpp | 133 + .../controls/page-turn-view/page-turn-view.h | 218 + dali-toolkit/public-api/controls/popup/popup.cpp | 153 + .../controls/scroll-component/scroll-bar.cpp | 81 + .../controls/scroll-component/scroll-component.cpp | 66 + .../controls/scrollable/item-view/album-layout.cpp | 1393 ++++++ .../controls/scrollable/item-view/album-layout.h | 369 ++ .../controls/scrollable/item-view/depth-layout.cpp | 761 +++ .../controls/scrollable/item-view/depth-layout.h | 288 ++ .../controls/scrollable/item-view/grid-layout.cpp | 762 +++ .../controls/scrollable/item-view/item-factory.cpp | 31 + .../controls/scrollable/item-view/item-layout.cpp | 241 + .../controls/scrollable/item-view/item-view.cpp | 192 + .../scrollable/item-view/navigation-layout.cpp | 579 +++ .../scrollable/item-view/navigation-layout.h | 246 + .../controls/scrollable/item-view/roll-layout.cpp | 588 +++ .../controls/scrollable/item-view/roll-layout.h | 198 + .../scrollable/item-view/spiral-layout.cpp | 599 +++ .../controls/scrollable/item-view/spiral-layout.h | 242 + .../scroll-view/scroll-view-carousel-effect.cpp | 59 + .../scroll-view/scroll-view-carousel-effect.h | 114 + .../scroll-view/scroll-view-constraints.cpp | 92 + .../scroll-view/scroll-view-constraints.h | 83 + .../scroll-view/scroll-view-cube-effect.cpp | 69 + .../scroll-view/scroll-view-custom-effect.cpp | 268 ++ .../scroll-view/scroll-view-depth-effect.cpp | 61 + .../scroll-view/scroll-view-depth-effect.h | 119 + .../scrollable/scroll-view/scroll-view-effect.cpp | 40 + .../scroll-view-page-carousel-effect.cpp | 58 + .../scroll-view/scroll-view-page-carousel-effect.h | 103 + .../scroll-view/scroll-view-page-cube-effect.cpp | 58 + .../scroll-view/scroll-view-page-cube-effect.h | 103 + .../scroll-view/scroll-view-page-spiral-effect.cpp | 58 + .../scroll-view/scroll-view-slide-effect.cpp | 94 + .../scroll-view/scroll-view-twist-effect.cpp | 99 + .../scroll-view/scroll-view-wobble-effect.cpp | 50 + .../scroll-view/scroll-view-wobble-effect.h | 77 + .../scrollable/scroll-view/scroll-view.cpp | 638 +++ .../public-api/controls/scrollable/scrollable.cpp | 113 + .../controls/selectors/rotating-selector.cpp | 120 + .../controls/selectors/rotating-selector.h | 160 + .../controls/shadow-view/shadow-view.cpp | 138 + .../public-api/controls/shadow-view/shadow-view.h | 258 + dali-toolkit/public-api/controls/slider/slider.cpp | 125 + dali-toolkit/public-api/controls/slider/slider.h | 152 + .../controls/super-blur-view/super-blur-view.cpp | 103 + .../public-api/controls/table-view/table-view.cpp | 206 + .../public-api/controls/table-view/table-view.h | 322 ++ .../public-api/controls/text-input/text-input.cpp | 384 ++ .../public-api/controls/text-view/text-view.cpp | 388 ++ .../public-api/controls/tool-bar/tool-bar.cpp | 96 + .../public-api/controls/tool-bar/tool-bar.h | 131 + dali-toolkit/public-api/controls/view/view.cpp | 125 + dali-toolkit/public-api/controls/view/view.h | 216 + dali-toolkit/public-api/enums.cpp | 39 + .../factory/localized-control-factory.cpp | 77 + dali-toolkit/public-api/file.list | 233 + .../public-api/focus-manager/focus-manager.cpp | 207 + .../focus-manager/keyboard-focus-manager.cpp | 135 + .../focus-manager/keyinput-focus-manager.cpp | 105 + .../focus-manager/keyinput-focus-manager.h | 143 + .../markup-processor/markup-processor.cpp | 1107 +++++ .../shader-effects/alpha-discard-effect.cpp | 60 + .../shader-effects/alpha-discard-effect.h | 68 + .../public-api/shader-effects/bendy-effect.cpp | 146 + .../public-api/shader-effects/bendy-effect.h | 100 + .../public-api/shader-effects/blind-effect.cpp | 96 + .../public-api/shader-effects/blind-effect.h | 75 + .../shader-effects/bubble-effect/bubble-effect.cpp | 251 + .../shader-effects/bubble-effect/bubble-effect.h | 139 + .../bubble-effect/color-adjuster.cpp | 97 + .../shader-effects/bubble-effect/color-adjuster.h | 68 + .../public-api/shader-effects/carousel-effect.cpp | 117 + .../public-api/shader-effects/carousel-effect.h | 118 + .../shader-effects/displacement-effect.cpp | 240 + .../shader-effects/displacement-effect.h | 221 + .../public-api/shader-effects/dissolve-effect.cpp | 198 + .../shader-effects/dissolve-local-effect.cpp | 167 + .../shader-effects/dissolve-local-effect.h | 129 + .../shader-effects/distance-field-effect.cpp | 312 ++ .../shader-effects/distance-field-effect.h | 196 + .../shader-effects/image-region-effect.cpp | 100 + .../public-api/shader-effects/iris-effect.cpp | 120 + .../public-api/shader-effects/mask-effect.cpp | 61 + .../public-api/shader-effects/mirror-effect.cpp | 122 + .../public-api/shader-effects/mirror-effect.h | 88 + .../shader-effects/motion-blur-effect.cpp | 328 ++ .../public-api/shader-effects/motion-blur-effect.h | 213 + .../shader-effects/motion-stretch-effect.cpp | 299 ++ .../shader-effects/motion-stretch-effect.h | 165 + .../shader-effects/nine-patch-mask-effect.cpp | 117 + .../public-api/shader-effects/overlay-effect.cpp | 70 + .../public-api/shader-effects/overlay-effect.h | 68 + .../shader-effects/page-turn-book-spine-effect.cpp | 130 + .../public-api/shader-effects/page-turn-effect.cpp | 96 + .../public-api/shader-effects/ripple-effect.cpp | 141 + .../public-api/shader-effects/ripple2d-effect.cpp | 106 + .../public-api/shader-effects/shear-effect.cpp | 119 + .../public-api/shader-effects/shear-effect.h | 98 + .../shader-effects/soft-button-effect.cpp | 423 ++ .../public-api/shader-effects/soft-button-effect.h | 175 + .../public-api/shader-effects/spot-effect.cpp | 116 + .../public-api/shader-effects/spot-effect.h | 86 + .../shader-effects/square-dissolve-effect.cpp | 132 + .../shader-effects/square-dissolve-effect.h | 113 + .../public-api/shader-effects/swirl-effect.cpp | 136 + .../public-api/shader-effects/water-effect.cpp | 101 + .../public-api/shader-effects/water-effect.h | 165 + .../cube-transition-cross-effect.cpp | 41 + .../cube-transition-cross-effect.h | 70 + .../transition-effects/cube-transition-effect.cpp | 116 + .../transition-effects/cube-transition-effect.h | 204 + .../cube-transition-fold-effect.cpp | 41 + .../cube-transition-fold-effect.h | 70 + .../cube-transition-wave-effect.cpp | 41 + .../cube-transition-wave-effect.h | 69 + packaging/dali-toolkit.spec | 110 + 658 files changed, 146680 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 automated-tests/.gitignore create mode 100644 automated-tests/_export_desktop.sh create mode 100755 automated-tests/_export_env.sh create mode 100644 automated-tests/_export_sbs.sh create mode 100755 automated-tests/_export_target_env.sh create mode 100755 automated-tests/build.sh create mode 100755 automated-tests/build_out.sh create mode 100644 automated-tests/coverage.mk create mode 100755 automated-tests/coverage.sh create mode 100644 automated-tests/dali-internal-test-suite/master-makefile.mk create mode 120000 automated-tests/dali-internal-test-suite/tc-gen.sh create mode 100644 automated-tests/dali-internal-test-suite/text-input/.gitignore create mode 120000 automated-tests/dali-internal-test-suite/text-input/Makefile create mode 100644 automated-tests/dali-internal-test-suite/text-input/file.list create mode 100644 automated-tests/dali-internal-test-suite/text-input/tslist create mode 100644 automated-tests/dali-internal-test-suite/text-input/utc-Dali-TextInput.cpp create mode 100644 automated-tests/dali-internal-test-suite/text-view/.gitignore create mode 120000 automated-tests/dali-internal-test-suite/text-view/Makefile create mode 100644 automated-tests/dali-internal-test-suite/text-view/file.list create mode 100644 automated-tests/dali-internal-test-suite/text-view/tslist create mode 100644 automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-HelperAndDebug.cpp create mode 100644 automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor-Types.cpp create mode 100644 automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor.cpp create mode 100644 automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Relayout-Utilities.cpp create mode 100644 automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView.cpp create mode 100644 automated-tests/dali-internal-test-suite/utc-MODULE-CLASS.cpp.in create mode 100644 automated-tests/dali-test-suite/.gitignore create mode 100644 automated-tests/dali-test-suite/alignment/.gitignore create mode 120000 automated-tests/dali-test-suite/alignment/Makefile create mode 100644 automated-tests/dali-test-suite/alignment/file.list create mode 100644 automated-tests/dali-test-suite/alignment/tslist create mode 100644 automated-tests/dali-test-suite/alignment/utc-Dali-Alignment.cpp create mode 100644 automated-tests/dali-test-suite/bubble-emitter/.gitignore create mode 120000 automated-tests/dali-test-suite/bubble-emitter/Makefile create mode 100644 automated-tests/dali-test-suite/bubble-emitter/file.list create mode 100644 automated-tests/dali-test-suite/bubble-emitter/tslist create mode 100644 automated-tests/dali-test-suite/bubble-emitter/utc-Dali-BubbleEmitter.cpp create mode 100644 automated-tests/dali-test-suite/builder/.gitignore create mode 120000 automated-tests/dali-test-suite/builder/Makefile create mode 100644 automated-tests/dali-test-suite/builder/file.list create mode 100644 automated-tests/dali-test-suite/builder/tslist create mode 100644 automated-tests/dali-test-suite/builder/utc-Dali-Builder.cpp create mode 100644 automated-tests/dali-test-suite/builder/utc-Dali-JsonParser.cpp create mode 100644 automated-tests/dali-test-suite/buttons/.gitignore create mode 120000 automated-tests/dali-test-suite/buttons/Makefile create mode 100644 automated-tests/dali-test-suite/buttons/file.list create mode 100644 automated-tests/dali-test-suite/buttons/tslist create mode 100644 automated-tests/dali-test-suite/buttons/utc-Dali-Button.cpp create mode 100644 automated-tests/dali-test-suite/buttons/utc-Dali-CheckBoxButton.cpp create mode 100644 automated-tests/dali-test-suite/buttons/utc-Dali-PushButton.cpp create mode 100644 automated-tests/dali-test-suite/cluster/.gitignore create mode 120000 automated-tests/dali-test-suite/cluster/Makefile create mode 100644 automated-tests/dali-test-suite/cluster/file.list create mode 100644 automated-tests/dali-test-suite/cluster/tslist create mode 100644 automated-tests/dali-test-suite/cluster/utc-Dali-Cluster.cpp create mode 100644 automated-tests/dali-test-suite/control/.gitignore create mode 120000 automated-tests/dali-test-suite/control/Makefile create mode 100644 automated-tests/dali-test-suite/control/dummy-control.h create mode 100644 automated-tests/dali-test-suite/control/file.list create mode 100644 automated-tests/dali-test-suite/control/tslist create mode 100644 automated-tests/dali-test-suite/control/utc-Dali-Control.cpp create mode 100644 automated-tests/dali-test-suite/control/utc-Dali-ControlImpl.cpp create mode 100644 automated-tests/dali-test-suite/default-controls/.gitignore create mode 120000 automated-tests/dali-test-suite/default-controls/Makefile create mode 100644 automated-tests/dali-test-suite/default-controls/file.list create mode 100644 automated-tests/dali-test-suite/default-controls/tslist create mode 100644 automated-tests/dali-test-suite/default-controls/utc-Dali-DefaultControls.cpp create mode 100644 automated-tests/dali-test-suite/focus-manager/.gitignore create mode 120000 automated-tests/dali-test-suite/focus-manager/Makefile create mode 100644 automated-tests/dali-test-suite/focus-manager/file.list create mode 100644 automated-tests/dali-test-suite/focus-manager/tslist create mode 100644 automated-tests/dali-test-suite/focus-manager/utc-Dali-FocusManager.cpp create mode 100644 automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyInputFocusManager.cpp create mode 100644 automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyboardFocusManager.cpp create mode 100644 automated-tests/dali-test-suite/item-view/.gitignore create mode 120000 automated-tests/dali-test-suite/item-view/Makefile create mode 100644 automated-tests/dali-test-suite/item-view/file.list create mode 100644 automated-tests/dali-test-suite/item-view/tslist create mode 100755 automated-tests/dali-test-suite/item-view/utc-Dali-AlbumLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-DepthLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-GridLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-ItemLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-ItemView.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-NavigationLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-RollLayout.cpp create mode 100644 automated-tests/dali-test-suite/item-view/utc-Dali-SpiralLayout.cpp create mode 100644 automated-tests/dali-test-suite/master-makefile.mk create mode 100644 automated-tests/dali-test-suite/navigation-frame/.gitignore create mode 120000 automated-tests/dali-test-suite/navigation-frame/Makefile create mode 100644 automated-tests/dali-test-suite/navigation-frame/file.list create mode 100644 automated-tests/dali-test-suite/navigation-frame/tslist create mode 100644 automated-tests/dali-test-suite/navigation-frame/utc-Dali-NavigationControl.cpp create mode 100644 automated-tests/dali-test-suite/navigation-frame/utc-Dali-Page.cpp create mode 100644 automated-tests/dali-test-suite/page-turn-view/.gitignore create mode 120000 automated-tests/dali-test-suite/page-turn-view/Makefile create mode 100644 automated-tests/dali-test-suite/page-turn-view/file.list create mode 100644 automated-tests/dali-test-suite/page-turn-view/tslist create mode 100644 automated-tests/dali-test-suite/page-turn-view/utc-Dali-PageTurnView.cpp create mode 100644 automated-tests/dali-test-suite/popup/.gitignore create mode 120000 automated-tests/dali-test-suite/popup/Makefile create mode 100644 automated-tests/dali-test-suite/popup/file.list create mode 100644 automated-tests/dali-test-suite/popup/tslist create mode 100644 automated-tests/dali-test-suite/popup/utc-Dali-Popup.cpp create mode 100644 automated-tests/dali-test-suite/scroll-view/.gitignore create mode 120000 automated-tests/dali-test-suite/scroll-view/Makefile create mode 100644 automated-tests/dali-test-suite/scroll-view/file.list create mode 100644 automated-tests/dali-test-suite/scroll-view/tslist create mode 100644 automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollView.cpp create mode 100644 automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollViewEffect.cpp create mode 100644 automated-tests/dali-test-suite/selectors/.gitignore create mode 120000 automated-tests/dali-test-suite/selectors/Makefile create mode 100644 automated-tests/dali-test-suite/selectors/file.list create mode 100644 automated-tests/dali-test-suite/selectors/tslist create mode 100644 automated-tests/dali-test-suite/selectors/utc-Dali-RotatingSelector.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/.gitignore create mode 120000 automated-tests/dali-test-suite/shader-effects/Makefile create mode 100644 automated-tests/dali-test-suite/shader-effects/file.list create mode 100644 automated-tests/dali-test-suite/shader-effects/tslist create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-BendyEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-BlindEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-BloomView.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-CarouselEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-DisplacementEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-DissolveEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-DistanceFieldEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-GaussianBlurView.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-IrisEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-MaskEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-NinePatchMaskEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-OverlayEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-PageTurnEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-Ripple2DEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-RippleEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-ShadowView.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-ShearEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-SoftButtonEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-SpotEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-SquareDissolveEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-SwirlEffect.cpp create mode 100644 automated-tests/dali-test-suite/shader-effects/utc-Dali-WaterEffect.cpp create mode 100644 automated-tests/dali-test-suite/slider/.gitignore create mode 120000 automated-tests/dali-test-suite/slider/Makefile create mode 100644 automated-tests/dali-test-suite/slider/file.list create mode 100644 automated-tests/dali-test-suite/slider/tslist create mode 100644 automated-tests/dali-test-suite/slider/utc-Dali-Slider.cpp create mode 100644 automated-tests/dali-test-suite/super-blur-view/.gitignore create mode 120000 automated-tests/dali-test-suite/super-blur-view/Makefile create mode 100644 automated-tests/dali-test-suite/super-blur-view/file.list create mode 100644 automated-tests/dali-test-suite/super-blur-view/tslist create mode 100644 automated-tests/dali-test-suite/super-blur-view/utc-Dali-SuperBlurView.cpp create mode 100644 automated-tests/dali-test-suite/table-view/.gitignore create mode 120000 automated-tests/dali-test-suite/table-view/Makefile create mode 100644 automated-tests/dali-test-suite/table-view/file.list create mode 100644 automated-tests/dali-test-suite/table-view/tslist create mode 100644 automated-tests/dali-test-suite/table-view/utc-Dali-TableView.cpp create mode 100755 automated-tests/dali-test-suite/tc-gen.sh create mode 100644 automated-tests/dali-test-suite/text-input/.gitignore create mode 120000 automated-tests/dali-test-suite/text-input/Makefile create mode 100644 automated-tests/dali-test-suite/text-input/file.list create mode 100644 automated-tests/dali-test-suite/text-input/tslist create mode 100644 automated-tests/dali-test-suite/text-input/utc-Dali-TextInput.cpp create mode 100644 automated-tests/dali-test-suite/text-view/.gitignore create mode 120000 automated-tests/dali-test-suite/text-view/Makefile create mode 100644 automated-tests/dali-test-suite/text-view/file.list create mode 100644 automated-tests/dali-test-suite/text-view/tslist create mode 100644 automated-tests/dali-test-suite/text-view/utc-Dali-MarkupProcessor.cpp create mode 100644 automated-tests/dali-test-suite/text-view/utc-Dali-TextView.cpp create mode 100644 automated-tests/dali-test-suite/toolbar/.gitignore create mode 120000 automated-tests/dali-test-suite/toolbar/Makefile create mode 100644 automated-tests/dali-test-suite/toolbar/file.list create mode 100644 automated-tests/dali-test-suite/toolbar/tslist create mode 100644 automated-tests/dali-test-suite/toolbar/utc-Dali-ToolBar.cpp create mode 100644 automated-tests/dali-test-suite/transition-effects/.gitignore create mode 120000 automated-tests/dali-test-suite/transition-effects/Makefile create mode 100644 automated-tests/dali-test-suite/transition-effects/file.list create mode 100644 automated-tests/dali-test-suite/transition-effects/tslist create mode 100644 automated-tests/dali-test-suite/transition-effects/utc-Dali-CubeTransitionEffect.cpp create mode 100644 automated-tests/dali-test-suite/ui-builder/.gitignore create mode 100644 automated-tests/dali-test-suite/ui-builder/file.list create mode 100644 automated-tests/dali-test-suite/utc-MODULE-CLASS.cpp.in create mode 100644 automated-tests/dali-test-suite/view/.gitignore create mode 120000 automated-tests/dali-test-suite/view/Makefile create mode 100644 automated-tests/dali-test-suite/view/file.list create mode 100644 automated-tests/dali-test-suite/view/tslist create mode 100644 automated-tests/dali-test-suite/view/utc-Dali-View.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/dali-toolkit-test-suite-utils.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-adaptor.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-adaptor.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-application.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-application.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-orientation.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-orientation.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-test-application.h create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-timer.cpp create mode 100644 automated-tests/dali-toolkit-test-utils/toolkit-timer.h create mode 100755 automated-tests/debug.sh create mode 100644 automated-tests/debug_target.sh create mode 100755 automated-tests/execute.sh create mode 100755 automated-tests/execute_target.sh create mode 100644 automated-tests/rules.mk.in create mode 100755 automated-tests/tbp.pl create mode 100644 automated-tests/tet_scen create mode 100644 automated-tests/tetbuild.cfg create mode 100644 automated-tests/tetclean.cfg create mode 100644 automated-tests/tetexec.cfg create mode 100644 build/slp/.gitignore create mode 100644 build/slp/Makefile.am create mode 100644 build/slp/README create mode 100644 build/slp/configure.ac create mode 100644 build/slp/dali-toolkit.pc.in create mode 100644 build/slp/dali-toolkit/Makefile.am create mode 100644 capi/dali-toolkit/public-api/controls/alignment/alignment.h create mode 100644 capi/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.h create mode 100644 capi/dali-toolkit/public-api/controls/buttons/button.h create mode 100644 capi/dali-toolkit/public-api/controls/buttons/push-button.h create mode 100644 capi/dali-toolkit/public-api/controls/cluster/cluster-style.h create mode 100644 capi/dali-toolkit/public-api/controls/control-impl.h create mode 100644 capi/dali-toolkit/public-api/controls/control.h create mode 100644 capi/dali-toolkit/public-api/controls/default-controls/push-button-factory.h create mode 100644 capi/dali-toolkit/public-api/controls/default-controls/solid-color-actor.h create mode 100644 capi/dali-toolkit/public-api/controls/image-view/masked-image-view.h create mode 100644 capi/dali-toolkit/public-api/controls/popup/popup.h create mode 100755 capi/dali-toolkit/public-api/controls/scroll-component/scroll-bar.h create mode 100644 capi/dali-toolkit/public-api/controls/scroll-component/scroll-component.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h create mode 100644 capi/dali-toolkit/public-api/controls/scrollable/scrollable.h create mode 100644 capi/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.h create mode 100644 capi/dali-toolkit/public-api/controls/text-input/text-input.h create mode 100644 capi/dali-toolkit/public-api/controls/text-view/text-view.h create mode 100644 capi/dali-toolkit/public-api/dali-toolkit-capi-internal.h create mode 100644 capi/dali-toolkit/public-api/enums.h create mode 100644 capi/dali-toolkit/public-api/factory/localized-control-factory.h create mode 100644 capi/dali-toolkit/public-api/file.list create mode 100644 capi/dali-toolkit/public-api/focus-manager/focus-manager.h create mode 100644 capi/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h create mode 100644 capi/dali-toolkit/public-api/markup-processor/markup-processor.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/dissolve-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/image-region-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/iris-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/mask-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/page-turn-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/ripple-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/ripple2d-effect.h create mode 100644 capi/dali-toolkit/public-api/shader-effects/swirl-effect.h create mode 100644 dali-toolkit.manifest create mode 100644 dali-toolkit/dali-toolkit.h create mode 100755 dali-toolkit/images/00_popup_bg.png create mode 100755 dali-toolkit/images/00_popup_bottom_bg.png create mode 100755 dali-toolkit/images/00_popup_bubble_bg.png create mode 100755 dali-toolkit/images/00_popup_bubble_tail_bottom.png create mode 100755 dali-toolkit/images/00_popup_button_bg.png create mode 100755 dali-toolkit/images/00_popup_button_pressed.png create mode 100644 dali-toolkit/images/B16-8_TTS_focus.png create mode 100644 dali-toolkit/images/copy_paste_icon_clipboard.png create mode 100644 dali-toolkit/images/copy_paste_icon_copy.png create mode 100644 dali-toolkit/images/copy_paste_icon_cut.png create mode 100644 dali-toolkit/images/copy_paste_icon_paste.png create mode 100644 dali-toolkit/images/copy_paste_icon_select.png create mode 100644 dali-toolkit/images/copy_paste_icon_select_all.png create mode 100755 dali-toolkit/images/copypanelLine.png create mode 100644 dali-toolkit/images/cursor.png create mode 100644 dali-toolkit/images/cutCopyPastePopup_bg.png create mode 100644 dali-toolkit/images/file.list create mode 100755 dali-toolkit/images/insertpoint-icon.png create mode 100644 dali-toolkit/images/keyboard_focus.png create mode 100644 dali-toolkit/images/magnifier-image-frame.png create mode 100644 dali-toolkit/images/magnifier.png create mode 100644 dali-toolkit/images/overshoot_ripple.png create mode 100755 dali-toolkit/images/popup_bg.png create mode 100755 dali-toolkit/images/popup_scroll.png create mode 100755 dali-toolkit/images/popup_tail_down.png create mode 100644 dali-toolkit/images/popup_tail_left.png create mode 100644 dali-toolkit/images/popup_tail_right.png create mode 100644 dali-toolkit/images/popup_tail_up.png create mode 100755 dali-toolkit/images/scroll_overshoot.png create mode 100644 dali-toolkit/images/slider-popup-arrow.png create mode 100644 dali-toolkit/images/slider-popup.png create mode 100644 dali-toolkit/images/slider-skin-handle.png create mode 100644 dali-toolkit/images/slider-skin-progress.png create mode 100644 dali-toolkit/images/slider-skin.png create mode 100755 dali-toolkit/images/text-input-selection-handle-left-press.png create mode 100755 dali-toolkit/images/text-input-selection-handle-left.png create mode 100755 dali-toolkit/images/text-input-selection-handle-right-press.png create mode 100755 dali-toolkit/images/text-input-selection-handle-right.png create mode 100644 dali-toolkit/internal/builder/builder-actor.cpp create mode 100644 dali-toolkit/internal/builder/builder-animations.cpp create mode 100644 dali-toolkit/internal/builder/builder-control.cpp create mode 100644 dali-toolkit/internal/builder/builder-declarations.h create mode 100644 dali-toolkit/internal/builder/builder-filesystem.h create mode 100644 dali-toolkit/internal/builder/builder-get-is.inl.h create mode 100755 dali-toolkit/internal/builder/builder-impl.cpp create mode 100644 dali-toolkit/internal/builder/builder-impl.h create mode 100644 dali-toolkit/internal/builder/builder-set-property.cpp create mode 100644 dali-toolkit/internal/builder/builder-signals.cpp create mode 100644 dali-toolkit/internal/builder/json-parser-impl.cpp create mode 100644 dali-toolkit/internal/builder/json-parser-impl.h create mode 100644 dali-toolkit/internal/builder/json-parser-state.cpp create mode 100644 dali-toolkit/internal/builder/json-parser-state.h create mode 100644 dali-toolkit/internal/builder/optional-value.h create mode 100644 dali-toolkit/internal/builder/tree-node-manipulator.cpp create mode 100644 dali-toolkit/internal/builder/tree-node-manipulator.h create mode 100644 dali-toolkit/internal/controls/alignment/alignment-impl.cpp create mode 100644 dali-toolkit/internal/controls/alignment/alignment-impl.h create mode 100644 dali-toolkit/internal/controls/bloom-view/bloom-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/bloom-view/bloom-view-impl.h create mode 100644 dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.cpp create mode 100644 dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/button-impl.cpp create mode 100644 dali-toolkit/internal/controls/buttons/button-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/button-painter-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.cpp create mode 100644 dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/check-box-button-impl.cpp create mode 100644 dali-toolkit/internal/controls/buttons/check-box-button-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/check-box-button-painter-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.cpp create mode 100644 dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/push-button-impl.cpp create mode 100644 dali-toolkit/internal/controls/buttons/push-button-impl.h create mode 100644 dali-toolkit/internal/controls/buttons/push-button-painter-impl.h create mode 100644 dali-toolkit/internal/controls/cluster/cluster-impl.cpp create mode 100644 dali-toolkit/internal/controls/cluster/cluster-impl.h create mode 100644 dali-toolkit/internal/controls/cluster/cluster-style-impl.cpp create mode 100644 dali-toolkit/internal/controls/cluster/cluster-style-impl.h create mode 100644 dali-toolkit/internal/controls/effects-view/effects-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/effects-view/effects-view-impl.h create mode 100644 dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.h create mode 100644 dali-toolkit/internal/controls/image-view/image-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/image-view/image-view-impl.h create mode 100644 dali-toolkit/internal/controls/image-view/masked-image-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/image-view/masked-image-view-impl.h create mode 100644 dali-toolkit/internal/controls/magnifier/magnifier-impl.cpp create mode 100644 dali-toolkit/internal/controls/magnifier/magnifier-impl.h create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-bar.cpp create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-bar.h create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.cpp create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.h create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.cpp create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.h create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.cpp create mode 100644 dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.h create mode 100644 dali-toolkit/internal/controls/navigation-frame/page-impl.cpp create mode 100644 dali-toolkit/internal/controls/navigation-frame/page-impl.h create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.h create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.h create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h create mode 100755 dali-toolkit/internal/controls/popup/popup-impl.cpp create mode 100755 dali-toolkit/internal/controls/popup/popup-impl.h create mode 100644 dali-toolkit/internal/controls/popup/popup-style-impl.cpp create mode 100644 dali-toolkit/internal/controls/popup/popup-style-impl.h create mode 100644 dali-toolkit/internal/controls/relayout-controller-impl.cpp create mode 100644 dali-toolkit/internal/controls/relayout-controller-impl.h create mode 100644 dali-toolkit/internal/controls/relayout-controller.cpp create mode 100644 dali-toolkit/internal/controls/relayout-controller.h create mode 100644 dali-toolkit/internal/controls/relayout-helper.cpp create mode 100644 dali-toolkit/internal/controls/relayout-helper.h create mode 100755 dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.cpp create mode 100755 dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.h create mode 100644 dali-toolkit/internal/controls/scroll-component/scroll-component-impl.cpp create mode 100644 dali-toolkit/internal/controls/scroll-component/scroll-component-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h create mode 100755 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.h create mode 100644 dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp create mode 100644 dali-toolkit/internal/controls/scrollable/scrollable-impl.h create mode 100644 dali-toolkit/internal/controls/selectors/rotating-selector-impl.cpp create mode 100644 dali-toolkit/internal/controls/selectors/rotating-selector-impl.h create mode 100644 dali-toolkit/internal/controls/shadow-view/shadow-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h create mode 100755 dali-toolkit/internal/controls/slider/slider-impl.cpp create mode 100755 dali-toolkit/internal/controls/slider/slider-impl.h create mode 100644 dali-toolkit/internal/controls/style-change-processor.cpp create mode 100644 dali-toolkit/internal/controls/style-change-processor.h create mode 100644 dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.h create mode 100644 dali-toolkit/internal/controls/table-view/array-2d.h create mode 100644 dali-toolkit/internal/controls/table-view/table-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/table-view/table-view-impl.h create mode 100644 dali-toolkit/internal/controls/text-input/text-input-impl.cpp create mode 100644 dali-toolkit/internal/controls/text-input/text-input-impl.h create mode 100644 dali-toolkit/internal/controls/text-input/text-input-popup-impl.cpp create mode 100644 dali-toolkit/internal/controls/text-input/text-input-popup-impl.h create mode 100644 dali-toolkit/internal/controls/text-view/relayout-utilities.cpp create mode 100644 dali-toolkit/internal/controls/text-view/relayout-utilities.h create mode 100644 dali-toolkit/internal/controls/text-view/split-by-char-policies.cpp create mode 100644 dali-toolkit/internal/controls/text-view/split-by-char-policies.h create mode 100644 dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.cpp create mode 100644 dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.h create mode 100644 dali-toolkit/internal/controls/text-view/split-by-word-policies.cpp create mode 100644 dali-toolkit/internal/controls/text-view/split-by-word-policies.h create mode 100644 dali-toolkit/internal/controls/text-view/text-actor-cache.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-actor-cache.h create mode 100644 dali-toolkit/internal/controls/text-view/text-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-processor.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-character-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-impl.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-impl.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-line-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-line-processor.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor-dbg.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor-types.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-processor.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-word-group-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h create mode 100644 dali-toolkit/internal/controls/text-view/text-view-word-processor.cpp create mode 100644 dali-toolkit/internal/controls/text-view/text-view-word-processor.h create mode 100644 dali-toolkit/internal/controls/tool-bar/tool-bar-impl.cpp create mode 100644 dali-toolkit/internal/controls/tool-bar/tool-bar-impl.h create mode 100644 dali-toolkit/internal/controls/view/view-impl.cpp create mode 100644 dali-toolkit/internal/controls/view/view-impl.h create mode 100644 dali-toolkit/internal/factory/localized-control-factory-impl.cpp create mode 100644 dali-toolkit/internal/factory/localized-control-factory-impl.h create mode 100644 dali-toolkit/internal/file.list create mode 100644 dali-toolkit/internal/filters/blur-two-pass-filter.cpp create mode 100644 dali-toolkit/internal/filters/blur-two-pass-filter.h create mode 100644 dali-toolkit/internal/filters/emboss-filter.cpp create mode 100644 dali-toolkit/internal/filters/emboss-filter.h create mode 100644 dali-toolkit/internal/filters/image-filter.cpp create mode 100644 dali-toolkit/internal/filters/image-filter.h create mode 100644 dali-toolkit/internal/filters/spread-filter.cpp create mode 100644 dali-toolkit/internal/filters/spread-filter.h create mode 100644 dali-toolkit/internal/focus-manager/focus-manager-impl.cpp create mode 100644 dali-toolkit/internal/focus-manager/focus-manager-impl.h create mode 100644 dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp create mode 100644 dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h create mode 100644 dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.cpp create mode 100644 dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.h create mode 100644 dali-toolkit/internal/shader-effects/page-turn-effect-impl.cpp create mode 100644 dali-toolkit/internal/shader-effects/page-turn-effect-impl.h create mode 100644 dali-toolkit/internal/shader-effects/water-effect-impl.cpp create mode 100644 dali-toolkit/internal/shader-effects/water-effect-impl.h create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.cpp create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.h create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-effect-impl.cpp create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-effect-impl.h create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.cpp create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.h create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.cpp create mode 100644 dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.h create mode 100644 dali-toolkit/public-api/builder/builder.cpp create mode 100644 dali-toolkit/public-api/builder/builder.h create mode 100644 dali-toolkit/public-api/builder/json-parser.cpp create mode 100644 dali-toolkit/public-api/builder/json-parser.h create mode 100644 dali-toolkit/public-api/builder/tree-node.cpp create mode 100644 dali-toolkit/public-api/builder/tree-node.h create mode 100644 dali-toolkit/public-api/controls/alignment/alignment.cpp create mode 100644 dali-toolkit/public-api/controls/bloom-view/bloom-view.cpp create mode 100644 dali-toolkit/public-api/controls/bloom-view/bloom-view.h create mode 100644 dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.cpp create mode 100644 dali-toolkit/public-api/controls/buttons/button.cpp create mode 100644 dali-toolkit/public-api/controls/buttons/check-box-button.cpp create mode 100644 dali-toolkit/public-api/controls/buttons/check-box-button.h create mode 100644 dali-toolkit/public-api/controls/buttons/push-button.cpp create mode 100644 dali-toolkit/public-api/controls/cluster/cluster-style.cpp create mode 100644 dali-toolkit/public-api/controls/cluster/cluster.cpp create mode 100644 dali-toolkit/public-api/controls/cluster/cluster.h create mode 100644 dali-toolkit/public-api/controls/control-impl.cpp create mode 100644 dali-toolkit/public-api/controls/control.cpp create mode 100644 dali-toolkit/public-api/controls/default-controls/check-button-factory.cpp create mode 100644 dali-toolkit/public-api/controls/default-controls/check-button-factory.h create mode 100644 dali-toolkit/public-api/controls/default-controls/push-button-factory.cpp create mode 100644 dali-toolkit/public-api/controls/default-controls/solid-color-actor.cpp create mode 100644 dali-toolkit/public-api/controls/effects-view/effects-view.cpp create mode 100644 dali-toolkit/public-api/controls/effects-view/effects-view.h create mode 100644 dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.cpp create mode 100644 dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.h create mode 100644 dali-toolkit/public-api/controls/image-view/image-view.cpp create mode 100644 dali-toolkit/public-api/controls/image-view/image-view.h create mode 100644 dali-toolkit/public-api/controls/image-view/masked-image-view.cpp create mode 100644 dali-toolkit/public-api/controls/magnifier/magnifier.cpp create mode 100644 dali-toolkit/public-api/controls/magnifier/magnifier.h create mode 100644 dali-toolkit/public-api/controls/navigation-frame/navigation-bar-style.h create mode 100644 dali-toolkit/public-api/controls/navigation-frame/navigation-control.cpp create mode 100644 dali-toolkit/public-api/controls/navigation-frame/navigation-control.h create mode 100644 dali-toolkit/public-api/controls/navigation-frame/page.cpp create mode 100644 dali-toolkit/public-api/controls/navigation-frame/page.h create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-factory.cpp create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-factory.h create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.cpp create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.h create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.cpp create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.h create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-view.cpp create mode 100644 dali-toolkit/public-api/controls/page-turn-view/page-turn-view.h create mode 100644 dali-toolkit/public-api/controls/popup/popup.cpp create mode 100755 dali-toolkit/public-api/controls/scroll-component/scroll-bar.cpp create mode 100644 dali-toolkit/public-api/controls/scroll-component/scroll-component.cpp create mode 100755 dali-toolkit/public-api/controls/scrollable/item-view/album-layout.cpp create mode 100755 dali-toolkit/public-api/controls/scrollable/item-view/album-layout.h create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.h create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/item-factory.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/item-layout.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/item-view.cpp create mode 100755 dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.cpp create mode 100755 dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.h create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.h create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.h create mode 100644 dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.cpp create mode 100644 dali-toolkit/public-api/controls/scrollable/scrollable.cpp create mode 100644 dali-toolkit/public-api/controls/selectors/rotating-selector.cpp create mode 100644 dali-toolkit/public-api/controls/selectors/rotating-selector.h create mode 100644 dali-toolkit/public-api/controls/shadow-view/shadow-view.cpp create mode 100644 dali-toolkit/public-api/controls/shadow-view/shadow-view.h create mode 100644 dali-toolkit/public-api/controls/slider/slider.cpp create mode 100644 dali-toolkit/public-api/controls/slider/slider.h create mode 100644 dali-toolkit/public-api/controls/super-blur-view/super-blur-view.cpp create mode 100644 dali-toolkit/public-api/controls/table-view/table-view.cpp create mode 100644 dali-toolkit/public-api/controls/table-view/table-view.h create mode 100644 dali-toolkit/public-api/controls/text-input/text-input.cpp create mode 100644 dali-toolkit/public-api/controls/text-view/text-view.cpp create mode 100644 dali-toolkit/public-api/controls/tool-bar/tool-bar.cpp create mode 100644 dali-toolkit/public-api/controls/tool-bar/tool-bar.h create mode 100644 dali-toolkit/public-api/controls/view/view.cpp create mode 100644 dali-toolkit/public-api/controls/view/view.h create mode 100644 dali-toolkit/public-api/enums.cpp create mode 100644 dali-toolkit/public-api/factory/localized-control-factory.cpp create mode 100755 dali-toolkit/public-api/file.list create mode 100644 dali-toolkit/public-api/focus-manager/focus-manager.cpp create mode 100644 dali-toolkit/public-api/focus-manager/keyboard-focus-manager.cpp create mode 100644 dali-toolkit/public-api/focus-manager/keyinput-focus-manager.cpp create mode 100644 dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h create mode 100644 dali-toolkit/public-api/markup-processor/markup-processor.cpp create mode 100644 dali-toolkit/public-api/shader-effects/alpha-discard-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/alpha-discard-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/bendy-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/bendy-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/blind-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/blind-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/bubble-effect/bubble-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/bubble-effect/bubble-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.cpp create mode 100644 dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.h create mode 100644 dali-toolkit/public-api/shader-effects/carousel-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/carousel-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/displacement-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/displacement-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/dissolve-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/dissolve-local-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/dissolve-local-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/distance-field-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/distance-field-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/image-region-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/iris-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/mask-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/mirror-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/mirror-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/motion-blur-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/motion-blur-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/motion-stretch-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/motion-stretch-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/overlay-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/overlay-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/page-turn-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/ripple-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/ripple2d-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/shear-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/shear-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/soft-button-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/soft-button-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/spot-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/spot-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/square-dissolve-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/square-dissolve-effect.h create mode 100644 dali-toolkit/public-api/shader-effects/swirl-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/water-effect.cpp create mode 100644 dali-toolkit/public-api/shader-effects/water-effect.h create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.cpp create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.h create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-effect.cpp create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-effect.h create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.cpp create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.h create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.cpp create mode 100644 dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.h create mode 100644 packaging/dali-toolkit.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8865c0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +.cproject +.project +.settings +.directory +Makefile.in +Makefile +*~ +*.o +*.o.d +*.pc +*.lo +*.loT +*.la +*.so +*.orig +*.odt +*.fodt +*.test +*.example +*.a +*.apk +*.ap_ +*.class +*.classpath +*.dex +*.gcno +*.gcda +*.gcov +.deps +.libs +*.swp +/docs/generated/* +/build/slp/doc +/build/slp/.cov +/build/desktop +/debian/build-stamp +/debian/config.status +/debian/libdali-toolkit-dbg.debhelper.log +/debian/libdali-toolkit-dbg +/debian/libdali-toolkit-dev.debhelper.log +/debian/libdali-toolkit-dev +/debian/libdali-toolkit.debhelper.log +/debian/libdali-toolkit.postinst.debhelper +/debian/libdali-toolkit.postrm.debhelper +/debian/libdali-toolkit.substvars +/debian/libdali-toolkit +/debian/tmp +/debian/files +/debian/libdali-toolkit-dbg.substvars +/debian/libdali-toolkit-dev.substvars +/debian/libdali-nsplugin.debhelper.log +/debian/libdali-nsplugin.postinst.debhelper +/debian/libdali-nsplugin.postrm.debhelper +/debian/libdali-nsplugin.substvars +/debian/libdali-nsplugin +/packaging/home* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..87ed7f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,205 @@ +Flora License + +Version 1.1, April, 2013 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Tizen Compliance Specification +and passes the Tizen Compliance Tests as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum + to the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional or + different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of + the Work otherwise complies with the conditions stated in this License + and your own copyright statement or terms and conditions do not conflict + the conditions stated in the License including section 3. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.1 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + 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. diff --git a/automated-tests/.gitignore b/automated-tests/.gitignore new file mode 100644 index 0000000..807a3b2 --- /dev/null +++ b/automated-tests/.gitignore @@ -0,0 +1,7 @@ +!Makefile +tet_captured +results-desktop +results-target +results +rules.mk +dali.info diff --git a/automated-tests/_export_desktop.sh b/automated-tests/_export_desktop.sh new file mode 100644 index 0000000..eeea3ed --- /dev/null +++ b/automated-tests/_export_desktop.sh @@ -0,0 +1,33 @@ +export ARCH=desktop + + +if [ -z "$DESKTOP_PREFIX" ] ; then + if [ -z "$PREFIX" ] ; then + echo "####################################################" + echo "# DESKTOP_PREFIX is not set. Recommend running #" + echo "# dali_env -s to create setenv script #" + echo "####################################################" + else + echo "####################################################" + echo "# DESKTOP_PREFIX is not set. Using PREFIX instead. #" + echo "# #" + echo "# Warning, PREFIX is deprecated, please use #" + echo "# dali_env to set up your environment. #" + echo "####################################################" + export DESKTOP_PREFIX=$PREFIX + fi +fi + +export TET_INSTALL_PATH=$HOME/Packages/tetware-desktop # Your tetware root path +export PATH=$TET_INSTALL_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_INSTALL_PATH/lib/tet3:$LD_LIBRARY_PATH +export TET_ROOT=$TET_INSTALL_PATH +export DALI_IMAGE_DIR=$DESKTOP_PREFIX/share/app.dalimenu/images/ +export DALI_MODEL_DIR=$DESKTOP_PREFIX/share/app.dalimenu/models/ +export DALI_STYLE_DIR=$DESKTOP_PREFIX/share/themes/dali/ + +set $(pwd) +export TET_SUITE_ROOT=$1 + +set $(date +%s) +FILE_NAME_EXTENSION=$1 diff --git a/automated-tests/_export_env.sh b/automated-tests/_export_env.sh new file mode 100755 index 0000000..4e2dc90 --- /dev/null +++ b/automated-tests/_export_env.sh @@ -0,0 +1,17 @@ +export ARCH=target + +export TET_INSTALL_PATH=/scratchbox/TETware # tetware root path +export TET_TARGET_PATH=$TET_INSTALL_PATH/tetware-target # tetware target path +export PATH=$TET_TARGET_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_TARGET_PATH/lib/tet3:$LD_LIBRARY_PATH +export DALI_IMAGE_DIR=/opt/share/app.dalimenu/images/ +export DALI_MODEL_DIR=/opt/share/app.dalimenu/models/ +export DALI_STYLE_DIR=/opt/share/themes/dali/ + +export TET_ROOT=$TET_TARGET_PATH + +set $(pwd) +export TET_SUITE_ROOT=$1 + +set $(date +%s) +FILE_NAME_EXTENSION=$1 diff --git a/automated-tests/_export_sbs.sh b/automated-tests/_export_sbs.sh new file mode 100644 index 0000000..bc5a4af --- /dev/null +++ b/automated-tests/_export_sbs.sh @@ -0,0 +1,17 @@ +export ARCH=target + +export TET_INSTALL_PATH=$HOME/git/TETware # tetware root path +export TET_TARGET_PATH=$TET_INSTALL_PATH +export PATH=$TET_TARGET_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_TARGET_PATH/lib/tet3:$LD_LIBRARY_PATH +export DALI_IMAGE_DIR=/opt/share/app.dalimenu/images/ +export DALI_MODEL_DIR=/opt/share/app.dalimenu/models/ +export DALI_STYLE_DIR=/opt/share/themes/dali/ + +export TET_ROOT=$TET_TARGET_PATH + +set $(pwd) +export TET_SUITE_ROOT=$1 + +set $(date +%s) +FILE_NAME_EXTENSION=$1 diff --git a/automated-tests/_export_target_env.sh b/automated-tests/_export_target_env.sh new file mode 100755 index 0000000..4c1b135 --- /dev/null +++ b/automated-tests/_export_target_env.sh @@ -0,0 +1,17 @@ +export ARCH=target + +export TET_INSTALL_PATH=/mnt/nfs/git/TETware # path to mount +export TET_TARGET_PATH=$TET_INSTALL_PATH +export PATH=$TET_TARGET_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_TARGET_PATH/lib/tet3:$LD_LIBRARY_PATH +export DALI_IMAGE_DIR=/opt/share/app.dalimenu/images/ +export DALI_MODEL_DIR=/opt/share/app.dalimenu/models/ +export DALI_STYLE_DIR=/opt/share/themes/dali/ + +export TET_ROOT=$TET_TARGET_PATH + +set $(pwd) +export TET_SUITE_ROOT=$1 + +set $(date +%s) +FILE_NAME_EXTENSION=$1 diff --git a/automated-tests/build.sh b/automated-tests/build.sh new file mode 100755 index 0000000..d857de3 --- /dev/null +++ b/automated-tests/build.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +TEMP=`getopt -o 2vds: --long 2,verbose,desktop,scenario: \ + -n 'build_out.sh' -- "$@"` + +if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +scenario=all +opt_verbose=0 +opt_env=scratchbox + +while true ; do + case "$1" in + -d|--desktop) opt_env=desktop ; shift ;; + -s|--scenario) scenario="$2" ; shift 2 ;; + -v|--verbose) opt_verbose=1 ; shift ;; + -2|--2) opt_env=sbs ; shift ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + + +case "$opt_env" in + desktop) + . _export_desktop.sh + cat < coverage.mk +LDFLAGS += --coverage +EOF + ;; + scratchbox) + . _export_env.sh + cat < coverage.mk +LDFLAGS += +EOF + ;; + sbs) + . _export_sbs.sh + cat < coverage.mk +LDFLAGS += +EOF + ;; +esac + + +echo PATH=$PATH +echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH +echo TET_ROOT=$TET_ROOT +echo TET_SUITE_ROOT=$TET_SUITE_ROOT +echo ARCH=$ARCH + +RESULT_DIR=results-$ARCH +HTML_RESULT=$RESULT_DIR/build-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/build-tar-result-$FILE_NAME_EXTENSION.journal + +# Faster cleanup. +find . -name Makefile -execdir make -f {} clean \; + +mkdir -p $RESULT_DIR +if [ $opt_verbose -eq 1 ] ; then + tcc -b -j - ./ $scenario | tee $JOURNAL_RESULT +else + tcc -b -j $JOURNAL_RESULT -p ./ $scenario +fi +./tbp.pl $JOURNAL_RESULT + + diff --git a/automated-tests/build_out.sh b/automated-tests/build_out.sh new file mode 100755 index 0000000..efb93a1 --- /dev/null +++ b/automated-tests/build_out.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./build.sh -v $* diff --git a/automated-tests/coverage.mk b/automated-tests/coverage.mk new file mode 100644 index 0000000..5012fd4 --- /dev/null +++ b/automated-tests/coverage.mk @@ -0,0 +1 @@ +LDFLAGS += --coverage diff --git a/automated-tests/coverage.sh b/automated-tests/coverage.sh new file mode 100755 index 0000000..98798db --- /dev/null +++ b/automated-tests/coverage.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +( cd ../build/slp ; make cov_data ) + +for i in `find . -name Makefile` ; do + ( + cd $(dirname $i) + echo `pwd` + covs=( `ls *.gcda 2>/dev/null` ) + if [[ $? -eq 0 ]] + then + make coverage + fi + ) +done + +( + cd .. ; + genhtml -o build/slp/doc/coverage `find . -name dali.info` +) + + diff --git a/automated-tests/dali-internal-test-suite/master-makefile.mk b/automated-tests/dali-internal-test-suite/master-makefile.mk new file mode 100644 index 0000000..a8e9921 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/master-makefile.mk @@ -0,0 +1,53 @@ +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd. +# +# Licensed under the Flora License, Version 1.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://floralicense.org/license/ +# +# 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. +# + +CC = g++ + +TARGETS = +include file.list + +PKGS = dali-core dali dali-toolkit dali-test-suite-utils +include ../../rules.mk +include ../../coverage.mk + +TOOLKIT_TEST_UTILS_DIR=../../dali-toolkit-test-utils/ + +CXXFLAGS += -I$(TOOLKIT_TEST_UTILS_DIR) + +TOOLKIT_TEST_UTILS_SRC_FILES = \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-application.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-adaptor.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-clipboard-event-notifier.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-accessibility-manager.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-physical-keyboard.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-style-monitor.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-timer.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-orientation.cpp + +all: $(TARGETS) + +%: %.cpp $(TOOLKIT_TEST_UTILS_SRC_FILES) + $(CC) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + @rm -f $(notdir $(TARGETS)) + @rm -f tet_captured + @rm -f *~ + @rm -f *.gcda *.gcno + +coverage: + @lcov --directory . -c -o dali.info + @lcov --remove dali.info "*boost*" "/usr/include/*" "*/automated-tests/*" -o dali.info diff --git a/automated-tests/dali-internal-test-suite/tc-gen.sh b/automated-tests/dali-internal-test-suite/tc-gen.sh new file mode 120000 index 0000000..b8e6201 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/tc-gen.sh @@ -0,0 +1 @@ +../dali-test-suite/tc-gen.sh \ No newline at end of file diff --git a/automated-tests/dali-internal-test-suite/text-input/.gitignore b/automated-tests/dali-internal-test-suite/text-input/.gitignore new file mode 100644 index 0000000..0f77dca --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-input/.gitignore @@ -0,0 +1 @@ +utc-Dali-TextInput diff --git a/automated-tests/dali-internal-test-suite/text-input/Makefile b/automated-tests/dali-internal-test-suite/text-input/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-input/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-internal-test-suite/text-input/file.list b/automated-tests/dali-internal-test-suite/text-input/file.list new file mode 100644 index 0000000..1e6d688 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-input/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-TextInput \ diff --git a/automated-tests/dali-internal-test-suite/text-input/tslist b/automated-tests/dali-internal-test-suite/text-input/tslist new file mode 100644 index 0000000..7bbabc6 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-input/tslist @@ -0,0 +1 @@ +/dali-internal-test-suite/text-input/utc-Dali-TextInput diff --git a/automated-tests/dali-internal-test-suite/text-input/utc-Dali-TextInput.cpp b/automated-tests/dali-internal-test-suite/text-input/utc-Dali-TextInput.cpp new file mode 100644 index 0000000..6315dde --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-input/utc-Dali-TextInput.cpp @@ -0,0 +1,145 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +#include + +// Internal includes +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliInternalTextInputTextSelection, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetGetExceedEnabled, POSITIVE_TC_IDX ); +// TEST_FUNCTION( UtcDaliTextInputMethod02, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +/** + * Test: Selected is replaced by new input text. + **/ +static void UtcDaliInternalTextInputTextSelection() +{ + const char* testChar = "v"; + + ToolkitTestApplication application; + + Dali::Integration::Core& core ( application.GetCore() ); + + tet_infoline("Testing Text Selection with replace."); + + Toolkit::TextInput textInput = Toolkit::TextInput::New(); + + DALI_TEST_CHECK(textInput); + + Stage::GetCurrent().Add(textInput); + + textInput.SetInitialText("Test String"); + + std::string initialText = textInput.GetText(); + + tet_printf("Set Initial text: %s\n", initialText.c_str() ); + + textInput.SetKeyInputFocus(); + + GetImpl(textInput).SelectText(0,11); + + tet_printf("Select all of Initial text\n"); + + Integration::KeyEvent event(testChar, testChar, 0, 0, 0, Integration::KeyEvent::Down ); + + core.SendEvent( event ); + + tet_printf("Simulate pressing of a key: %s\n", testChar ); + + std::string newText = textInput.GetText(); + + tet_printf("Check current text (%s) is the new text \n", newText.c_str() ); + + DALI_TEST_EQUALS("v",textInput.GetText(), TEST_LOCATION); +} + +static void UtcDaliTextInputSetGetExceedEnabled() +{ + tet_infoline("UtcDaliTextInputSetGetExceedEnabled: "); + + ToolkitTestApplication application; + + Toolkit::TextInput textInput = Toolkit::TextInput::New(); + textInput.SetMultilinePolicy( Toolkit::TextView::SplitByWord ); + textInput.SetWidthExceedPolicy( Toolkit::TextView::Split ); + textInput.SetHeightExceedPolicy( Toolkit::TextView::Original ); + + DALI_TEST_CHECK( textInput.GetExceedEnabled() ); + + Toolkit::Internal::TextInput& textInputImpl = static_cast( textInput.GetImplementation() ); + + textInput.SetSize( 50.f, 50.f ); + textInput.SetExceedEnabled( false ); + + DALI_TEST_CHECK( !textInput.GetExceedEnabled() ); + + + textInputImpl.InsertAt( Text("He"), 0 ); + + DALI_TEST_EQUALS("He",textInput.GetText(), TEST_LOCATION); + + textInputImpl.InsertAt( Text("llo"), 2 ); + + DALI_TEST_EQUALS("Hello",textInput.GetText(), TEST_LOCATION); + + textInputImpl.InsertAt( Text(" world! hello world hello world hello world"), 5 ); // Doesn't fit so is not added. + + DALI_TEST_EQUALS("Hello",textInput.GetText(), TEST_LOCATION); +} diff --git a/automated-tests/dali-internal-test-suite/text-view/.gitignore b/automated-tests/dali-internal-test-suite/text-view/.gitignore new file mode 100644 index 0000000..881389b --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/.gitignore @@ -0,0 +1,5 @@ +utc-Dali-TextView +utc-Dali-TextView-HelperAndDebug +utc-Dali-TextView-Processor +utc-Dali-TextView-Processor-Types +utc-Dali-TextView-Relayout-Utilities diff --git a/automated-tests/dali-internal-test-suite/text-view/Makefile b/automated-tests/dali-internal-test-suite/text-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-internal-test-suite/text-view/file.list b/automated-tests/dali-internal-test-suite/text-view/file.list new file mode 100644 index 0000000..7e75352 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/file.list @@ -0,0 +1,6 @@ +TARGETS += \ + utc-Dali-TextView \ + utc-Dali-TextView-HelperAndDebug \ + utc-Dali-TextView-Processor \ + utc-Dali-TextView-Processor-Types \ + utc-Dali-TextView-Relayout-Utilities \ \ No newline at end of file diff --git a/automated-tests/dali-internal-test-suite/text-view/tslist b/automated-tests/dali-internal-test-suite/text-view/tslist new file mode 100644 index 0000000..5ee4f51 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/tslist @@ -0,0 +1,5 @@ +/dali-internal-test-suite/text-view/utc-Dali-TextView +/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor +/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor-Types +/dali-internal-test-suite/text-view/utc-Dali-TextView-Relayout-Utilities +/dali-internal-test-suite/text-view/utc-Dali-TextView-HelperAndDebug diff --git a/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-HelperAndDebug.cpp b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-HelperAndDebug.cpp new file mode 100644 index 0000000..494039b --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-HelperAndDebug.cpp @@ -0,0 +1,304 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +namespace +{ +// Data structures used to create an 'experiment' in TET cases + +const Toolkit::Internal::TextView::LayoutParameters DEFAULT_LAYOUT_PARAMETERS; +const Toolkit::Internal::TextView::VisualParameters DEFAULT_VISUAL_PARAMETERS; + +struct GetIndicesFromGlobalCharacterIndexTest +{ + std::string description; + std::string input; + std::size_t position; + std::size_t lineIndex; + std::size_t groupIndex; + std::size_t wordIndex; + std::size_t characterIndex; +}; + +/** + * Gets the line, group, word, and character indices for a given text and a given position and checks the results with the given indices. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param input The input text. + * @param position Global position of the character. i.e in a text with with 1000 characters, position could be any value from 0 to 1000. + * @param resultLineIndex Index to the line where the character is located. + * @param resultGroupIndex Index to the group within the line where the character is located. + * @param resultWordIndex Index to the word within the group where the character is located. + * @param resultCharacterIndex Index to the character within the word where the character is located. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestGetIndicesFromGlobalCharacterIndex( const std::string& description, + const std::string& input, + const std::size_t position, + const std::size_t resultLineIndex, + const std::size_t resultGroupIndex, + const std::size_t resultWordIndex, + const std::size_t resultCharacterIndex, + const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create natural size, layout and text-actor info for the input word. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + TextViewProcessor::TextInfoIndices indices; + TextViewProcessor::GetIndicesFromGlobalCharacterIndex( position, + inputLayout, + indices ); + + if( indices.mLineIndex != resultLineIndex ) + { + tet_printf( "Fail. different line index. %s", location ); + return false; + } + if( indices.mGroupIndex != resultGroupIndex ) + { + tet_printf( "Fail. different group index. %s", location ); + return false; + } + if( indices.mWordIndex != resultWordIndex ) + { + tet_printf( "Fail. different word index. %s", location ); + return false; + } + if( indices.mCharacterIndex != resultCharacterIndex ) + { + tet_printf( "Fail. different character index. %s", location ); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewGetIndicesFromGlobalCharacterIndex, POSITIVE_TC_IDX ); // Tests correctness when indices to lines, groups, words and characters are worked out from a given global position. +TEST_FUNCTION( UtcDaliTextViewDebugCouts, POSITIVE_TC_IDX ); // Tests debug functions just to not to penalize the coverage. + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliTextViewGetIndicesFromGlobalCharacterIndex() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewGetIndicesFromGlobalCharacterIndex : "); + struct GetIndicesFromGlobalCharacterIndexTest getIndicesFromGlobalCharacterIndexTests[] = + { + { + std::string( "Test position 0" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 0, + 0, + 0, + 0, + 0 + }, + { + std::string( "Test position 76. (just after the last \\n)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 76, + 4, + 0, + 0, + 0 + }, + { + std::string( "Test position 73. (the last \\n)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 75, + 3, + 0, + 0, + 0 + }, + { + std::string( "Test position 35. (first hebrew character)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 35, + 1, + 1, + 0, + 0 + }, + { + std::string( "Test position 3. (end of the first word)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 3, + 0, + 0, + 0, + 3 + }, + /* TODO Check for mixed RTL and LTR text. + { + std::string( "Test position 33. (end of the second word of the second line)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 33, + 1, + 0, + 2, + 3 + }, + { + std::string( "Test position 43. (last hebrew character)" ), + std::string( "text text text text text\n" + "text text טקסט טקסט text\n" + "text text text text text\n" + "\n" ), + 43, + 1, + 1, + 3, + 3 + }, + */ + }; + const std::size_t numberOfTests( 5 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const GetIndicesFromGlobalCharacterIndexTest& test = getIndicesFromGlobalCharacterIndexTests[index]; + + if( !TestGetIndicesFromGlobalCharacterIndex( test.description, test.input, test.position, test.lineIndex, test.groupIndex, test.wordIndex, test.characterIndex, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewDebugCouts() +{ + ///////////////////////////////////////////////////// + // Text debug functions to not to penalize coverage + ///////////////////////////////////////////////////// + + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewDebugCouts : "); + + Toolkit::Internal::TextView::RelayoutData relayoutData; + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( std::string( "Hello world\nhello world" ), inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + Actor dummy = Actor::New(); + Toolkit::Internal::SplitByNewLineChar::Relayout( dummy, + Toolkit::Internal::TextView::RELAYOUT_ALL, + DEFAULT_LAYOUT_PARAMETERS, + DEFAULT_VISUAL_PARAMETERS, + relayoutData ); + + TextViewProcessor::dbgPrint( relayoutData.mTextLayoutInfo ); + + TextStyle textStyle; + TextViewProcessor::dbgPrint( textStyle ); + + TextViewProcessor::TextInfoIndices indices; + TextViewProcessor::dbgPrint( indices ); + + TextViewProcessor::dbgPrint( inputStyledText ); + + tet_result( TET_PASS ); +} diff --git a/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor-Types.cpp b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor-Types.cpp new file mode 100644 index 0000000..3591c26 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor-Types.cpp @@ -0,0 +1,311 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +namespace +{ +// Data structures used to create an 'experiment' in TET cases + +////////////////////////////////////////////////////////////////// +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewDefaultConstructorDestructor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewCopyConstructorOperator, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewEqualityOperator, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliTextViewDefaultConstructorDestructor() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewDefaultConstructorDestructor : "); + + TextViewProcessor::TextInfoIndices indices; + DALI_TEST_EQUALS( indices.mLineIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( indices.mGroupIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( indices.mWordIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( indices.mCharacterIndex, 0u, TEST_LOCATION ); + + TextViewProcessor::CharacterLayoutInfo characterLayoutInfo; + DALI_TEST_EQUALS( characterLayoutInfo.mHeight, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mAdvance, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mBearing, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mPosition, Vector3::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mOffset, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mUnderlineThickness, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mUnderlinePosition, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( !characterLayoutInfo.mTextActor ); + DALI_TEST_CHECK( characterLayoutInfo.mStyledText.mText.IsEmpty() ); + DALI_TEST_EQUALS( characterLayoutInfo.mColorAlpha, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mGradientColor, Vector4::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mStartPoint, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mEndPoint, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( characterLayoutInfo.mIsVisible ); + DALI_TEST_CHECK( characterLayoutInfo.mSetText ); + DALI_TEST_CHECK( characterLayoutInfo.mSetStyle ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo; + DALI_TEST_EQUALS( wordLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo.mAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo.mType, TextViewProcessor::NoSeparator, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo.mCharactersLayoutInfo.size(), 0u, TEST_LOCATION ); + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo; + DALI_TEST_EQUALS( wordGroupLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mDirection, TextViewProcessor::LTR, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mWordsLayoutInfo.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mNumberOfCharacters, 0u, TEST_LOCATION ); + + TextViewProcessor::LineLayoutInfo lineLayoutInfo; + DALI_TEST_EQUALS( lineLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo.mAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo.mLineHeightOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo.mWordGroupsLayoutInfo.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo.mNumberOfCharacters, 0u, TEST_LOCATION ); + + TextViewProcessor::TextLayoutInfo textLayoutInfo; + DALI_TEST_EQUALS( textLayoutInfo.mWholeTextSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mMaxWordWidth, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mLinesLayoutInfo.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mNumberOfCharacters, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mMaxItalicsOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mEllipsizeLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mEllipsizeLayoutInfo.mAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mEllipsizeLayoutInfo.mType, TextViewProcessor::NoSeparator, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mEllipsizeLayoutInfo.mCharactersLayoutInfo.size(), 0u, TEST_LOCATION ); +} + +static void UtcDaliTextViewCopyConstructorOperator() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCopyConstructorOperator : "); + + TextViewProcessor::CharacterLayoutInfo characterLayoutInfo; + characterLayoutInfo.mHeight = 1.f; + characterLayoutInfo.mAdvance = 1.f; + characterLayoutInfo.mBearing = 1.f; + characterLayoutInfo.mPosition = Vector3( 1.f, 1.f, 1.f ); + characterLayoutInfo.mOffset = Vector2( 1.f, 1.f ); + characterLayoutInfo.mSize = Vector2( 1.f, 1.f ); + characterLayoutInfo.mAscender = 1.f; + characterLayoutInfo.mUnderlineThickness = 1.f; + characterLayoutInfo.mUnderlinePosition = 1.f; + + characterLayoutInfo.mTextActor = TextActor::New( "Hello" ); + characterLayoutInfo.mStyledText.mText = Text( "Hello" ); + + characterLayoutInfo.mColorAlpha = 0.f; + characterLayoutInfo.mGradientColor = Vector4( 1.f, 1.f, 1.f, 1.f ); + characterLayoutInfo.mStartPoint = Vector2( 1.f, 1.f ); + characterLayoutInfo.mEndPoint = Vector2( 1.f, 1.f ); + characterLayoutInfo.mIsVisible = false; + characterLayoutInfo.mSetText = false; + characterLayoutInfo.mSetStyle = false; + + TextViewProcessor::CharacterLayoutInfo characterLayoutInfo1; + characterLayoutInfo1 = characterLayoutInfo; + + DALI_TEST_EQUALS( characterLayoutInfo1.mHeight, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mAdvance, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mBearing, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mPosition, Vector3( 1.f, 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mOffset, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mUnderlineThickness, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mUnderlinePosition, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( characterLayoutInfo1.mTextActor ); + DALI_TEST_EQUALS( characterLayoutInfo1.mStyledText.mText.GetLength(), 5u, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mColorAlpha, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mGradientColor, Vector4( 1.f, 1.f, 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mStartPoint, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo1.mEndPoint, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( !characterLayoutInfo1.mIsVisible ); + DALI_TEST_CHECK( !characterLayoutInfo1.mSetText ); + DALI_TEST_CHECK( !characterLayoutInfo1.mSetStyle ); + + TextViewProcessor::CharacterLayoutInfo characterLayoutInfo2( characterLayoutInfo ); + DALI_TEST_EQUALS( characterLayoutInfo2.mHeight, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mAdvance, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mBearing, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mPosition, Vector3( 1.f, 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mOffset, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mUnderlineThickness, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mUnderlinePosition, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( characterLayoutInfo2.mTextActor ); + DALI_TEST_EQUALS( characterLayoutInfo2.mStyledText.mText.GetLength(), 5u, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mColorAlpha, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mGradientColor, Vector4( 1.f, 1.f, 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mStartPoint, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo2.mEndPoint, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( !characterLayoutInfo2.mIsVisible ); + DALI_TEST_CHECK( !characterLayoutInfo2.mSetText ); + DALI_TEST_CHECK( !characterLayoutInfo2.mSetStyle ); + + // Increases coverage. + characterLayoutInfo2.mTextActor.Reset(); + characterLayoutInfo1 = characterLayoutInfo2; + DALI_TEST_CHECK( !characterLayoutInfo1.mTextActor ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo; + wordLayoutInfo.mSize = Vector2( 1.f, 1.f ); + wordLayoutInfo.mAscender = 1.f; + wordLayoutInfo.mType = TextViewProcessor::LineSeparator; + + TextViewProcessor::WordLayoutInfo wordLayoutInfo1; + wordLayoutInfo1 = wordLayoutInfo; + + DALI_TEST_EQUALS( wordLayoutInfo1.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo1.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo1.mType, TextViewProcessor::LineSeparator, TEST_LOCATION ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo2( wordLayoutInfo ); + + DALI_TEST_EQUALS( wordLayoutInfo2.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo2.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordLayoutInfo2.mType, TextViewProcessor::LineSeparator, TEST_LOCATION ); + + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo; + wordGroupLayoutInfo.mSize = Vector2( 1.f, 1.f ); + wordGroupLayoutInfo.mAscender = 1.f; + wordGroupLayoutInfo.mDirection = TextViewProcessor::RTL; + wordGroupLayoutInfo.mNumberOfCharacters = 1u; + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo1; + wordGroupLayoutInfo1 = wordGroupLayoutInfo; + + DALI_TEST_EQUALS( wordGroupLayoutInfo.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mDirection, TextViewProcessor::RTL, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mNumberOfCharacters, 1u, TEST_LOCATION ); + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo2( wordGroupLayoutInfo ); + + DALI_TEST_EQUALS( wordGroupLayoutInfo.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mDirection, TextViewProcessor::RTL, TEST_LOCATION ); + DALI_TEST_EQUALS( wordGroupLayoutInfo.mNumberOfCharacters, 1u, TEST_LOCATION ); + + + TextViewProcessor::LineLayoutInfo lineLayoutInfo; + lineLayoutInfo.mSize = Vector2( 1.f, 1.f ); + lineLayoutInfo.mAscender = 1.f; + lineLayoutInfo.mLineHeightOffset = 1.f; + lineLayoutInfo.mNumberOfCharacters = 1u; + + TextViewProcessor::LineLayoutInfo lineLayoutInfo1; + lineLayoutInfo1 = lineLayoutInfo; + + DALI_TEST_EQUALS( lineLayoutInfo1.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo1.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo1.mLineHeightOffset, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo1.mNumberOfCharacters, 1u, TEST_LOCATION ); + + TextViewProcessor::LineLayoutInfo lineLayoutInfo2( lineLayoutInfo ); + + DALI_TEST_EQUALS( lineLayoutInfo2.mSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo2.mAscender, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo2.mLineHeightOffset, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( lineLayoutInfo2.mNumberOfCharacters, 1u, TEST_LOCATION ); + + TextViewProcessor::TextLayoutInfo textLayoutInfo; + textLayoutInfo.mWholeTextSize = Vector2( 1.f, 1.f ); + textLayoutInfo.mMaxWordWidth = 1.f; + textLayoutInfo.mNumberOfCharacters = 1u; + textLayoutInfo.mMaxItalicsOffset = 1.f; + + TextViewProcessor::TextLayoutInfo textLayoutInfo1; + textLayoutInfo1 = textLayoutInfo; + + DALI_TEST_EQUALS( textLayoutInfo1.mWholeTextSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo1.mMaxWordWidth, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo1.mNumberOfCharacters, 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo1.mMaxItalicsOffset, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + TextViewProcessor::TextLayoutInfo textLayoutInfo2( textLayoutInfo ); + + DALI_TEST_EQUALS( textLayoutInfo2.mWholeTextSize, Vector2( 1.f, 1.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mMaxWordWidth, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mNumberOfCharacters, 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mMaxItalicsOffset, 1.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliTextViewEqualityOperator() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewEqualityOperator : "); + + TextViewProcessor::TextInfoIndices indices; + TextViewProcessor::TextInfoIndices indices1( 1u, 1u, 1u, 1u ); + + DALI_TEST_CHECK( !( indices == indices1 ) ); + + indices = indices1; + + DALI_TEST_CHECK( indices == indices1 ); +} diff --git a/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor.cpp b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor.cpp new file mode 100644 index 0000000..8f9a85e --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Processor.cpp @@ -0,0 +1,428 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +namespace +{ +// Data structures used to create an 'experiment' in TET cases + +////////////////////////////////////////////////////////////////// + +struct BeginsRightToLeftCharacterTest +{ + std::string description; + std::string input; + bool result; +}; + +bool TestBeginsRightToLeftCharacter( const std::string& description, const std::string& input, const bool result, const char* location ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( input, styledText ); + + const bool ret = ( result == TextProcessor::BeginsRightToLeftCharacter( styledText ) ); + + if( !ret ) + { + tet_printf( "Fail. %s", location ); + tet_printf( "Input : %s", input.c_str() ); + } + + return ret; +} + +////////////////////////////////////////////////////////////////// + +struct ContainsRightToLeftCharacterTest +{ + std::string description; + std::string input; + bool result; +}; + +bool TestContainsRightToLeftCharacter( const std::string& description, const std::string& input, const bool result, const char* location ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( input, styledText ); + + const bool ret = ( result == TextProcessor::ContainsRightToLeftCharacter( styledText ) ); + + if( !ret ) + { + tet_printf( "Fail. %s", location ); + tet_printf( "Input : %s", input.c_str() ); + } + + return ret; +} + +////////////////////////////////////////////////////////////////// + +struct FindNearestWordTest +{ + std::string description; + std::string input; + std::size_t offset; + std::size_t start; + std::size_t end; +}; + +bool TestFindNearestWord( const std::string& description, const std::string& input, const std::size_t offset, const std::size_t startResult, const std::size_t endResult, const char* location ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( input, styledText ); + + std::size_t start; + std::size_t end; + TextProcessor::FindNearestWord( styledText, offset, start, end ); + + const bool ret = ( start == startResult ) && ( end == endResult ); + + if( !ret ) + { + tet_printf( "Fail. %s", location ); + tet_printf( "Input : %s, offset %d, start %d, end %d", input.c_str(), offset, start, end ); + } + + return ret; +} + +////////////////////////////////////////////////////////////////// + +struct SplitInLinesTest +{ + std::string inputText; + + std::size_t resultNumberOfLines; +}; + +bool TestSplitInLines( const SplitInLinesTest& test, const char* location ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( test.inputText, styledText ); + + std::vector lines; + + TextProcessor::SplitInLines( styledText, + lines ); + + if( lines.size() != test.resultNumberOfLines ) + { + tet_printf( "Fail. %s", location ); + tet_printf( "Different number of lines, result %d, expected result %d", lines.size(), test.resultNumberOfLines ); + + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +struct SplitInWordsTest +{ + std::string inputText; + + std::size_t resultNumberOfWords; +}; + +bool TestSplitInWords( const SplitInWordsTest& test, const char* location ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( test.inputText, styledText ); + + std::vector words; + + TextProcessor::SplitInWords( styledText, + words ); + + if( words.size() != test.resultNumberOfWords ) + { + tet_printf( "Fail. %s", location ); + tet_printf( "Different number of words, result %d, expected result %d", words.size(), test.resultNumberOfWords ); + + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewSplitInLines, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSplitInWords, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewBeginsRightToLeftCharacter, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewContainsRightToLeftCharacter, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewFindNearestWord, POSITIVE_TC_IDX ); +// TEST_FUNCTION( , POSITIVE_TC_IDX ); +// TEST_FUNCTION( , NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliTextViewSplitInLines() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewSplitInLines : "); + + struct SplitInLinesTest splitInLinesTest[] = + { + { + std::string( "Hello world\nhello world." ), + 2 + }, + { + std::string( "Hello world\nhello world.\n\n" ), + 4 + } + }; + const std::size_t numberOfTests( 2 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const SplitInLinesTest& test = splitInLinesTest[index]; + + if( !TestSplitInLines( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewSplitInWords() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewSplitInWords : "); + + struct SplitInWordsTest splitInWordsTest[] = + { + { + std::string( "Hello world, hello word!" ), + 7 + }, + }; + const std::size_t numberOfTests( 1 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const SplitInWordsTest& test = splitInWordsTest[index]; + + if( !TestSplitInWords( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewBeginsRightToLeftCharacter() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewBeginsRightToLeftCharacter : "); + + struct BeginsRightToLeftCharacterTest beginsRightToLeftCharacterTest[] = + { + { + std::string( "Test if it begins with a right to left character. Should return false." ), + std::string( "Hello world مرحبا العالم." ), + false + }, + { + std::string( "Test if it begins with a right to left character. Should return true." ), + std::string( "مرحبا العالم Hola mundo." ), + true + } + }; + const std::size_t numberOfTests( 2 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const BeginsRightToLeftCharacterTest& test = beginsRightToLeftCharacterTest[index]; + + if( !TestBeginsRightToLeftCharacter( test.description, test.input, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewContainsRightToLeftCharacter() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewContainsRightToLeftCharacter : "); + + struct ContainsRightToLeftCharacterTest containsRightToLeftCharacterTest[] = + { + { + std::string( "Test if it contains a right to left character. Should return true." ), + std::string( "Hello world مرحبا العالم." ), + true + }, + { + std::string( "Test if it contains a right to left character. Should return true." ), + std::string( "مرحبا العالم Hola mundo." ), + true + }, + { + std::string( "Test if it contains a right to left character. Should return false." ), + std::string( "Hello world." ), + false + }, + { + std::string( "Test if it contains a right to left character. Should return true." ), + std::string( "مرحبا العالم." ), + true + } + }; + const std::size_t numberOfTests( 4 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const ContainsRightToLeftCharacterTest& test = containsRightToLeftCharacterTest[index]; + + if( !TestContainsRightToLeftCharacter( test.description, test.input, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewFindNearestWord() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewFindNearestWord : "); + + struct FindNearestWordTest findNearestWordTest[] = + { + { + std::string( "" ), + std::string( "Hello world, hola mundo" ), + 0u, + 0u, + 5u + }, + { + std::string( "" ), + std::string( "Hello world, hola mundo" ), + 7u, + 6u, + 12u + }, + { + std::string( "" ), + std::string( "Hello world, hola mundo" ), + 11u, + 6u, + 12u + }, + { + std::string( "" ), + std::string( "Hello world, hola mundo" ), + 23u, + 18u, + 23u + }, + { + std::string( "" ), + std::string( "Hello world, hola mundo" ), + 5u, + 0u, + 5u + }, + { + std::string( "" ), + std::string( "Hello world, hola mundo مرحبا العالم" ), + 24u, + 25u, + 30u + } + }; + + const std::size_t numberOfTests( 6 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const FindNearestWordTest& test = findNearestWordTest[index]; + + if( !TestFindNearestWord( test.description, test.input, test.offset, test.start, test.end, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} diff --git a/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Relayout-Utilities.cpp b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Relayout-Utilities.cpp new file mode 100644 index 0000000..9b5ddef --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView-Relayout-Utilities.cpp @@ -0,0 +1,856 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +namespace +{ + +const Toolkit::Internal::TextView::LayoutParameters DEFAULT_LAYOUT_PARAMETERS; + +// Data structures used to create an 'experiment' in TET cases + + +bool TestEqual( float x, float y ) +{ + return ( fabsf( x - y ) < Math::MACHINE_EPSILON_1000 ); +} + +////////////////////////////////////////////////////////////////// + +struct CalculateSubLineLayoutTest +{ + std::string description; + std::string inputLine; + float parentWidth; + std::size_t groupIndex; + std::size_t wordIndex; + std::size_t characterIndex; + TextViewRelayout::HorizontalWrapType splitPolicy; + float shrinkFactor; + + float resultLineLength; + float resultMaxCharHeight; + float resultMaxAscender; +}; + +bool TestCalculateSubLineLayout( const CalculateSubLineLayoutTest& test, const char* location ) +{ + tet_printf( "%s", test.description.c_str() ); + + // Create styled text. + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( test.inputLine, inputStyledText ); + + // Create styled text layout info. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + // Prepare input parameters and the result structure and call the function to be tested. + + // Creaqte indices. + TextViewProcessor::TextInfoIndices indices( 0, test.groupIndex, test.wordIndex, test.characterIndex ); + + // Get the input line. + TextViewProcessor::LineLayoutInfo inputLineLayout; + + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + inputLineLayout = *relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(); + } + + // Result struct. + TextViewRelayout::SubLineLayoutInfo resultLayoutInfo; + + CalculateSubLineLayout( test.parentWidth, + indices, + inputLineLayout, + test.splitPolicy, + test.shrinkFactor, + resultLayoutInfo ); + + // Check results. + if( !TestEqual( test.resultLineLength, resultLayoutInfo.mLineLength ) ) + { + tet_printf( "Fail. different line length %f == %f. %s", test.resultLineLength, resultLayoutInfo.mLineLength, location ); + return false; + } + + if( !TestEqual( test.resultMaxCharHeight, resultLayoutInfo.mMaxCharHeight ) ) + { + tet_printf( "Fail. different max character height %f == %f. %s", test.resultMaxCharHeight, resultLayoutInfo.mMaxCharHeight, location ); + return false; + } + + if( !TestEqual( test.resultMaxAscender, resultLayoutInfo.mMaxAscender ) ) + { + tet_printf( "Fail. different max ascender %f == %f. %s", test.resultMaxAscender, resultLayoutInfo.mMaxAscender, location ); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +struct AlignmentOffsetTest +{ + Toolkit::Alignment::Type alignment; + float parentSize; + float wholeTextSize; + + float resultOffset; +}; + +bool TestAlignmentOffset( const AlignmentOffsetTest& test, const char* location ) +{ + float offset = 0.f; + + switch( test.alignment ) + { + case Toolkit::Alignment::HorizontalLeft: + case Toolkit::Alignment::HorizontalCenter: + case Toolkit::Alignment::HorizontalRight: + { + offset = TextViewRelayout::CalculateXoffset( test.alignment, test.parentSize, test.wholeTextSize ); + break; + } + case Toolkit::Alignment::VerticalTop: + case Toolkit::Alignment::VerticalCenter: + case Toolkit::Alignment::VerticalBottom: + { + offset = TextViewRelayout::CalculateYoffset( test.alignment, test.parentSize, test.wholeTextSize ); + break; + } + } + + // Check results. + if( !TestEqual( test.resultOffset, offset ) ) + { + tet_printf( "Fail. different offset %f == %f. %s", test.resultOffset, offset, location ); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +struct JustificationOffsetTest +{ + Toolkit::TextView::LineJustification justification; + float wholeTextWidth; + float lineLength; + + float resultOffset; +}; + +bool TestJustificationOffset( const JustificationOffsetTest& test, const char* location ) +{ + float offset = TextViewRelayout::CalculateJustificationOffset( test.justification, test.wholeTextWidth, test.lineLength ); + + // Check results. + if( !TestEqual( test.resultOffset, offset ) ) + { + tet_printf( "Fail. different offset %f == %f. %s", test.resultOffset, offset, location ); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +struct CalculateVisibilityTest +{ + Vector3 position; + Size size; + Size parentSize; + TextViewRelayout::VisibilityTestType type; + + bool resultVisible; +}; + +bool TestCalculateVisibility( const CalculateVisibilityTest& test, const char* location ) +{ + if( test.resultVisible != TextViewRelayout::IsVisible( test.position, test.size, test.parentSize, test.type ) ) + { + tet_printf( "Fail. different visibility. Type %d, %s", test.type, location ); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////// + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewDefaultConstructorDestructor, POSITIVE_TC_IDX ); // Calls structs's default constructor and destructors and checks their default values. +TEST_FUNCTION( UtcDaliTextViewCalculateSubLineLayout, POSITIVE_TC_IDX ); // Checks the function which calculates the layout info of the portion of the line which fits on the text-view width. +TEST_FUNCTION( UtcDaliTextViewCalculateAlignmentOffsets, POSITIVE_TC_IDX ); // Checks the horizontal and vertical alignaments (for the whole text). +TEST_FUNCTION( UtcDaliTextViewCalculateJustificationOffsets, POSITIVE_TC_IDX ); // Checks the justification alignment (line per line). +TEST_FUNCTION( UtcDaliTextViewCalculateVisibility, POSITIVE_TC_IDX ); // Checks the text-actor visibility within the text-view with a rectangle intersection test. + +TEST_FUNCTION( UtcDaliTextViewMiscelaneousAsserts, NEGATIVE_TC_IDX ); // Tests some strange asserts. + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliTextViewDefaultConstructorDestructor() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewDefaultConstructorDestructor : "); + + // Test RelayoutParameters defaults. + TextViewRelayout::RelayoutParameters relayoutParameters; + + DALI_TEST_EQUALS( relayoutParameters.mPositionOffset, Vector3::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mLineSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mWordSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mCharacterSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mIndices.mLineIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mIndices.mGroupIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mIndices.mWordIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mIndices.mCharacterIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( relayoutParameters.mCharacterGlobalIndex, 0u, TEST_LOCATION ); + DALI_TEST_CHECK( !relayoutParameters.mIsFirstCharacter ); + DALI_TEST_CHECK( !relayoutParameters.mIsFirstCharacterOfWord ); + DALI_TEST_CHECK( !relayoutParameters.mIsNewLine ); + DALI_TEST_CHECK( !relayoutParameters.mIsNewLineCharacter ); + DALI_TEST_CHECK( !relayoutParameters.mIsWhiteSpace ); + DALI_TEST_CHECK( !relayoutParameters.mIsVisible ); + + // Test FadeParameter defaults + TextViewRelayout::FadeParameters fadeParameters; + + DALI_TEST_EQUALS( fadeParameters.mRightFadeBoundary, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mRightFadeThreshold, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mRightFadeBoundaryOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mRightFadeThresholdOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mRightAlphaCoeficients, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mLeftFadeBoundary, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mLeftFadeThreshold, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mLeftFadeBoundaryOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mLeftFadeThresholdOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mLeftAlphaCoeficients, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mTopFadeBoundary, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mTopFadeThreshold, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mTopFadeBoundaryOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mTopFadeThresholdOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mTopAlphaCoeficients, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mBottomFadeBoundary, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mBottomFadeThreshold, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mBottomFadeBoundaryOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mBottomFadeThresholdOffset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeParameters.mBottomAlphaCoeficients, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_CHECK( !fadeParameters.mIsPartiallyVisible ); + + // Test EllipsizeParameters defaults + TextViewRelayout::EllipsizeParameters ellipsizeParameters; + + DALI_TEST_EQUALS( ellipsizeParameters.mPosition, Vector3::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( ellipsizeParameters.mLineDescender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( ellipsizeParameters.mLineWidth, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( ellipsizeParameters.mEllipsizeBoundary, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( ellipsizeParameters.mFirstIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( ellipsizeParameters.mLastIndex, 0u, TEST_LOCATION ); + DALI_TEST_CHECK( !ellipsizeParameters.mEllipsizeLine ); + DALI_TEST_CHECK( !ellipsizeParameters.mIsLineWidthFullyVisible ); + DALI_TEST_CHECK( !ellipsizeParameters.mIsLineHeightFullyVisible ); + DALI_TEST_CHECK( !ellipsizeParameters.mIsNextLineFullyVisibleHeight ); + DALI_TEST_CHECK( !ellipsizeParameters.mCreateEllipsizedTextActors ); + DALI_TEST_CHECK( !ellipsizeParameters.mLineFits ); + DALI_TEST_CHECK( !ellipsizeParameters.mWordFits ); + + // Test UnderlineInfo defaults + TextViewRelayout::UnderlineInfo underlineInfo; + + DALI_TEST_EQUALS( underlineInfo.mMaxHeight, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( underlineInfo.mMaxThickness, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( underlineInfo.mPosition, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + // Test TextUnderlineStatus defaults + TextViewRelayout::TextUnderlineStatus textUnderlineStatus; + + DALI_TEST_CHECK( textUnderlineStatus.mUnderlineInfo.empty() ); + DALI_TEST_EQUALS( textUnderlineStatus.mCharacterGlobalIndex, 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textUnderlineStatus.mLineGlobalIndex, 0u, TEST_LOCATION ); + DALI_TEST_CHECK( !textUnderlineStatus.mCurrentUnderlineStatus ); + + // Test SubLineLayoutInfo defaults + TextViewRelayout::SubLineLayoutInfo subLineLayoutInfo; + + DALI_TEST_EQUALS( subLineLayoutInfo.mLineLength, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( subLineLayoutInfo.mMaxCharHeight, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( subLineLayoutInfo.mMaxAscender, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliTextViewCalculateSubLineLayout() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCalculateSubLineLayout : "); + + struct CalculateSubLineLayoutTest calculateSubLineLayoutTest[] = + { + //WrapByCharacter + { + "The line is wraped by character. All characters have the same size.", + "Hello world", // input line + 100.f, // parent width + 0, + 0, // indices + 0, + TextViewRelayout::WrapByCharacter, // split policy + 1.f, + // results + 91.041672f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by character. There are characters with different sizes.", + "Hello world", // input line + 100.f, // parent width + 0, + 0, // indices + 0, + TextViewRelayout::WrapByCharacter, // split policy + 1.f, + // results + 94.835075f, // line length. (only fits 8 characters 6x11.38 + 2x13.27) + 13.276911f, // max character height + 11.949220f // max ascender + }, + { + "The line is wraped by character. There are characters with different sizes. It calculates the layout for the second line.", + "Hello world hello world", // input line + 100.f, // parent width + 0, + 2, // indices. The third character of the third word starts in a new line. + 2, + TextViewRelayout::WrapByCharacter, // split policy + 1.f, + // results + 91.041672f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by character. There are characters with different sizes. It calculates the layout for the third line.", + "Hello world hello world", // input line + 100.f, // parent width + 0, + 4, // indices. The fifth character of the fifth word starts in a new line. + 4, + TextViewRelayout::WrapByCharacter, // split policy + 1.f, + // results + 92.938377f, // line length. (only fits 8 characters 8x11.38) + 13.276911f, // max character height + 11.949220f // max ascender + }, + + //WrapByWord + { + "The line is wraped by word. All characters have the same size.", + "Hello world", // input line + 100.f, // parent width + 0, + 0, // indices. It shouldn't use the index character so 9999999 shouldn't make it crash. + 9999999, + TextViewRelayout::WrapByWord, // split policy + 1.f, + // results + 56.901047f, // line length. (only fits 5 characters 5x11.38, white space is not counted) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by word. There are characters with different sizes.", + "Hello world", // input line + 100.f, // parent width + 0, + 0, // indices. + 0, + TextViewRelayout::WrapByWord, // split policy + 1.f, + // results + 58.797747f, // line length. (only fits 5 characters 4x11.38 + 13.276911, white space is not counted) + 13.276911f, // max character height + 11.949220f // max ascender + }, + { + "The line is wraped by word. There are characters with different sizes. It calculates the layout for the second line.", + "Hello world hello world", // input line + 100.f, // parent width + 0, + 2, // indices. The third word starts in a new line. + 0, + TextViewRelayout::WrapByWord, // split policy + 1.f, + // results + 60.694449f, // line length. (only fits 5 characters 2x13.276911 + 3x11.38) + 13.276911f, // max character height + 11.949220f // max ascender + }, + { + "The line is wraped by word. The word doen't fit.", + "Hello world", // input line + 40.f, // parent width + 0, + 0, // indices. The third word starts in a new line. + 0, + TextViewRelayout::WrapByWord, // split policy + 1.f, + // results + 0.f, // line length. (The word doesn't fit) + 11.380209f, // max character height + 10.242188f // max ascender + }, + + //WrapByWordAndSplit + { + "The line is wraped by word and by character. All characters have the same size. There is not a long word.", + "Hello world hello world", // input line + 100.f, // parent width + 0, + 0, // indices. + 0, + TextViewRelayout::WrapByWordAndSplit, // split policy + 1.f, + // results + 56.901047f, // line length. (only fits 5 characters 5x11.38, white space is not counted) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by word and by character. All characters have the same size. There is a long word.", + "Helloooooooo world", // input line + 100.f, // parent width + 0, + 0, // indices. + 0, + TextViewRelayout::WrapByWordAndSplit, // split policy + 1.f, + // results + 91.041672f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by word and by character. There are characters with different sizes. There is a long word. It calculates the layout for the second line.", + "Helloooooooo world", // input line + 100.f, // parent width + 0, + 0, // indices. + 8, + TextViewRelayout::WrapByWordAndSplit, // split policy + 1.f, + // results + 45.520836f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by word and by character. There are characters with different sizes. There is a shrink factor.", + "Helloooooooo world", // input line + 100.f, // parent width + 0, + 0, // indices. + 8, + TextViewRelayout::WrapByWordAndSplit, // split policy + 0.7f, + // results + 95.593755f, // line length. (only fits 12 characters 8x11.38) + 7.9661463f, // max character height + 7.169531f // max ascender + }, + + //WrapByLineAndSplit + { + "The line is wraped by end of line and by character. All characters have the same size.", + "Hello world", // input line + 100.f, // parent width + 0, + 0, // indices + 0, + TextViewRelayout::WrapByLineAndSplit, // split policy + 1.f, + // results + 91.041672f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line fits in the width.", + "Hello", // input line + 100.f, // parent width + 0, + 0, // indices + 0, + TextViewRelayout::WrapByLineAndSplit, // split policy + 1.f, + // results + 56.901047f, // line length. (only fits 5 characters 5x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + { + "The line is wraped by end of line and by character. All characters have the same size. It calculates the layout for the second line.", + "Hello world, hello world", // input line + 100.f, // parent width + 0, + 2, // indices + 2, + TextViewRelayout::WrapByLineAndSplit, // split policy + 1.f, + // results + 91.041672f, // line length. (only fits 8 characters 8x11.38) + 11.380209f, // max character height + 10.242188f // max ascender + }, + }; + const std::size_t numberOfTests( 15 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const CalculateSubLineLayoutTest& test = calculateSubLineLayoutTest[index]; + + if( !TestCalculateSubLineLayout( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewCalculateAlignmentOffsets() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCalculateAlignmentOffsets : "); + + struct AlignmentOffsetTest alignmentOffsetTest[] = + { + { + Toolkit::Alignment::HorizontalLeft, + 100.f, + 75.f, + 0.f + }, + { + Toolkit::Alignment::HorizontalCenter, + 100.f, + 75.f, + 12.5f + }, + { + Toolkit::Alignment::HorizontalRight, + 100.f, + 75.f, + 25.f + }, + { + Toolkit::Alignment::VerticalTop, + 100.f, + 75.f, + 0.f + }, + { + Toolkit::Alignment::VerticalCenter, + 100.f, + 75.f, + 12.5f + }, + { + Toolkit::Alignment::VerticalBottom, + 100.f, + 75.f, + 25.f + } + }; + const std::size_t numberOfTests( 6 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const AlignmentOffsetTest& test = alignmentOffsetTest[index]; + + if( !TestAlignmentOffset( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewCalculateJustificationOffsets() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCalculateJustificationOffsets : "); + + struct JustificationOffsetTest justificationOffsetTest[] = + { + { + Toolkit::TextView::Left, + 100.f, + 75.f, + 0.f + }, + { + Toolkit::TextView::Justified, + 100.f, + 75.f, + 0.f + }, + { + Toolkit::TextView::Center, + 100.f, + 150.f, + -25.f + }, + { + Toolkit::TextView::Right, + 100.f, + 75.f, + 25.f + }, + }; + const std::size_t numberOfTests( 4 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const JustificationOffsetTest& test = justificationOffsetTest[index]; + + if( !TestJustificationOffset( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + + +static void UtcDaliTextViewCalculateVisibility() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCalculateVisibility : "); + + struct CalculateVisibilityTest calculateVisibilityTest[] = + { + { + Vector3( 0.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE, + true + }, + { + Vector3( 10.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE, + true + }, + { + Vector3( 0.f, 10.f, 0.f ), + Size( 150.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE, + false + }, + { + Vector3( 0.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE_WIDTH, + true + }, + { + Vector3( 95.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE_WIDTH, + false + }, + { + Vector3( 0.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE_HEIGHT, + true + }, + { + Vector3( 0.f, 0.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::FULLY_VISIBLE_HEIGHT, + false + }, + { + Vector3( -10.f, 10.f, 0.f ), + Size( 150.f, 150.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE, + true + }, + { + Vector3( -100.f, -100.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE, + false + }, + { + Vector3( -10.f, 10.f, 0.f ), + Size( 50.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE_WIDTH, + true + }, + { + Vector3( 110.f, 10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE_WIDTH, + false + }, + { + Vector3( 0.f, 20.f, 0.f ), + Size( 10.f, 50.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE_HEIGHT, + true + }, + { + Vector3( 0.f, -10.f, 0.f ), + Size( 10.f, 10.f ), + Size( 100.f, 100.f ), + TextViewRelayout::PARTIALLY_VISIBLE_HEIGHT, + false + }, + }; + const std::size_t numberOfTests( 13 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const CalculateVisibilityTest& test = calculateVisibilityTest[index]; + + if( !TestCalculateVisibility( test, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewMiscelaneousAsserts() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMiscelaneousAsserts : "); + + float offset = 0.f; + + bool assert1 = false; + bool assert2 = false; + try + { + offset = Toolkit::Internal::TextViewRelayout::CalculateXoffset( Toolkit::Alignment::VerticalTop, 100.f, 50.f ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewRelayout::CalculateXoffset: Wrong horizontal text alignment. Did you set a vertical one?\"", TEST_LOCATION ); + assert1 = true; + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + DALI_TEST_EQUALS( offset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + try + { + offset = Toolkit::Internal::TextViewRelayout::CalculateYoffset( Toolkit::Alignment::HorizontalRight, 100.f, 50.f ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewRelayout::CalculateXoffset: Wrong vertical text alignment. Did you set an horizontal one?\"", TEST_LOCATION ); + assert2 = true; + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + DALI_TEST_EQUALS( offset, 0.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + DALI_TEST_CHECK( assert1 && assert2 ); + +} diff --git a/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView.cpp b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView.cpp new file mode 100644 index 0000000..6312da8 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/text-view/utc-Dali-TextView.cpp @@ -0,0 +1,2917 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +namespace +{ + +const Toolkit::Internal::TextView::LayoutParameters DEFAULT_LAYOUT_PARAMETERS; +const Toolkit::Internal::TextView::VisualParameters DEFAULT_VISUAL_PARAMETERS; + +// Data structures used to create an 'experiment' in TET cases + +struct SplitWordTest +{ + std::string description; + std::string input; + std::size_t position; + std::string firstResult; + std::string lastResult; +}; + +struct SplitWordGroupTest +{ + std::string description; + std::string input; + std::size_t wordPosition; + std::size_t position; + std::string firstResult; + std::string lastResult; +}; + +struct SplitLineTest +{ + std::string description; + std::string input; + std::size_t groupPosition; + std::size_t wordPosition; + std::size_t position; + float lineHeightOffset; + std::string firstResult; + std::string lastResult; +}; + +struct MergeWordsTest +{ + std::string description; + std::string inputFirst; + std::string inputLast; + std::string result; +}; + +struct MergeWordGroupsTest +{ + std::string description; + std::string inputFirst; + std::string inputLast; + std::string result; +}; + +struct MergeLinesTest +{ + std::string description; + std::string inputFirst; + std::string inputLast; + float lineHeightOffset; + std::string result; +}; + +struct RemoveCharactersFromWordTest +{ + std::string description; + std::string input; + std::size_t position; + std::size_t numberOfCharacters; + std::string result; +}; + +struct RemoveWordsFromGroupTest +{ + std::string description; + std::string input; + std::size_t wordIndex; + std::size_t numberOfWords; + std::string result; +}; + +struct RemoveGroupsFromLineTest +{ + std::string description; + std::string input; + std::size_t groupIndex; + std::size_t numberOfGroups; + float lineHeightOffset; + std::string result; +}; + +enum UpdateTextInfoOperation +{ + Insert, + Remove, + Replace +}; + +struct UpdateTextInfoTest +{ + std::string description; + UpdateTextInfoOperation operation; + std::string input; + std::size_t position; + std::size_t numberOfCharacters; + std::string inputText; + float lineHeightOffset; + std::string result; +}; + +// Useful Print functions when something goes wrong. + +void Print( const TextViewProcessor::CharacterLayoutInfo& character ) +{ + std::cout << " height : " << character.mHeight << std::endl; + std::cout << " advance : " << character.mAdvance << std::endl; + std::cout << " bearing : " << character.mBearing << std::endl; + std::cout << " mPosition : " << character.mPosition << std::endl; + std::cout << " mSize : " << character.mSize << std::endl; + std::cout << " mAscender : " << character.mAscender << std::endl; + + if( character.mTextActor ) + { + std::cout << "[" << character.mTextActor.GetText() << "]"; + } + else + { + std::cout << "{" << character.mStyledText.mText.GetText() << "}"; + } +} + +void Print( const TextViewProcessor::WordLayoutInfo& word ) +{ + std::cout << "["; + std::cout << " mSize : " << word.mSize << std::endl; + std::cout << " mAscender : " << word.mAscender << std::endl; + std::cout << " mType : " << word.mType << std::endl; + std::cout << "mNumberOfCharacters : " << word.mCharactersLayoutInfo.size() << std::endl; + std::cout << "["; + for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator it = word.mCharactersLayoutInfo.begin(), endIt = word.mCharactersLayoutInfo.end(); it != endIt; ++it ) + { + Print( *it ); + } + std::cout << "]"; std::cout << std::endl; + std::cout << "]"; std::cout << std::endl; +} + +void Print( const TextViewProcessor::WordGroupLayoutInfo& wordGroup ) +{ + std::cout << "("; + std::cout << " mSize : " << wordGroup.mSize << std::endl; + std::cout << " mAscender : " << wordGroup.mAscender << std::endl; + std::cout << " mDirection : " << wordGroup.mDirection << std::endl; + std::cout << "mNumberOfCharacters : " << wordGroup.mNumberOfCharacters << std::endl; + for( TextViewProcessor::WordLayoutInfoContainer::const_iterator it = wordGroup.mWordsLayoutInfo.begin(), endIt = wordGroup.mWordsLayoutInfo.end(); it != endIt; ++it ) + { + Print( *it ); + } + std::cout << ")"; std::cout << std::endl; +} + +void Print( const TextViewProcessor::LineLayoutInfo& line ) +{ + std::cout << "<"; + std::cout << " mSize : " << line.mSize << std::endl; + std::cout << " mAscender : " << line.mAscender << std::endl; + std::cout << "mNumberOfCharacters : " << line.mNumberOfCharacters << std::endl; + for( TextViewProcessor::WordGroupLayoutInfoContainer::const_iterator it = line.mWordGroupsLayoutInfo.begin(), endIt = line.mWordGroupsLayoutInfo.end(); it != endIt; ++it ) + { + Print( *it ); + } + std::cout << ">" << std::endl; +} + +void Print( const TextViewProcessor::TextLayoutInfo& text ) +{ + std::cout << "||"; + for( TextViewProcessor::LineLayoutInfoContainer::const_iterator it = text.mLinesLayoutInfo.begin(), endIt = text.mLinesLayoutInfo.end(); it != endIt; ++it ) + { + Print( *it ); + } + std::cout << "||" << std::endl; +} + +void Print( const TextStyle& style ) +{ + std::cout << " font name : " << style.GetFontName() << std::endl; + std::cout << " : " << style.GetFontStyle() << std::endl; + std::cout << " : " << style.GetFontPointSize() << std::endl; + std::cout << " : " << style.GetWeight() << std::endl; + std::cout << " : " << style.GetTextColor() << std::endl; + std::cout << " : " << style.GetItalics() << std::endl; + std::cout << " : " << style.GetUnderline() << std::endl; + std::cout << " : " << style.GetShadow() << std::endl; + std::cout << " : " << style.GetShadowColor() << std::endl; + std::cout << " : " << style.GetShadowOffset() << std::endl; + std::cout << " : " << style.GetGlow() << std::endl; + std::cout << " : " << style.GetGlowColor() << std::endl; + std::cout << " : " << style.GetGlowIntensity() << std::endl; + std::cout << " : " << style.GetSmoothEdge() << std::endl; + std::cout << " : " << style.GetOutline() << std::endl; + std::cout << " : " << style.GetOutlineThickness() << std::endl; +} + +// Test functions used to check if two data structures are equal. + +bool TestEqual( float x, float y ) +{ + return ( fabsf( x - y ) < Math::MACHINE_EPSILON_1000 ); +} + +bool TestEqual( const TextViewProcessor::CharacterLayoutInfo& character1, + const TextViewProcessor::CharacterLayoutInfo& character2 ) +{ + if( !TestEqual( character1.mHeight, character2.mHeight ) ) + { + return false; + } + if( !TestEqual( character1.mAdvance, character2.mAdvance ) ) + { + return false; + } + if( !TestEqual( character1.mBearing, character2.mBearing ) ) + { + return false; + } + + if( !TestEqual( character1.mPosition.x, character2.mPosition.x ) ) + { + return false; + } + if( !TestEqual( character1.mPosition.y, character2.mPosition.y ) ) + { + return false; + } + + if( !TestEqual( character1.mSize.x, character2.mSize.x ) ) + { + return false; + } + if( !TestEqual( character1.mSize.y, character2.mSize.y ) ) + { + return false; + } + + if( !TestEqual( character1.mAscender, character2.mAscender ) ) + { + return false; + } + + if( character1.mTextActor && !character2.mTextActor ) + { + return false; + } + + if( !character1.mTextActor && character2.mTextActor ) + { + return false; + } + + std::string text1; + std::string text2; + TextStyle style1; + TextStyle style2; + + if( character1.mTextActor ) + { + text1 = character1.mTextActor.GetText(); + style1 = character1.mTextActor.GetTextStyle(); + + text2 = character2.mTextActor.GetText(); + style2 = character2.mTextActor.GetTextStyle(); + } + + if( text1 != text2 ) + { + return false; + } + + if( style1 != style2 ) + { + return false; + } + + text1 = character1.mStyledText.mText.GetText(); + style1 = character1.mStyledText.mStyle; + + text2 = character2.mStyledText.mText.GetText(); + style2 = character2.mStyledText.mStyle; + + if( text1 != text2 ) + { + return false; + } + + if( style1 != style2 ) + { + return false; + } + + return true; +} + +bool TestEqual( const TextViewProcessor::WordLayoutInfo& word1, + const TextViewProcessor::WordLayoutInfo& word2 ) +{ + if( !TestEqual( word1.mSize.x, word2.mSize.x ) ) + { + return false; + } + if( !TestEqual( word1.mSize.y, word2.mSize.y ) ) + { + return false; + } + + if( !TestEqual( word1.mAscender, word2.mAscender ) ) + { + return false; + } + + if( word1.mType != word2.mType ) + { + return false; + } + + if( word1.mCharactersLayoutInfo.size() != word2.mCharactersLayoutInfo.size() ) + { + return false; + } + + for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator it1 = word1.mCharactersLayoutInfo.begin(), endIt1 = word1.mCharactersLayoutInfo.end(), + it2 = word2.mCharactersLayoutInfo.begin(), endIt2 = word2.mCharactersLayoutInfo.end(); + ( it1 != endIt1 ) && ( it2 != endIt2 ); + ++it1, ++it2 ) + { + if( !TestEqual( *it1, *it2 ) ) + { + return false; + } + } + + return true; +} + +bool TestEqual( const TextViewProcessor::WordGroupLayoutInfo& group1, + const TextViewProcessor::WordGroupLayoutInfo& group2 ) +{ + + if( group1.mNumberOfCharacters != group2.mNumberOfCharacters ) + { + return false; + } + + if( group1.mWordsLayoutInfo.size() != group2.mWordsLayoutInfo.size() ) + { + return false; + } + + if( !TestEqual( group1.mSize.x, group2.mSize.x ) ) + { + return false; + } + if( !TestEqual( group1.mSize.y, group2.mSize.y ) ) + { + return false; + } + + if( !TestEqual( group1.mAscender, group2.mAscender ) ) + { + return false; + } + + if( group1.mDirection != group2.mDirection ) + { + return false; + } + + for( TextViewProcessor::WordLayoutInfoContainer::const_iterator it1 = group1.mWordsLayoutInfo.begin(), endIt1 = group1.mWordsLayoutInfo.end(), + it2 = group2.mWordsLayoutInfo.begin(), endIt2 = group2.mWordsLayoutInfo.end(); + ( it1 != endIt1 ) && ( it2 != endIt2 ); + ++it1, ++it2 ) + { + if( !TestEqual( *it1, *it2 ) ) + { + return false; + } + } + + return true; +} + +bool TestEqual( const TextViewProcessor::LineLayoutInfo& line1, + const TextViewProcessor::LineLayoutInfo& line2 ) +{ + if( !TestEqual( line1.mSize.x, line2.mSize.x ) ) + { + return false; + } + if( !TestEqual( line1.mSize.y, line2.mSize.y ) ) + { + return false; + } + + if( !TestEqual( line1.mAscender, line2.mAscender ) ) + { + return false; + } + + if( line1.mNumberOfCharacters != line2.mNumberOfCharacters ) + { + return false; + } + + if( line1.mWordGroupsLayoutInfo.size() != line2.mWordGroupsLayoutInfo.size() ) + { + return false; + } + + for( TextViewProcessor::WordGroupLayoutInfoContainer::const_iterator it1 = line1.mWordGroupsLayoutInfo.begin(), endIt1 = line1.mWordGroupsLayoutInfo.end(), + it2 = line2.mWordGroupsLayoutInfo.begin(), endIt2 = line2.mWordGroupsLayoutInfo.end(); + ( it1 != endIt1 ) && ( it2 != endIt2 ); + ++it1, ++it2 ) + { + if( !TestEqual( *it1, *it2 ) ) + { + return false; + } + } + + return true; +} + +bool TestEqual( const TextViewProcessor::TextLayoutInfo& text1, + const TextViewProcessor::TextLayoutInfo& text2 ) +{ + if( !TestEqual( text1.mWholeTextSize.x, text2.mWholeTextSize.x ) ) + { + return false; + } + if( !TestEqual( text1.mWholeTextSize.y, text2.mWholeTextSize.y ) ) + { + return false; + } + + if( !TestEqual( text1.mMaxWordWidth, text2.mMaxWordWidth ) ) + { + return false; + } + + if( text1.mNumberOfCharacters != text2.mNumberOfCharacters ) + { + return false; + } + + if( text1.mLinesLayoutInfo.size() != text2.mLinesLayoutInfo.size() ) + { + return false; + } + + for( TextViewProcessor::LineLayoutInfoContainer::const_iterator it1 = text1.mLinesLayoutInfo.begin(), endIt1 = text1.mLinesLayoutInfo.end(), + it2 = text2.mLinesLayoutInfo.begin(), endIt2 = text2.mLinesLayoutInfo.end(); + ( it1 != endIt1 ) && ( it2 != endIt2 ); + ++it1, ++it2 ) + { + if( !TestEqual( *it1, *it2 ) ) + { + return false; + } + } + + return true; +} + +/** + * Splits the \e input word in two by the given \e position and checks the results with \e firstResult and \e lastResult. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. i.e. "Split the word from the beginning. (position 0)". + * @param input The input word. + * @param position Where to split the word. + * @param firstResult First part of the split word. + * @param lastResult Last part of the split word. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestSplitWord( const std::string& description, const std::string& input, const size_t position, const std::string& firstResult, const std::string& lastResult, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input word. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + // Get the input word + TextViewProcessor::WordLayoutInfo inputWordLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + inputWordLayout = *( *( *inputLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Create layout info for the first part of the result (after split the word) + + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& firstResultLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray firstResultStyledText; + MarkupProcessor::GetStyledTextArray( firstResult, firstResultStyledText ); + + TextViewProcessor::CreateTextInfo( firstResultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + firstRelayoutData ); + + // Get the first result word + TextViewProcessor::WordLayoutInfo firstResultWordLayout; + + if( !firstResultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *firstResultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + firstResultWordLayout = *( *( *firstResultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Create layout info for the last part of the result (after split the word) + + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& lastResultLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray lastResultStyledText; + MarkupProcessor::GetStyledTextArray( lastResult, lastResultStyledText ); + + TextViewProcessor::CreateTextInfo( lastResultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + lastRelayoutData ); + + // Get the last result word + TextViewProcessor::WordLayoutInfo lastResultWordLayout; + + if( !lastResultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *lastResultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + lastResultWordLayout = *( *( *lastResultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Split the word. + + TextViewProcessor::WordLayoutInfo lastWordLayoutInfo; + + SplitWord( position, + inputWordLayout, + lastWordLayoutInfo ); + + // Test results + if( !TestEqual( inputWordLayout, firstResultWordLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + if( !TestEqual( lastWordLayoutInfo, lastResultWordLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Splits the \e input group of words in two by the given \e wordPosition and \e position and checks the results with \e firstResult and \e lastResult. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. i.e. "Split the group of words from the beginning. (wordPosition 0 and position 0)". + * @param input The input word. + * @param wordPosition Index to the word within the group where to split the group. + * @param position Where to split the word. + * @param firstResult First part of the split group of words. + * @param lastResult Last part of the split group of words. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestSplitWordGroup( const std::string& description, + const std::string& input, + const size_t wordPosition, + const size_t position, + const std::string& firstResult, + const std::string& lastResult, + const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input group of words. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + // Get the input group of words + TextViewProcessor::WordGroupLayoutInfo inputWordGroupLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + inputWordGroupLayout = *( *inputLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Create layout info for the first part of the result (after split the group of words) + + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& firstResultLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray firstResultStyledText; + MarkupProcessor::GetStyledTextArray( firstResult, firstResultStyledText ); + + TextViewProcessor::CreateTextInfo( firstResultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + firstRelayoutData ); + + // Get the first result group of words + TextViewProcessor::WordGroupLayoutInfo firstResultWordGroupLayout; + + if( !firstResultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *firstResultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + firstResultWordGroupLayout = *( *firstResultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Create layout info for the last part of the result (after split the group of words) + + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& lastResultLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray lastResultStyledText; + MarkupProcessor::GetStyledTextArray( lastResult, lastResultStyledText ); + + TextViewProcessor::CreateTextInfo( lastResultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + lastRelayoutData ); + + // Get the last result group of words + TextViewProcessor::WordGroupLayoutInfo lastResultWordGroupLayout; + + if( !lastResultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *lastResultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + lastResultWordGroupLayout = *( *lastResultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Split the group of words. + + TextViewProcessor::WordGroupLayoutInfo lastWordGroupLayoutInfo; + + TextViewProcessor::TextInfoIndices indices( 0, 0, wordPosition, position ); + SplitWordGroup( indices, + inputWordGroupLayout, + lastWordGroupLayoutInfo ); + + // Test results + if( !TestEqual( inputWordGroupLayout, firstResultWordGroupLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + if( !TestEqual( lastWordGroupLayoutInfo, lastResultWordGroupLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Splits the \e input line in two by the given \e groupPosition, \e wordPosition and \e position and checks the results with \e firstResult and \e lastResult. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. i.e. "Split the line from the beginning. (groupPosition 0, wordPosition 0 and position 0)". + * @param input The input word. + * @param groupPosition Index to the group of words within the line where to split the line. + * @param wordPosition Index to the word within the group where to split the group. + * @param position Where to split the word. + * @param lineHeightOffset Offset between lines. + * @param firstResult First part of the split line. + * @param lastResult Last part of the split line. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestSplitLine( const std::string& description, + const std::string& input, + const size_t groupPosition, + const size_t wordPosition, + const size_t position, + const float lineHeightOffset, + const std::string& firstResult, + const std::string& lastResult, + const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input line. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + relayoutData ); + + // Get the input line + TextViewProcessor::LineLayoutInfo inputLineLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + inputLineLayout = *inputLayout.mLinesLayoutInfo.begin(); + } + + // Create layout info for the first part of the result (after split the line) + + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& firstResultLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray firstResultStyledText; + MarkupProcessor::GetStyledTextArray( firstResult, firstResultStyledText ); + + TextViewProcessor::CreateTextInfo( firstResultStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + firstRelayoutData ); + + // Get the first result line + TextViewProcessor::LineLayoutInfo firstResultLineLayout; + + if( !firstResultLayout.mLinesLayoutInfo.empty() ) + { + firstResultLineLayout = *firstResultLayout.mLinesLayoutInfo.begin(); + } + + // Create layout info for the last part of the result (after split the line) + + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& lastResultLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray lastResultStyledText; + MarkupProcessor::GetStyledTextArray( lastResult, lastResultStyledText ); + + TextViewProcessor::CreateTextInfo( lastResultStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + lastRelayoutData ); + + // Get the last result line + TextViewProcessor::LineLayoutInfo lastResultLineLayout; + + if( !lastResultLayout.mLinesLayoutInfo.empty() ) + { + lastResultLineLayout = *lastResultLayout.mLinesLayoutInfo.begin(); + } + + // Split the line. + + TextViewProcessor::LineLayoutInfo lastLineLayoutInfo; + + TextViewProcessor::TextInfoIndices indices( 0, groupPosition, wordPosition, position ); + SplitLine( indices, + PointSize( lineHeightOffset ), + inputLineLayout, + lastLineLayoutInfo ); + + // Test results + if( !TestEqual( inputLineLayout, firstResultLineLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + if( !TestEqual( lastLineLayoutInfo, lastResultLineLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Merges the \e inputFirst word and the \e inputLast word, and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. i.e. "Merge two words with same style". + * @param inputFirst The first part of the word. + * @param inputLast The last part of the word. + * @param result The merged word. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestMergeWords( const std::string& description, const std::string& inputFirst, const std::string& inputLast, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the inputFirst word. + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& inputFirstLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputFirstStyledText; + MarkupProcessor::GetStyledTextArray( inputFirst, inputFirstStyledText ); + + TextViewProcessor::CreateTextInfo( inputFirstStyledText, + DEFAULT_LAYOUT_PARAMETERS, + firstRelayoutData ); + + // Get the input word + TextViewProcessor::WordLayoutInfo inputFirstWordLayout; + + if( !inputFirstLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputFirstLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + inputFirstWordLayout = *( *( *inputFirstLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Create layout info for the inputLast word. + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& inputLastLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputLastStyledText; + MarkupProcessor::GetStyledTextArray( inputLast, inputLastStyledText ); + + TextViewProcessor::CreateTextInfo( inputLastStyledText, + DEFAULT_LAYOUT_PARAMETERS, + lastRelayoutData ); + + // Get the input word + TextViewProcessor::WordLayoutInfo inputLastWordLayout; + + if( !inputLastLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLastLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + inputLastWordLayout = *( *( *inputLastLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Create layout info for the result word. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + resultRelayoutData ); + + // Get the result word + TextViewProcessor::WordLayoutInfo resultWordLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *resultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + resultWordLayout = *( *( *resultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + MergeWord( inputFirstWordLayout, + inputLastWordLayout ); + + if( !TestEqual( inputFirstWordLayout, resultWordLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Merges the \e inputFirst group of words and the \e inputLast group of words, and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param inputFirst The first part of the group of words. + * @param inputLast The last part of the group of words. + * @param result The merged group of word. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestMergeGroupsOfWords( const std::string& description, const std::string& inputFirst, const std::string& inputLast, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the inputFirst group of word. + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& inputFirstLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputFirstStyledText; + MarkupProcessor::GetStyledTextArray( inputFirst, inputFirstStyledText ); + + TextViewProcessor::CreateTextInfo( inputFirstStyledText, + DEFAULT_LAYOUT_PARAMETERS, + firstRelayoutData ); + + // Get the input group of words. + TextViewProcessor::WordGroupLayoutInfo inputFirstWordGroupLayout; + + if( !inputFirstLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputFirstLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + inputFirstWordGroupLayout = *( *inputFirstLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Create layout info for the inputLast group of words. + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& inputLastLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputLastStyledText; + MarkupProcessor::GetStyledTextArray( inputLast, inputLastStyledText ); + + TextViewProcessor::CreateTextInfo( inputLastStyledText, + DEFAULT_LAYOUT_PARAMETERS, + lastRelayoutData ); + + // Get the input group of words + TextViewProcessor::WordGroupLayoutInfo inputLastWordGroupLayout; + + if( !inputLastLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLastLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + inputLastWordGroupLayout = *( *inputLastLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Create layout info for the result group of words. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + resultRelayoutData ); + + // Get the result word + TextViewProcessor::WordGroupLayoutInfo resultWordGroupLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *resultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + resultWordGroupLayout = *( *resultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + MergeWordGroup( inputFirstWordGroupLayout, + inputLastWordGroupLayout ); + + if( !TestEqual( inputFirstWordGroupLayout, resultWordGroupLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Merges the \e inputFirst line and the \e inputLast line, and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param inputFirst The first part of the line. + * @param inputLast The last part of the line. + * @param lineHeightOffset Offset between lines. + * @param result The merged line. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestMergeLines( const std::string& description, const std::string& inputFirst, const std::string& inputLast, const float lineHeightOffset, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the inputFirst line. + Toolkit::Internal::TextView::RelayoutData firstRelayoutData; + TextViewProcessor::TextLayoutInfo& inputFirstLayout( firstRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputFirstStyledText; + MarkupProcessor::GetStyledTextArray( inputFirst, inputFirstStyledText ); + + TextViewProcessor::CreateTextInfo( inputFirstStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + firstRelayoutData ); + + // Get the input word + TextViewProcessor::LineLayoutInfo inputFirstLineLayout; + + if( !inputFirstLayout.mLinesLayoutInfo.empty() ) + { + inputFirstLineLayout = *inputFirstLayout.mLinesLayoutInfo.begin(); + } + + // Create layout info for the inputLast line. + Toolkit::Internal::TextView::RelayoutData lastRelayoutData; + TextViewProcessor::TextLayoutInfo& inputLastLayout( lastRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputLastStyledText; + MarkupProcessor::GetStyledTextArray( inputLast, inputLastStyledText ); + + TextViewProcessor::CreateTextInfo( inputLastStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + lastRelayoutData ); + + // Get the input word + TextViewProcessor::LineLayoutInfo inputLastLineLayout; + + if( !inputLastLayout.mLinesLayoutInfo.empty() ) + { + inputLastLineLayout = *inputLastLayout.mLinesLayoutInfo.begin(); + } + + // Create layout info for the result word. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + resultRelayoutData ); + + // Get the result word + TextViewProcessor::LineLayoutInfo resultLineLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + resultLineLayout = *resultLayout.mLinesLayoutInfo.begin(); + } + + MergeLine( inputFirstLineLayout, + inputLastLineLayout ); + + if( !TestEqual( inputFirstLineLayout, resultLineLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Removes from the \e input word the \e numberOfCharacters characters starting from the given \e position and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. i.e. "Remove a whole group of characters. Merge". + * @param input The input word. + * @param position Where to start to remove characters + * @param numberOfCharacters The number of characters to remove. + * @param result The word without the removed characters. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestRemoveCharactersFromWord( const std::string& description, const std::string& input, const std::size_t position, const std::size_t numberOfCharacters, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input word. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + // Get the input word + TextViewProcessor::WordLayoutInfo inputWordLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + inputWordLayout = *( *( *inputLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + // Create layout info for the result word. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + resultRelayoutData ); + + // Get the result word + TextViewProcessor::WordLayoutInfo resultWordLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *resultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& group( *line.mWordGroupsLayoutInfo.begin() ); + if( !group.mWordsLayoutInfo.empty() ) + { + resultWordLayout = *( *( *resultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + } + } + } + + RemoveCharactersFromWord( position, + numberOfCharacters, + inputWordLayout ); + + if( !TestEqual( inputWordLayout, resultWordLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Removes from the \e input group of words the \e numberOfWords words starting from the given \e wordIndex and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param input The input group of words. + * @param wordIndex Where to start to remove words. + * @param numberOfWords The number of words to remove. + * @param result The group of words without the removed words. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestRemoveWordsFromGroup( const std::string& description, const std::string& input, const std::size_t wordIndex, const std::size_t numberOfWords, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input group of words. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + // Get the input group of words + TextViewProcessor::WordGroupLayoutInfo inputWordGroupLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *inputLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + inputWordGroupLayout = *( *inputLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + // Create layout info for the result group of words. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + DEFAULT_LAYOUT_PARAMETERS, + resultRelayoutData ); + + // Get the result group of words. + TextViewProcessor::WordGroupLayoutInfo resultWordGroupLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& line( *resultLayout.mLinesLayoutInfo.begin() ); + if( !line.mWordGroupsLayoutInfo.empty() ) + { + resultWordGroupLayout = *( *resultLayout.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + } + } + + RemoveWordsFromWordGroup( wordIndex, + numberOfWords, + inputWordGroupLayout ); + + if( !TestEqual( inputWordGroupLayout, resultWordGroupLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + + +/** + * Removes from the \e input line the \e numberOfGroups groups of words starting from the given \e groupIndex and checks the results with \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param input The input line. + * @param groupIndex Where to start to remove groups of words + * @param numberOfGroups The number of groups of words to remove. + * @param lineHeightOffset Offset between lines. + * @param result The line without the removed groups of words. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestRemoveGroupsFromLine( const std::string& description, const std::string& input, const std::size_t groupIndex, const std::size_t numberOfGroups, const float lineHeightOffset, const std::string& result, const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input line. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + relayoutData ); + + // Get the input line + TextViewProcessor::LineLayoutInfo inputLineLayout; + + if( !inputLayout.mLinesLayoutInfo.empty() ) + { + inputLineLayout = *inputLayout.mLinesLayoutInfo.begin(); + } + + // Create layout info for the result line. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + resultRelayoutData ); + + // Get the result line + TextViewProcessor::LineLayoutInfo resultLineLayout; + + if( !resultLayout.mLinesLayoutInfo.empty() ) + { + resultLineLayout = *resultLayout.mLinesLayoutInfo.begin(); + } + + RemoveWordGroupsFromLine( groupIndex, + numberOfGroups, + PointSize( lineHeightOffset ), + inputLineLayout ); + + if( !TestEqual( inputLineLayout, resultLineLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + return false; + } + + return true; +} + +/** + * Tests inserts, removes and updates operation in the given \e input text and checks with the given \e result. + * + * If the test fails it prints a short description and the line where this function was called. + * + * @param description Short description of the experiment. + * @param operation Type of update operation (insert, remove, replace) + * @param input The input text. + * @param position Where to insert, remove or replace text. + * @param numberOfCharacters Number of characters to remove or replace. + * @param inputText Inserted or updated text. + * @param lineHeightOffset Offset between lines. + * @param result Expected result. + * @param location Where this function has been called. + * + * @return \e true if the experiment is successful. Otherwise returns \e false. + */ +bool TestUpdateTextInfo( const std::string& description, + const UpdateTextInfoOperation operation, + const std::string& input, + const std::size_t position, + const std::size_t numberOfCharacters, + const std::string& inputText, + const float lineHeightOffset, + const std::string& result, + const char* location ) +{ + tet_printf( "%s", description.c_str() ); + + // Create layout info for the input. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& inputLayout( relayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( input, inputStyledText ); + + TextViewProcessor::CreateTextInfo( inputStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + relayoutData ); + + // Create layout info for the result. + Toolkit::Internal::TextView::RelayoutData resultRelayoutData; + TextViewProcessor::TextLayoutInfo& resultLayout( resultRelayoutData.mTextLayoutInfo ); + + MarkupProcessor::StyledTextArray resultStyledText; + MarkupProcessor::GetStyledTextArray( result, resultStyledText ); + + TextViewProcessor::CreateTextInfo( resultStyledText, + Toolkit::Internal::TextView::LayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ), + resultRelayoutData ); + + // Choose operation and call appropiate UpdateTextInfo() method. + const Toolkit::Internal::TextView::LayoutParameters layoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Center, + PointSize( lineHeightOffset ), + std::string( "..." ) ); + + switch( operation ) + { + case Insert: + { + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( inputText, inputStyledText ); + + TextViewProcessor::UpdateTextInfo( position, + inputStyledText, + layoutParameters, + relayoutData ); + break; + } + case Remove: + { + TextViewProcessor::UpdateTextInfo( position, + numberOfCharacters, + layoutParameters, + relayoutData, + TextViewProcessor::CLEAR_TEXT ); + break; + } + case Replace: + { + MarkupProcessor::StyledTextArray inputStyledText; + MarkupProcessor::GetStyledTextArray( inputText, inputStyledText ); + + TextViewProcessor::UpdateTextInfo( position, + numberOfCharacters, + inputStyledText, + layoutParameters, + relayoutData ); + break; + } + default: + { + tet_printf( "TestUpdateTextInfo: unknown update operation. %s", location ); + return false; + } + } + + if( !TestEqual( inputLayout, resultLayout ) ) + { + tet_printf( "Fail. different layout info. %s", location ); + + std::cout << " result : "; Print( inputLayout ); + std::cout << " expected result : "; Print( resultLayout ); + return false; + } + + return true; +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewCreateTextInfo, POSITIVE_TC_IDX ); // Tests data structures are built well. +TEST_FUNCTION( UtcDaliTextViewUpdateTextInfo, POSITIVE_TC_IDX ); // Tests update operations within a whole text (insert, remove, replace). +TEST_FUNCTION( UtcDaliTextViewSplitWord, POSITIVE_TC_IDX ); // Tests the split word operation. +TEST_FUNCTION( UtcDaliTextViewSplitWordGroup, POSITIVE_TC_IDX ); // Tests the split group of words operation. +TEST_FUNCTION( UtcDaliTextViewSplitLine, POSITIVE_TC_IDX ); // Tests the split line operation. +TEST_FUNCTION( UtcDaliTextViewMergeWord01, POSITIVE_TC_IDX ); // Tests the merge word operation. +TEST_FUNCTION( UtcDaliTextViewMergeWord02, NEGATIVE_TC_IDX ); // Tests invalid inputs in the merge word operation. +TEST_FUNCTION( UtcDaliTextViewMergeGroup01, POSITIVE_TC_IDX ); // Tests the merge group of words operation. +TEST_FUNCTION( UtcDaliTextViewMergeGroup02, NEGATIVE_TC_IDX ); // Tests invalid inputs in the merge group of words operation. +TEST_FUNCTION( UtcDaliTextViewMergeLine01, POSITIVE_TC_IDX ); // Tests the merge line operation. +TEST_FUNCTION( UtcDaliTextViewMergeLine02, NEGATIVE_TC_IDX ); // Tests invalid inputs in the merge line operation. +TEST_FUNCTION( UtcDaliTextViewRemoveCharactersFromWord, POSITIVE_TC_IDX ); // Tests the remove characters from a word operation. +TEST_FUNCTION( UtcDaliTextViewRemoveWordsFromGroup, POSITIVE_TC_IDX ); // Tests the remove words from a group of words operation. +TEST_FUNCTION( UtcDaliTextViewRemoveGroupsFromLine, POSITIVE_TC_IDX ); // Tests the remove groups of words from a line operation. + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliTextViewCreateTextInfo() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewCreateTextInfo : "); + + // Metrics for characters + + // Font size = 10 + // size : [9.48351, 9.48351] + // advance : 9.48351 + // bearing : 8.53516 + // ascender : 8.53516 + + // Font size = 12 + // size : [11.3802, 11.3802] + // advance : 11.3802 + // bearing : 10.2422 + // ascender : 10.2422 + + // Font size = 14 + // size : [13.2769, 13.2769] + // advance : 13.2769 + // bearing : 11.9492 + // ascender : 11.9492 + + const float WIDTH_10( 9.48351f ); + const float HEIGHT_10( 9.48351f ); + const float ADVANCE_10( 9.48351f ); + const float BEARING_10( 8.53516f ); + const float ASCENDER_10( 8.53516f ); + + const float WIDTH_12( 11.3802f ); + const float HEIGHT_12( 11.3802f ); + const float ADVANCE_12( 11.3802f ); + const float BEARING_12( 10.2422f ); + const float ASCENDER_12( 10.2422f ); + + + // Generate a text. + Toolkit::Internal::TextView::RelayoutData relayoutData; + TextViewProcessor::TextLayoutInfo& textLayoutInfo( relayoutData.mTextLayoutInfo ); + + std::string text( "Hello world!\n" + "\n" ); + + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + TextViewProcessor::CreateTextInfo( styledText, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData ); + + + // Build the text info with metric values. + + // Characters + + TextViewProcessor::CharacterLayoutInfo layoutInfo10; // ( [lo wo]) + layoutInfo10.mHeight = HEIGHT_10; + layoutInfo10.mAdvance = ADVANCE_10; + layoutInfo10.mBearing = BEARING_10; + layoutInfo10.mSize = Size( WIDTH_10, HEIGHT_10 ); + layoutInfo10.mAscender = ASCENDER_10; + TextViewProcessor::CharacterLayoutInfo layoutInfo12; // ( [Hel], [rld!] and [CR]) + layoutInfo12.mHeight = HEIGHT_12; + layoutInfo12.mAdvance = ADVANCE_12; + layoutInfo12.mBearing = BEARING_12; + layoutInfo12.mSize = Size( WIDTH_12, HEIGHT_12 ); + layoutInfo12.mAscender = ASCENDER_12; + + TextStyle style10; + style10.SetFontPointSize( PointSize( 10.f ) ); + TextStyle style12; + style12.SetFontPointSize( PointSize( 0.f ) ); // point size is set to zero because is a default point size. + + layoutInfo12.mStyledText.mStyle = style12; + layoutInfo10.mStyledText.mStyle = style10; + + // Words + + TextViewProcessor::WordLayoutInfo wordLayout1, wordLayout2, wordLayout3, wordLayout4; + + // Hello + wordLayout1.mSize = Size( 3.f * WIDTH_12 + 2.f * WIDTH_10, HEIGHT_12 ); + wordLayout1.mAscender = ASCENDER_12; + wordLayout1.mType = TextViewProcessor::NoSeparator; + + layoutInfo12.mStyledText.mText = Text( "H" ); + wordLayout1.mCharactersLayoutInfo.push_back( layoutInfo12 ); // H + layoutInfo12.mStyledText.mText = Text( "e" ); + wordLayout1.mCharactersLayoutInfo.push_back( layoutInfo12 ); // e + layoutInfo12.mStyledText.mText = Text( "l" ); + wordLayout1.mCharactersLayoutInfo.push_back( layoutInfo12 ); // l + layoutInfo10.mStyledText.mText = Text( "l" ); + wordLayout1.mCharactersLayoutInfo.push_back( layoutInfo10 ); // l + layoutInfo10.mStyledText.mText = Text( "o" ); + wordLayout1.mCharactersLayoutInfo.push_back( layoutInfo10 ); // o + + // (white space) + wordLayout2.mSize = Size( WIDTH_10, HEIGHT_10 ); + wordLayout2.mAscender = ASCENDER_10; + wordLayout2.mType = TextViewProcessor::WordSeparator; + layoutInfo10.mStyledText.mText = Text( " " ); + wordLayout2.mCharactersLayoutInfo.push_back( layoutInfo10 ); // (white space) + + // world! + wordLayout3.mSize = Size( 2.f * WIDTH_10 + 4.f * WIDTH_12, HEIGHT_12 ); + wordLayout3.mAscender = ASCENDER_12; + wordLayout3.mType = TextViewProcessor::NoSeparator; + layoutInfo10.mStyledText.mText = Text( "w" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo10 ); // w + layoutInfo10.mStyledText.mText = Text( "o" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo10 ); // o + layoutInfo12.mStyledText.mText = Text( "r" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo12 ); // r + layoutInfo12.mStyledText.mText = Text( "l" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo12 ); // l + layoutInfo12.mStyledText.mText = Text( "d" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo12 ); // d + layoutInfo12.mStyledText.mText = Text( "!" ); + wordLayout3.mCharactersLayoutInfo.push_back( layoutInfo12 ); // ! + + // (new line character) + wordLayout4.mSize = Size( 0.f, HEIGHT_12 ); + wordLayout4.mAscender = ASCENDER_12; + wordLayout4.mType = TextViewProcessor::LineSeparator; + layoutInfo12.mStyledText.mText = Text( "\n" ); + layoutInfo12.mSize.width = 0.f; + wordLayout4.mCharactersLayoutInfo.push_back( layoutInfo12 ); // (new line char) + + // Groups + + TextViewProcessor::WordGroupLayoutInfo groupLayout1, groupLayout2; + + groupLayout1.mSize = Size( 5.f * WIDTH_10 + 7.f * WIDTH_12, HEIGHT_12 ); + groupLayout1.mAscender = ASCENDER_12; + groupLayout1.mDirection = TextViewProcessor::LTR; + groupLayout1.mNumberOfCharacters = 13; + groupLayout1.mWordsLayoutInfo.push_back( wordLayout1 ); + groupLayout1.mWordsLayoutInfo.push_back( wordLayout2 ); + groupLayout1.mWordsLayoutInfo.push_back( wordLayout3 ); + groupLayout1.mWordsLayoutInfo.push_back( wordLayout4 ); + + groupLayout2.mSize = Size( 0.f, HEIGHT_12 ); + groupLayout2.mAscender = ASCENDER_12; + groupLayout2.mDirection = TextViewProcessor::LTR; + groupLayout2.mNumberOfCharacters = 1; + groupLayout2.mWordsLayoutInfo.push_back( wordLayout4 ); + + // Lines + + TextViewProcessor::LineLayoutInfo lineLayout1, lineLayout2, lineLayout3; + + lineLayout1.mSize = Size( 5.f * WIDTH_10 + 7.f * WIDTH_12, HEIGHT_12 ); + lineLayout1.mAscender = ASCENDER_12; + lineLayout1.mNumberOfCharacters = 13; + lineLayout1.mWordGroupsLayoutInfo.push_back( groupLayout1 ); + + lineLayout2.mSize = Size( 0.f, HEIGHT_12 ); + lineLayout2.mAscender = ASCENDER_12; + lineLayout2.mNumberOfCharacters = 1; + lineLayout2.mWordGroupsLayoutInfo.push_back( groupLayout2 ); + + lineLayout3.mSize = Size( 0.f, HEIGHT_12 ); + + // Text (layout) + TextViewProcessor::TextLayoutInfo textLayout; + + textLayout.mWholeTextSize = Size( 5.f * WIDTH_10 + 7.f * WIDTH_12, 3.f * HEIGHT_12 ); + textLayout.mMaxWordWidth = 2.f * WIDTH_10 + 4.f * WIDTH_12; + textLayout.mNumberOfCharacters = 14; + textLayout.mLinesLayoutInfo.push_back( lineLayout1 ); + textLayout.mLinesLayoutInfo.push_back( lineLayout2 ); + textLayout.mLinesLayoutInfo.push_back( lineLayout3 ); + + if(!TestEqual( textLayout, textLayoutInfo )) + { + std::cout << "Layout fails" << std::endl; + Print(textLayout); std::cout << std::endl; + Print(textLayoutInfo); std::cout << std::endl; + } + + DALI_TEST_CHECK( TestEqual( textLayout, textLayoutInfo ) ); +} + +static void UtcDaliTextViewSplitWord() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewSplitWord : "); + + struct SplitWordTest splitWordTests[] = + { + { + std::string( "Split word, position 0." ), + std::string( "Helloooo" ), + 0, + std::string( "" ), + std::string( "Helloooo" ), + }, + { + std::string( "Split word, position 8." ), + std::string( "Helloooo" ), + 8, + std::string( "Helloooo" ), + std::string( "" ), + }, + { + std::string( "Split word, position 2." ), + std::string( "Helloooo" ), + 2, + std::string( "He" ), + std::string( "lloooo" ), + }, + { + std::string( "Split word, position 3." ), + std::string( "Helloooo" ), + 3, + std::string( "Hel" ), + std::string( "loooo" ), + }, + { + std::string( "Split word, position 4." ), + std::string( "Helloooo" ), + 4, + std::string( "Hell" ), + std::string( "oooo" ), + }, + }; + const std::size_t numberOfTests( 5 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const SplitWordTest& test = splitWordTests[index]; + + if( !TestSplitWord( test.description, test.input, test.position, test.firstResult, test.lastResult, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewUpdateTextInfo() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewUpdateTextInfo : "); + + struct UpdateTextInfoTest updateTextInfoTest[] = + { + // Remove operations + + { + std::string( "Remove from new line character to first character next line." ), + Remove, + std::string("Hello world\nhello world."), + 11, + 2, + std::string(""), + 0.f, + std::string("Hello worldello world."), + }, + { + std::string( "Replace style from new line character to first character next line." ), + Replace, + std::string("Hello world\nhello world."), + 11, + 2, + std::string("\nh"), + 0.f, + std::string("Hello world\nhello world."), + }, + { + std::string( "Remove from the beginning to the middle of last word." ), + Remove, + std::string("Hello world, hello world."), + 0, + 22, + std::string(), // Not used. + 0.f, + std::string("ld."), + }, + { + std::string( "Remove from the beginning to the middle of the text." ), + Remove, + std::string("Hello world hello world."), + 0, + 12, + std::string(), // Not used. + 0.f, + std::string("hello world."), + }, + // Remove within the same word: + // * within the same group of characters. + { + std::string( "Remove within the same word, within the same group of characters" ), + Remove, + std::string("Hello world\nhello world"), + 7, + 3, + std::string(), // Not used. + 0.f, + std::string( "Hello wd\nhello world" ) + }, + // * whole group of characters (merge adjacent group of characters) + { + std::string( "Remove within the same word, whole group of characters (merge adjacent group of characters)" ), + Remove, + std::string("Hello world\nhello world"), + 7, + 3, + std::string(), // Not used. + 0.f, + std::string( "Hello wd\nhello world" ) + }, + // * whole group of characters (don't merge adjacent gtoup of characters) + { + std::string( "Remove within the same word, whole group of characters (don't merge adjacent gtoup of characters)" ), + Remove, + std::string("Hello world\nhello world"), + 7, + 3, + std::string(), // Not used. + 0.f, + std::string( "Hello wd\nhello world" ) + }, + // * Remove whole word (merge words) + { + std::string( "Remove within the same word, whole word (merge words)" ), + Remove, + std::string("Hello world\nhello world"), + 5, + 1, + std::string(), // Not used. + 0.f, + std::string( "Helloworld\nhello world" ) + }, + // * Remove whole word (don't merge words) + { + std::string( "Remove within the same word, whole word (don't merge words)" ), + Remove, + std::string("Hello world\nhello world"), + 6, + 5, + std::string(), // Not used. + 0.f, + std::string( "Hello \nhello world" ) + }, + // * Remove whole word (merge lines) + { + std::string( "Remove within the same word, whole word (merge lines)" ), + Remove, + std::string("Hello world\nhello world"), + 11, + 1, + std::string(), // Not used. + 0.f, + std::string( "Hello worldhello world" ) + }, + // * Remove whole group of words + /* TODO check this when RTL text is working + { + std::string( "Remove within the same line, whole group of words (merge groups)" ), + Remove, + std::string("Hello world, שלום עולם, hello world"), + 10, + 15, + std::string(), // Not used. + 0.f, + std::string( "Hello worlello world" ) + }, + */ + // * Remove whole line + { + std::string( "Remove whole line" ), + Remove, + std::string("Hello world, hello world\n" + "Hello world, hello world\n" + "Hello world, hello world\n" + "Hello world, hello world\n"), + 25, + 25, + std::string(), // Not used. + 0.f, + std::string("Hello world, hello world\n" + "Hello world, hello world\n" + "Hello world, hello world\n"), + }, + { + std::string( "Remove whole line" ), + Remove, + std::string("Hello world, hello world\n" + "H"), + 25, + 1, + std::string(), // Not used. + 0.f, + std::string("Hello world, hello world\n"), + }, + + + // Insert operations + { + std::string( "insert some text" ), + Insert, + std::string("inpuext"), + 4, + 0, // Not used + std::string( "t t" ), + 0.f, + std::string( "input text" ) + }, + { + std::string( "Insert text at the end" ), + Insert, + std::string("touch "), + 6, + 0, + std::string("me\nhello"), + 0.f, + std::string("touch me\nhello") + }, + + // Replace operations. + { + std::string( "Replace style from the beginning to some point in the middle of the text." ), + Replace, + std::string( "Hello world" ), + 0, + 7, + std::string( "Hello w" ), + 0.f, + std::string( "Hello world" ) + }, + { + std::string( "Replace style from the middle of the text to the end." ), + Replace, + std::string( "Touch me\nhello" ), + 6, + 8, + std::string( "me\nhello" ), + 0.f, + std::string( "Touch me\nhello" ) + }, + { + std::string( "Remove characters from text. Previous next test:Replace style from the middle of the text 1." ), + Remove, + std::string( "Touch me\nhello\nworld" ), + 6, + 8, + std::string( "" ), + 0.f, + std::string( "Touch \nworld" ) + }, + { + std::string( "Insert styled text in the middle of a text. Previous: Replace style from the middle of the text 1." ), + Insert, + std::string( "Touch \nworld" ), + 6, + 0, + std::string( "me\nhello" ), + 0.f, + std::string( "Touch me\nhello\nworld" ) + }, + { + std::string( "Replace style from the middle of the text 1." ), + Replace, + std::string( "Touch me\nhello\nworld" ), + 6, + 8, + std::string( "me\nhello" ), + 0.f, + std::string( "Touch me\nhello\nworld" ) + }, + { + std::string( "Remove characters from text. Previous next test:Replace style from the middle of the text 2." ), + Remove, + std::string( "Touch me\nhello\nworld" ), + 6, + 9, + std::string( "" ), + 0.f, + std::string( "Touch world" ) + }, + { + std::string( "Replace style from the middle of the text 2." ), + Replace, + std::string( "Touch me\nhello\nworld" ), + 6, + 9, + std::string( "me\nhello\n" ), + 0.f, + std::string( "Touch me\nhello\nworld" ) + }, + }; + const std::size_t numberOfTests( 21 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const UpdateTextInfoTest& test = updateTextInfoTest[index]; + + if( !TestUpdateTextInfo( test.description, test.operation, test.input, test.position, test.numberOfCharacters, test.inputText, test.lineHeightOffset, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewSplitWordGroup() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewSplitWordGroup : "); + + struct SplitWordGroupTest splitWordGroupTests[] = + { + { + std::string( "Split word group, wordPosition 0, position 0." ), + std::string( "Helloooo wooorld" ), + 0, + 0, + std::string( "" ), + std::string( "Helloooo wooorld" ), + }, + { + std::string( "Split word group, wordPosition 2, position 8." ), + std::string( "Helloooo wooorld" ), + 2, + 7, + std::string( "Helloooo wooorld" ), + std::string( "" ), + }, + { + std::string( "Split word group, wordPosition 0, position 2." ), + std::string( "Helloooo wooorld" ), + 0, + 2, + std::string( "He" ), + std::string( "lloooo wooorld" ), + }, + { + std::string( "Split word group, wordPosition 0, position 3." ), + std::string( "Helloooo wooorld" ), + 0, + 3, + std::string( "Hel" ), + std::string( "loooo wooorld" ), + }, + { + std::string( "Split word group, wordPosition 0, position 4." ), + std::string( "Helloooo wooorld" ), + 0, + 4, + std::string( "Hell" ), + std::string( "oooo wooorld" ), + }, + { + std::string( "Split word group, wordPosition 1, position 0." ), + std::string( "Helloooo wooorld" ), + 1, + 0, + std::string( "Helloooo" ), + std::string( " wooorld" ), + }, + }; + const std::size_t numberOfTests( 6 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const SplitWordGroupTest& test = splitWordGroupTests[index]; + + if( !TestSplitWordGroup( test.description, test.input, test.wordPosition, test.position, test.firstResult, test.lastResult, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewSplitLine() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewSplitLine : "); + + struct SplitLineTest splitLineTests[] = + { + { + std::string( "Split line, groupPosition 0, wordPosition 0, position 0." ), + std::string( "Helloooo wooorld שלום עולם text text" ), + 0, + 0, + 0, + 3.f, + std::string( "" ), + std::string( "Helloooo wooorld שלום עולם text text" ), + }, + { + std::string( "Split line, groupPosition 2, wordPosition 2, position 4." ), + std::string( "Helloooo wooorld שלום עולם text text" ), + 2, + 2, + 4, + 0.f, + std::string( "Helloooo wooorld שלום עולם text text" ), + std::string( "" ), + }, + /* TODO check when RTL is working. + { + std::string( "Split line, groupPosition 1, wordPosition 2, position 0." ), + std::string( "Helloooo wooorld שלום עולם text text" ), + 1, + 2, + 0, + 0.f, + std::string( "Helloooo wooorld שלום" ), + std::string( " עולם text text" ), + }, + { + std::string( "Split line, groupPosition 1, wordPosition 0, position 0." ), + std::string( "Helloooo wooorld שלום עולם text text" ), + 1, + 0, + 0, + 0.f, + std::string( "Helloooo wooorld " ), + std::string( "שלום עולם text text" ), + }, + */ + { + std::string( "Split line, groupPosition 2, wordPosition 0, position 0." ), + std::string( "Helloooo wooorld שלום עולם text text" ), + 2, + 0, + 0, + 6.f, + std::string( "Helloooo wooorld שלום עולם " ), + std::string( "text text" ), + }, + }; + const std::size_t numberOfTests( 3 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const SplitLineTest& test = splitLineTests[index]; + + if( !TestSplitLine( test.description, test.input, test.groupPosition, test.wordPosition, test.position, test.lineHeightOffset, test.firstResult, test.lastResult, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewMergeWord01() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeWord01 : "); + + struct MergeWordsTest mergeWordsTests[] = + { + { + std::string( "Merge words with same style." ), + std::string( "Hel" ), + std::string( "lo" ), + std::string( "Hello" ), + }, + { + std::string( "Merge words with different styles." ), + std::string( "lo" ), + std::string( "Hello" ) + }, + }; + const std::size_t numberOfTests( 2 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const MergeWordsTest& test = mergeWordsTests[index]; + + if( !TestMergeWords( test.description, test.inputFirst, test.inputLast, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewMergeWord02() +{ + // Negative test. + // It test white spaces and new line characters can't be merged to other words. + + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeWord02 : "); + + // Generate three words + + Toolkit::Internal::TextView::RelayoutData relayoutData01; + Toolkit::Internal::TextView::RelayoutData relayoutData02; + Toolkit::Internal::TextView::RelayoutData relayoutData03; + TextViewProcessor::TextLayoutInfo& textLayoutInfo01( relayoutData01.mTextLayoutInfo ); + TextViewProcessor::TextLayoutInfo& textLayoutInfo02( relayoutData02.mTextLayoutInfo ); + TextViewProcessor::TextLayoutInfo& textLayoutInfo03( relayoutData03.mTextLayoutInfo ); + + std::string text01( " " ); + std::string text02( "\n" ); + std::string text03( "a" ); + MarkupProcessor::StyledTextArray styledText01; + MarkupProcessor::StyledTextArray styledText02; + MarkupProcessor::StyledTextArray styledText03; + MarkupProcessor::GetStyledTextArray( text01, styledText01 ); + MarkupProcessor::GetStyledTextArray( text02, styledText02 ); + MarkupProcessor::GetStyledTextArray( text03, styledText03 ); + + TextViewProcessor::CreateTextInfo( styledText01, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData01 ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo01; + + wordLayoutInfo01 = *( *( *textLayoutInfo01.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + + TextViewProcessor::CreateTextInfo( styledText02, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData02 ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo02; + + wordLayoutInfo02 = *( *( *textLayoutInfo02.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + + TextViewProcessor::CreateTextInfo( styledText03, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData03 ); + + TextViewProcessor::WordLayoutInfo wordLayoutInfo03; + + wordLayoutInfo03 = *( *( *textLayoutInfo03.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin() ).mWordsLayoutInfo.begin(); + + // Test MergeWord() asserts if white spaces or new line chars are merged. + bool assert1 = false; + bool assert2 = false; + bool assert3 = false; + bool assert4 = false; + bool assert5 = false; + bool assert6 = false; + + try + { + MergeWord( wordLayoutInfo01, + wordLayoutInfo02 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert1 = true; + } + try + { + MergeWord( wordLayoutInfo01, + wordLayoutInfo03 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert2 = true; + } + try + { + MergeWord( wordLayoutInfo02, + wordLayoutInfo01 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert3 = true; + } + try + { + MergeWord( wordLayoutInfo02, + wordLayoutInfo03 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert4 = true; + } + try + { + MergeWord( wordLayoutInfo03, + wordLayoutInfo01 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert5 = true; + } + try + { + MergeWord( wordLayoutInfo03, + wordLayoutInfo02 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words.\"", TEST_LOCATION ); + assert6 = true; + } + + if( assert1 && assert2 && assert3 && assert4 && assert5 && assert6 ) + { + tet_result( TET_PASS ); + } + else + { + tet_result( TET_FAIL ); + } +} + +static void UtcDaliTextViewMergeGroup01() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeGroup01 : "); + + struct MergeWordGroupsTest mergeWordGroupssTests[] = + { + { + std::string( "Merge a void first group." ), + std::string( "" ), + std::string( "Hello world" ), + std::string( "Hello world" ), + }, + { + std::string( "Merge a void last group." ), + std::string( "Hello world" ), + std::string( "" ), + std::string( "Hello world" ), + }, + { + std::string( "Merge groups and merge last and first words." ), + std::string( "Hello wor" ), + std::string( "ld, hello world" ), + std::string( "Hello world, hello world" ), + }, + { + std::string( "Merge groups and don't merge last and first words." ), + std::string( "Hello world, " ), + std::string( "hello world" ), + std::string( "Hello world, hello world" ) + }, + }; + const std::size_t numberOfTests( 4 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const MergeWordGroupsTest& test = mergeWordGroupssTests[index]; + + if( !TestMergeGroupsOfWords( test.description, test.inputFirst, test.inputLast, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result(TET_PASS); +} + +static void UtcDaliTextViewMergeGroup02() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeGroup02 : "); + + Toolkit::Internal::TextView::RelayoutData relayoutData01; + Toolkit::Internal::TextView::RelayoutData relayoutData02; + Toolkit::Internal::TextView::RelayoutData relayoutData03; + TextViewProcessor::TextLayoutInfo& textLayoutInfo01( relayoutData01.mTextLayoutInfo ); + TextViewProcessor::TextLayoutInfo& textLayoutInfo02( relayoutData02.mTextLayoutInfo ); + TextViewProcessor::TextLayoutInfo& textLayoutInfo03( relayoutData03.mTextLayoutInfo ); + + std::string text01( "Hello \n" ); + std::string text02( "world" ); + std::string text03( "السلام عليكم" ); + MarkupProcessor::StyledTextArray styledText01; + MarkupProcessor::StyledTextArray styledText02; + MarkupProcessor::StyledTextArray styledText03; + MarkupProcessor::GetStyledTextArray( text01, styledText01 ); + MarkupProcessor::GetStyledTextArray( text02, styledText02 ); + MarkupProcessor::GetStyledTextArray( text03, styledText03 ); + + TextViewProcessor::CreateTextInfo( styledText01, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData01 ); + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo01; + + wordGroupLayoutInfo01 = *( *textLayoutInfo01.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + + TextViewProcessor::CreateTextInfo( styledText02, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData02 ); + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo02; + + wordGroupLayoutInfo02 = *( *textLayoutInfo02.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + + TextViewProcessor::CreateTextInfo( styledText03, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData03 ); + + TextViewProcessor::WordGroupLayoutInfo wordGroupLayoutInfo03; + + wordGroupLayoutInfo03 = *( *textLayoutInfo03.mLinesLayoutInfo.begin() ).mWordGroupsLayoutInfo.begin(); + + bool assert1 = false; + bool assert2 = false; + + try + { + MergeWordGroup( wordGroupLayoutInfo01, + wordGroupLayoutInfo02 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWordGroup(). ERROR: A group of words can't be merged to another group which finishes with a new line character.\"", TEST_LOCATION ); + assert1 = true; + } + + try + { + MergeWordGroup( wordGroupLayoutInfo03, + wordGroupLayoutInfo02 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeWordGroup(). ERROR: groups with different direction can't be merged.\"", TEST_LOCATION ); + assert2 = true; + } + + if( assert1 && assert2 ) + { + tet_result( TET_PASS ); + } + else + { + tet_result( TET_FAIL ); + } +} + +static void UtcDaliTextViewMergeLine01() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeLine01 : "); + + struct MergeLinesTest mergeLinesTests[] = + { + { + std::string( "Merge a void first line." ), + std::string( "" ), + std::string( "Hello world, this is a whole line" ), + 2.f, + std::string( "Hello world, this is a whole line" ) + }, + { + std::string( "Merge a void last line." ), + std::string( "Hello world, this is a whole line" ), + std::string( "" ), + 0.f, + std::string( "Hello world, this is a whole line" ) + }, + /* TODO: check when RTL text is working. + { + std::string( "Merge lines and merge last and first groups" ), + std::string( "Hello world, שלום" ), + std::string( " עולם, hello world." ), + 6.f, + std::string( "Hello world, שלום עולם, hello world." ) + }, + { + std::string( "Merge lines and don't merge last and first words." ), + std::string( "Hello world, " ), + std::string( "שלום עולם, hello world." ), + 3.f, + std::string( "Hello world, שלום עולם, hello world." ) + }, + */ + { + std::string( "Merge lines. Don't merge words" ), + std::string( "Hello world," ), + std::string( " this is a whole line" ), + 0.f, + std::string( "Hello world, this is a whole line" ) + }, + { + std::string( "Merge lines. Merge words" ), + std::string( "Hello world, th" ), + std::string( "is is a whole line" ), + 0.f, + std::string( "Hello world, this is a whole line" ) + }, + }; + const std::size_t numberOfTests( 4 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const MergeLinesTest& test = mergeLinesTests[index]; + + if( !TestMergeLines( test.description, test.inputFirst, test.inputLast, test.lineHeightOffset, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewMergeLine02() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeLine02 : "); + + Toolkit::Internal::TextView::RelayoutData relayoutData01; + Toolkit::Internal::TextView::RelayoutData relayoutData02; + TextViewProcessor::TextLayoutInfo& textLayoutInfo01( relayoutData01.mTextLayoutInfo ); + TextViewProcessor::TextLayoutInfo& textLayoutInfo02( relayoutData02.mTextLayoutInfo ); + + std::string text01( "Hello world\n" ); + std::string text02( "hello world" ); + MarkupProcessor::StyledTextArray styledText01; + MarkupProcessor::StyledTextArray styledText02; + MarkupProcessor::GetStyledTextArray( text01, styledText01 ); + MarkupProcessor::GetStyledTextArray( text02, styledText02 ); + + TextViewProcessor::CreateTextInfo( styledText01, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData01 ); + + TextViewProcessor::LineLayoutInfo lineLayoutInfo01; + + lineLayoutInfo01 = *textLayoutInfo01.mLinesLayoutInfo.begin(); + + TextViewProcessor::CreateTextInfo( styledText02, + DEFAULT_LAYOUT_PARAMETERS, + relayoutData02 ); + + TextViewProcessor::LineLayoutInfo lineLayoutInfo02; + + lineLayoutInfo02 = *textLayoutInfo02.mLinesLayoutInfo.begin(); + + bool assert1 = false; + + try + { + MergeLine( lineLayoutInfo01, + lineLayoutInfo02 ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + DALI_TEST_EQUALS( e.mCondition, "!\"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character.\"", TEST_LOCATION ); + assert1 = true; + } + + if( assert1 ) + { + tet_result( TET_PASS ); + } + else + { + tet_result( TET_FAIL ); + } +} + +void UtcDaliTextViewRemoveCharactersFromWord() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewMergeWord02 : "); + + struct RemoveCharactersFromWordTest removeCharactersFromWordTests[] = + { + { + std::string( "Delete 0 characters." ), + std::string( "Hello" ), + 3, + 0, + std::string( "Hello" ), + }, + { + std::string( "Delete within the same group of characters. Starting from the beginning" ), + std::string( "Hello" ), + 0, + 3, + std::string( "lo" ), + }, + { + std::string( "Delete within the same group of characters. Somewhere in the middle" ), + std::string( "Hello" ), + 2, + 2, + std::string( "Heo" ), + }, + { + std::string( "Delete within the same group of characters. Starting somewhere in the middle to the end" ), + std::string( "Hello" ), + 3, + 2, + std::string( "Hel" ), + }, + { + std::string( "Delete within the same group of characters. Finish just before a new one." ), + std::string( "HelloWorld" ), + 1, + 2, + std::string( "HloWorld" ), + }, + { + std::string( "Delete starting in one group of characters and finishing in a different one. No merge of groups." ), + std::string( "HelloWorld" ), + 2, + 3, + std::string( "HeWorld" ), + }, + { + std::string( "Delete within the same group of characters. Starting just after a different one." ), + std::string( "HelloWorld" ), + 7, + 2, + std::string( "HelloWod" ), + }, + { + std::string( "Delete whole group of characters. No merge" ), + std::string( "HelloWorld" ), + 3, + 4, + std::string( "Helrld" ), + }, + { + std::string( "Delete whole group of characters and part of the adjacent ones. No merge" ), + std::string( "HelloWorld" ), + 2, + 6, + std::string( "Held" ), + }, + { + std::string( "Delete whole group of characters. Merge" ), + std::string( "HelloWorld" ), + 3, + 4, + std::string( "Helrld" ), + }, + { + std::string( "Delete whole group of characters and part of the adjacent ones. Merge" ), + std::string( "HelloWorld" ), + 2, + 6, + std::string( "Held" ), + }, + }; + const std::size_t numberOfTests( 11 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const RemoveCharactersFromWordTest& test = removeCharactersFromWordTests[index]; + + if( !TestRemoveCharactersFromWord( test.description, test.input, test.position, test.numberOfCharacters, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewRemoveWordsFromGroup() +{ + // Note: Currently RemoveWordsFromWordGroup() function is only used to remove a number of words from the beginning, or + // from a given index to the end. RemoveWordsFromWordGroup() doesn't merge words (if a white space is removed) so + // tehere isn't any TET case to cover these cases. To be done if needed. + + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewRemoveWordsFromGroup : "); + struct RemoveWordsFromGroupTest removeWordsFromGroupTests[] = + { + { + std::string( "Delete 0 words." ), + std::string( "Hello world, hello world" ), + 3, + 0, + std::string( "Hello world, hello world" ), + }, + { + std::string( "Delete some words in the middle. Don't merge words" ), + std::string( "Hello world, hello world" ), + 1, + 4, + std::string( "Hello world" ), + }, + { + std::string( "Delete words up to the end" ), + std::string( "Hello world, hello world" ), + 5, + 2, + std::string( "Hello world, hello" ), + }, + { + std::string( "Delete words from the beginning." ), + std::string( "Hello world, hello world" ), + 0, + 3, + std::string( " hello world" ), + }, + }; + const std::size_t numberOfTests( 4 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const RemoveWordsFromGroupTest& test = removeWordsFromGroupTests[index]; + + if( !TestRemoveWordsFromGroup( test.description, test.input, test.wordIndex, test.numberOfWords, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} + +static void UtcDaliTextViewRemoveGroupsFromLine() +{ + // Note: Currently RemoveWordGroupsFromLine() function is only used to remove a number of group of words from the beginning, or + // from a given index to the end. RemoveWordGroupsFromLine() doesn't merge groups of words (if a whole group of words is removed) so + // tehere isn't any TET case to cover these cases. To be done if needed. + + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextViewRemoveGroupsFromLine : "); + struct RemoveGroupsFromLineTest removeGroupsFromLineTests[] = + { + { + std::string( "Delete 0 groups of words." ), + std::string( "Hello hello, שלום עולם hello hello" ), + 1, + 0, + 2.f, + std::string( "Hello hello, שלום עולם hello hello" ), + }, + { + std::string( "Delete from the middle to the end." ), + std::string( "Hello hello, שלום עולם hello hello" ), + 1, + 2, + 0.f, + std::string( "Hello hello, " ), + }, + { + std::string( "Delete from the beginning to the middle." ), + std::string( "Hello hello, שלום עולם hello hello" ), + 0, + 2, + 6.f, + std::string( "hello hello" ), + }, + }; + const std::size_t numberOfTests( 3 ); + + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const RemoveGroupsFromLineTest& test = removeGroupsFromLineTests[index]; + + if( !TestRemoveGroupsFromLine( test.description, test.input, test.groupIndex, test.numberOfGroups, test.lineHeightOffset, test.result, TEST_LOCATION ) ) + { + tet_result( TET_FAIL ); + } + } + + tet_result( TET_PASS ); +} diff --git a/automated-tests/dali-internal-test-suite/utc-MODULE-CLASS.cpp.in b/automated-tests/dali-internal-test-suite/utc-MODULE-CLASS.cpp.in new file mode 100644 index 0000000..2467239 --- /dev/null +++ b/automated-tests/dali-internal-test-suite/utc-MODULE-CLASS.cpp.in @@ -0,0 +1,96 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +// Internal headers are allowed here + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Toolkit::Internal; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( Utc@MODULE@@CLASS@Method01, POSITIVE_TC_IDX ); +TEST_FUNCTION( Utc@MODULE@@CLASS@Method02, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void Utc@MODULE@@CLASS@Method01() +{ + ToolkitTestApplication application; + + tet_infoline("Journaled printf Output"); + tet_result(TET_FAIL); +#if 0 + tet_result(TET_PASS); +#endif +} + + +// Negative test case for a method +static void Utc@MODULE@@CLASS@Method02() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + + try + { + /* My test code and results */ + DALI_TEST_EQUALS(myVar, expectedValue, TEST_LOCATION); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "assert conditional", TEST_LOCATION); + } +} + diff --git a/automated-tests/dali-test-suite/.gitignore b/automated-tests/dali-test-suite/.gitignore new file mode 100644 index 0000000..45486c6 --- /dev/null +++ b/automated-tests/dali-test-suite/.gitignore @@ -0,0 +1 @@ +utc-Dali-ScrollViewEffect diff --git a/automated-tests/dali-test-suite/alignment/.gitignore b/automated-tests/dali-test-suite/alignment/.gitignore new file mode 100644 index 0000000..31af42e --- /dev/null +++ b/automated-tests/dali-test-suite/alignment/.gitignore @@ -0,0 +1 @@ +utc-Dali-Alignment diff --git a/automated-tests/dali-test-suite/alignment/Makefile b/automated-tests/dali-test-suite/alignment/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/alignment/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/alignment/file.list b/automated-tests/dali-test-suite/alignment/file.list new file mode 100644 index 0000000..7a4d9f1 --- /dev/null +++ b/automated-tests/dali-test-suite/alignment/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-Alignment \ diff --git a/automated-tests/dali-test-suite/alignment/tslist b/automated-tests/dali-test-suite/alignment/tslist new file mode 100644 index 0000000..76ce95e --- /dev/null +++ b/automated-tests/dali-test-suite/alignment/tslist @@ -0,0 +1 @@ +/dali-test-suite/alignment/utc-Dali-Alignment diff --git a/automated-tests/dali-test-suite/alignment/utc-Dali-Alignment.cpp b/automated-tests/dali-test-suite/alignment/utc-Dali-Alignment.cpp new file mode 100644 index 0000000..2dd1e85 --- /dev/null +++ b/automated-tests/dali-test-suite/alignment/utc-Dali-Alignment.cpp @@ -0,0 +1,1127 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliAlignmentConstructorNegative, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentConstructorPositive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentConstructorRegister, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetAlignmentTypePositiveOffStage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetAlignmentTypePositiveOnStage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetAlignmentTypeNegative, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentGetAlignmentType, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetScaling, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentGetScaling, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetPaddingPositive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentSetPaddingNegative, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentGetPadding, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentChildAddAndRemove, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentOnSizeSet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentOnTouchEvent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentOnKeyEvent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentOnSizeAnimation, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAlignmentCopyAndAssignment, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliAlignmentConstructorNegative() +{ + ToolkitTestApplication application; + + Alignment alignment; + + try + { + Alignment::Padding padding; + alignment.SetPadding(padding); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "alignment") + { + tet_result(TET_PASS); + } + } +} + +static void UtcDaliAlignmentConstructorPositive() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + + try + { + Alignment::Padding padding; + alignment.SetPadding(padding); + tet_result(TET_PASS); + } + catch (DaliException& exception) + { + tet_result(TET_FAIL); + } + + Actor actor = alignment; + alignment = Alignment::DownCast( actor ); + + DALI_TEST_CHECK( alignment ); +} + +static void UtcDaliAlignmentConstructorRegister() +{ + ToolkitTestApplication application; + + //Te ensure the object is registered after creation + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + Alignment alignment = Alignment::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliAlignmentSetAlignmentTypePositiveOffStage() +{ + ToolkitTestApplication application; + + // Default, HorizontalCenter, VerticalCenter - Ensure they do not change! + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalCenter | Alignment::VerticalCenter)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalLeft, VerticalCenter + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::HorizontalLeft); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalRight, VerticalCenter + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::HorizontalRight); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalLeft, VerticalTop + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalLeft | Alignment::VerticalTop)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalCenter, VerticalTop + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::VerticalTop); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalRight, VerticalTop + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalRight | Alignment::VerticalTop)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalLeft, VerticalBottom + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalLeft | Alignment::VerticalBottom)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalCenter, VerticalBottom + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::VerticalBottom); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } + + // HorizontalRight, VerticalBottom + { + Alignment alignment = Alignment::New(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalRight | Alignment::VerticalBottom)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + } +} + +static void UtcDaliAlignmentSetAlignmentTypePositiveOnStage() +{ + ToolkitTestApplication application; + + // Default, HorizontalCenter, VerticalCenter - Ensure they do not change! + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalCenter | Alignment::VerticalCenter)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalCenter + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::HorizontalLeft); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalCenter + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::HorizontalRight); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalTop + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalLeft | Alignment::VerticalTop)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalCenter, VerticalTop + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::VerticalTop); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalTop + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalRight | Alignment::VerticalTop)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalBottom + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalLeft | Alignment::VerticalBottom)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalCenter, VerticalBottom + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::VerticalBottom); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalBottom + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + // Check default values + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + + Alignment::Type type(Alignment::Type(Alignment::HorizontalRight | Alignment::VerticalBottom)); + alignment.SetAlignmentType(type); + DALI_TEST_CHECK(alignment.GetAlignmentType() & type); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } +} + +static void UtcDaliAlignmentSetAlignmentTypeNegative() +{ + ToolkitTestApplication application; + + // Setting HorizontalLeft, HorizontalCenter + { + Alignment alignment = Alignment::New(); + Alignment::Type type(Alignment::Type(Alignment::HorizontalLeft | Alignment::HorizontalCenter)); + + try + { + alignment.SetAlignmentType(type); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "!horizontalSet") + { + tet_result(TET_PASS); + } + } + } + + // Setting HorizontalCenter, HorizontalRight + { + Alignment alignment = Alignment::New(); + Alignment::Type type(Alignment::Type(Alignment::HorizontalCenter | Alignment::HorizontalRight)); + + try + { + alignment.SetAlignmentType(type); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "!horizontalSet") + { + tet_result(TET_PASS); + } + } + } + + // Setting VerticalTop, VerticalCenter + { + Alignment alignment = Alignment::New(); + Alignment::Type type(Alignment::Type(Alignment::VerticalTop | Alignment::VerticalCenter)); + + try + { + alignment.SetAlignmentType(type); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "!verticalSet") + { + tet_result(TET_PASS); + } + } + } + + // Setting VerticalCenter, VerticalBottom + { + Alignment alignment = Alignment::New(); + Alignment::Type type(Alignment::Type(Alignment::VerticalTop | Alignment::VerticalBottom)); + + try + { + alignment.SetAlignmentType(type); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "!veritcalSet") + { + tet_result(TET_PASS); + } + } + } +} + +static void UtcDaliAlignmentGetAlignmentType() +{ + ToolkitTestApplication application; + + // Default, HorizonalCenter, VerticalCenter + { + Alignment alignment = Alignment::New(); + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalCenter + { + Alignment alignment = Alignment::New(Alignment::HorizontalLeft); + DALI_TEST_EQUALS(Alignment::HorizontalLeft | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalCenter + { + Alignment alignment = Alignment::New(Alignment::HorizontalRight); + DALI_TEST_EQUALS(Alignment::HorizontalRight | Alignment::VerticalCenter, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalTop + { + Alignment alignment = Alignment::New(Alignment::HorizontalLeft, Alignment::VerticalTop); + DALI_TEST_EQUALS(Alignment::HorizontalLeft | Alignment::VerticalTop, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalCenter, VerticalTop + { + Alignment alignment = Alignment::New(Alignment::HorizontalCenter, Alignment::VerticalTop); + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalTop, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalTop + { + Alignment alignment = Alignment::New(Alignment::HorizontalRight, Alignment::VerticalTop); + DALI_TEST_EQUALS(Alignment::HorizontalRight | Alignment::VerticalTop, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalLeft, VerticalBottom + { + Alignment alignment = Alignment::New(Alignment::HorizontalLeft, Alignment::VerticalBottom); + DALI_TEST_EQUALS(Alignment::HorizontalLeft | Alignment::VerticalBottom, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalCenter, VerticalBottom + { + Alignment alignment = Alignment::New(Alignment::HorizontalCenter, Alignment::VerticalBottom); + DALI_TEST_EQUALS(Alignment::HorizontalCenter | Alignment::VerticalBottom, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // HorizontalRight, VerticalBottom + { + Alignment alignment = Alignment::New(Alignment::HorizontalRight, Alignment::VerticalBottom); + DALI_TEST_EQUALS(Alignment::HorizontalRight | Alignment::VerticalBottom, alignment.GetAlignmentType(), TEST_LOCATION); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } +} + +static void UtcDaliAlignmentSetScaling() +{ + ToolkitTestApplication application; + + // ScaleToFill + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(Alignment::ScaleNone, alignment.GetScaling(), TEST_LOCATION); + alignment.SetScaling(Alignment::ScaleToFill); + DALI_TEST_EQUALS(Alignment::ScaleToFill, alignment.GetScaling(), TEST_LOCATION); + application.Render(); + application.SendNotification(); + + // For complete line coverage + alignment.SetAlignmentType(Alignment::HorizontalLeft); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::HorizontalRight); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalTop); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalBottom); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // ScaleToFitKeepAspect + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(Alignment::ScaleNone, alignment.GetScaling(), TEST_LOCATION); + alignment.SetScaling(Alignment::ScaleToFitKeepAspect); + DALI_TEST_EQUALS(Alignment::ScaleToFitKeepAspect, alignment.GetScaling(), TEST_LOCATION); + application.Render(); + application.SendNotification(); + + // For complete line coverage + alignment.SetAlignmentType(Alignment::HorizontalLeft); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::HorizontalRight); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalTop); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalBottom); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // ScaleToFillKeepAspect + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(Alignment::ScaleNone, alignment.GetScaling(), TEST_LOCATION); + alignment.SetScaling(Alignment::ScaleToFillKeepAspect); + DALI_TEST_EQUALS(Alignment::ScaleToFillKeepAspect, alignment.GetScaling(), TEST_LOCATION); + application.Render(); + application.SendNotification(); + + // For complete line coverage + alignment.SetAlignmentType(Alignment::HorizontalLeft); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::HorizontalRight); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalTop); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalBottom); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // ShrinkToFit + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(Alignment::ScaleNone, alignment.GetScaling(), TEST_LOCATION); + alignment.SetScaling(Alignment::ShrinkToFit); + DALI_TEST_EQUALS(Alignment::ShrinkToFit, alignment.GetScaling(), TEST_LOCATION); + application.Render(); + application.SendNotification(); + + // For complete line coverage + alignment.SetAlignmentType(Alignment::HorizontalLeft); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::HorizontalRight); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalTop); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalBottom); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } + + // ShrinkToFitKeepAspect + { + Alignment alignment = Alignment::New(); + alignment.Add(RenderableActor::New()); + Stage::GetCurrent().Add(alignment); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(Alignment::ScaleNone, alignment.GetScaling(), TEST_LOCATION); + alignment.SetScaling(Alignment::ShrinkToFitKeepAspect); + DALI_TEST_EQUALS(Alignment::ShrinkToFitKeepAspect, alignment.GetScaling(), TEST_LOCATION); + application.Render(); + application.SendNotification(); + + // For complete line coverage + alignment.SetAlignmentType(Alignment::HorizontalLeft); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::HorizontalRight); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalTop); + application.Render(); + application.SendNotification(); + alignment.SetAlignmentType(Alignment::VerticalBottom); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); + application.Render(); + application.SendNotification(); + } +} + +static void UtcDaliAlignmentGetScaling() +{ + ToolkitTestApplication application; + + // ScaleToFill + { + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleNone); + + alignment.SetScaling(Alignment::ScaleToFill); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleToFill); + } + + // ScaleToFitKeepAspect + { + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleNone); + + alignment.SetScaling(Alignment::ScaleToFitKeepAspect); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleToFitKeepAspect); + } + + // ScaleToFillKeepAspect + { + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleNone); + + alignment.SetScaling(Alignment::ScaleToFillKeepAspect); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleToFillKeepAspect); + } + + // ShrinkToFit + { + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleNone); + + alignment.SetScaling(Alignment::ShrinkToFit); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ShrinkToFit); + } + + // ShrinkToFitKeepAspect + { + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ScaleNone); + + alignment.SetScaling(Alignment::ShrinkToFitKeepAspect); + DALI_TEST_CHECK(alignment.GetScaling() == Alignment::ShrinkToFitKeepAspect); + } + +} + +static void UtcDaliAlignmentSetPaddingPositive() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + + Alignment::Padding padding(1.0f, 1.5f, 2.f, 0.5f); + DALI_TEST_CHECK( fabs( padding.left - alignment.GetPadding().left ) > GetRangedEpsilon( padding.left, alignment.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( padding.right - alignment.GetPadding().right ) > GetRangedEpsilon( padding.right, alignment.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( padding.top - alignment.GetPadding().top ) > GetRangedEpsilon( padding.top, alignment.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( padding.bottom - alignment.GetPadding().bottom ) > GetRangedEpsilon( padding.bottom, alignment.GetPadding().bottom ) ); + + alignment.SetPadding(padding); + DALI_TEST_CHECK( fabs( padding.left - alignment.GetPadding().left ) < GetRangedEpsilon( padding.left, alignment.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( padding.right - alignment.GetPadding().right ) < GetRangedEpsilon( padding.right, alignment.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( padding.top - alignment.GetPadding().top ) < GetRangedEpsilon( padding.top, alignment.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( padding.bottom - alignment.GetPadding().bottom ) < GetRangedEpsilon( padding.bottom, alignment.GetPadding().bottom ) ); +} + +static void UtcDaliAlignmentSetPaddingNegative() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + + try + { + Alignment::Padding padding(-1.0f, 1.5f, 2.f, 0.f); + alignment.SetPadding(padding); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f )") + { + tet_result(TET_PASS); + } + } + + try + { + Alignment::Padding padding(1.0f, 1.5f, -2.f, 0.f); + alignment.SetPadding(padding); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f )") + { + tet_result(TET_PASS); + } + } + + try + { + Alignment::Padding padding(1.0f, 1.5f, 2.f, -1.f); + alignment.SetPadding(padding); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f )") + { + tet_result(TET_PASS); + } + } + + try + { + Alignment::Padding padding(1.0f, -1.5f, 2.f, 0.f); + alignment.SetPadding(padding); + tet_result(TET_FAIL); + } + catch (DaliException& exception) + { + if (exception.mCondition == "( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f )") + { + tet_result(TET_PASS); + } + } +} + +static void UtcDaliAlignmentGetPadding() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + DALI_TEST_CHECK( fabs( alignment.GetPadding().left ) < GetRangedEpsilon( 0.f, alignment.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( alignment.GetPadding().right ) < GetRangedEpsilon( 0.f, alignment.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( alignment.GetPadding().top ) < GetRangedEpsilon( 0.f, alignment.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( alignment.GetPadding().bottom ) < GetRangedEpsilon( 0.f, alignment.GetPadding().bottom ) ); + + Alignment::Padding padding(1.0f, 1.5f, 2.f, 0.f); + alignment.SetPadding(padding); + DALI_TEST_CHECK( fabs( padding.left - alignment.GetPadding().left ) < GetRangedEpsilon( padding.left, alignment.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( padding.right - alignment.GetPadding().right ) < GetRangedEpsilon( padding.right, alignment.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( padding.top - alignment.GetPadding().top ) < GetRangedEpsilon( padding.top, alignment.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( padding.bottom - alignment.GetPadding().bottom ) < GetRangedEpsilon( padding.bottom, alignment.GetPadding().bottom ) ); +} + +static void UtcDaliAlignmentChildAddAndRemove() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + Stage::GetCurrent().Add(alignment); + + application.Render(); + application.SendNotification(); + + Actor actor = RenderableActor::New(); + alignment.Add(actor); + + DALI_TEST_EQUALS(alignment.GetChildCount(), 1u, TEST_LOCATION); + + application.Render(); + application.SendNotification(); + + alignment.Remove(actor); + + DALI_TEST_EQUALS(alignment.GetChildCount(), 0u, TEST_LOCATION); + + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(alignment); +} + +static void UtcDaliAlignmentOnSizeSet() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + Stage::GetCurrent().Add(alignment); + + application.Render(); + application.SendNotification(); + + Vector3 size(100.0f, 200.0f, 0.0f); + alignment.SetSize(size); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(size, alignment.GetImplementation().GetControlSize(), TEST_LOCATION); + + Stage::GetCurrent().Remove(alignment); +} + +/////////////////////////////////////////////////////////////////////////////// +static bool TouchEventCallback(Actor actor, const TouchEvent& event) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void UtcDaliAlignmentOnTouchEvent() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + alignment.SetSize(100.0f, 100.0f); + alignment.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(alignment); + + alignment.TouchedSignal().Connect(&TouchEventCallback); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + Integration::TouchEvent touchEvent(1); + TouchPoint point(1, TouchPoint::Down, 20.0f, 20.0f); + touchEvent.AddPoint(point); + application.GetCore().SendEvent(touchEvent); + + tet_result(TET_PASS); // For line coverage, as long as there are no exceptions, we assume passed. +} + +static void UtcDaliAlignmentOnKeyEvent() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + Stage::GetCurrent().Add(alignment); + + alignment.SetKeyInputFocus(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + Integration::KeyEvent keyEvent; + application.GetCore().SendEvent(keyEvent); + + tet_result(TET_PASS); // For line coverage, as long as there are no exceptions, we assume passed. +} + +static void UtcDaliAlignmentOnSizeAnimation() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + Stage::GetCurrent().Add(alignment); + + Animation animation = Animation::New(100.0f); + animation.Resize(alignment, Vector3(100.0f, 150.0f, 200.0f)); + animation.Play(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + tet_result(TET_PASS); // For line coverage, as long as there are no exceptions, we assume passed. +} + +static void UtcDaliAlignmentCopyAndAssignment() +{ + ToolkitTestApplication application; + + Alignment alignment = Alignment::New(); + Alignment emptyAlignment; + + Alignment::Padding padding(100.0f, 150.0f, 200.f, 0.f); + alignment.SetPadding(padding); + + Alignment alignmentCopy(alignment); + DALI_TEST_CHECK( fabs( padding.left - alignmentCopy.GetPadding().left ) < GetRangedEpsilon( padding.left, alignmentCopy.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( padding.right - alignmentCopy.GetPadding().right ) < GetRangedEpsilon( padding.right, alignmentCopy.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( padding.top - alignmentCopy.GetPadding().top ) < GetRangedEpsilon( padding.top, alignmentCopy.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( padding.bottom - alignmentCopy.GetPadding().bottom ) < GetRangedEpsilon( padding.bottom, alignmentCopy.GetPadding().bottom ) ); + + Alignment alignmentEmptyCopy(emptyAlignment); + DALI_TEST_CHECK(emptyAlignment == alignmentEmptyCopy); + + Alignment alignmentEquals; + alignmentEquals = alignment; + DALI_TEST_CHECK( fabs( padding.left - alignmentEquals.GetPadding().left ) < GetRangedEpsilon( padding.left, alignmentEquals.GetPadding().left ) ); + DALI_TEST_CHECK( fabs( padding.right - alignmentEquals.GetPadding().right ) < GetRangedEpsilon( padding.right, alignmentEquals.GetPadding().right ) ); + DALI_TEST_CHECK( fabs( padding.top - alignmentEquals.GetPadding().top ) < GetRangedEpsilon( padding.top, alignmentEquals.GetPadding().top ) ); + DALI_TEST_CHECK( fabs( padding.bottom - alignmentEquals.GetPadding().bottom ) < GetRangedEpsilon( padding.bottom, alignmentEquals.GetPadding().bottom ) ); + + Alignment alignmentEmptyEquals; + alignmentEmptyEquals = emptyAlignment; + DALI_TEST_CHECK(emptyAlignment == alignmentEmptyEquals); + + // Self assignment + alignment = alignment; + DALI_TEST_CHECK(alignment == alignmentCopy); +} diff --git a/automated-tests/dali-test-suite/bubble-emitter/.gitignore b/automated-tests/dali-test-suite/bubble-emitter/.gitignore new file mode 100644 index 0000000..f514af1 --- /dev/null +++ b/automated-tests/dali-test-suite/bubble-emitter/.gitignore @@ -0,0 +1 @@ +utc-Dali-BubbleEmitter diff --git a/automated-tests/dali-test-suite/bubble-emitter/Makefile b/automated-tests/dali-test-suite/bubble-emitter/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/bubble-emitter/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/bubble-emitter/file.list b/automated-tests/dali-test-suite/bubble-emitter/file.list new file mode 100644 index 0000000..2d1ab31 --- /dev/null +++ b/automated-tests/dali-test-suite/bubble-emitter/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-BubbleEmitter \ diff --git a/automated-tests/dali-test-suite/bubble-emitter/tslist b/automated-tests/dali-test-suite/bubble-emitter/tslist new file mode 100644 index 0000000..c73cb09 --- /dev/null +++ b/automated-tests/dali-test-suite/bubble-emitter/tslist @@ -0,0 +1 @@ +/dali-test-suite/bubble-emitter/utc-Dali-BubbleEmitter diff --git a/automated-tests/dali-test-suite/bubble-emitter/utc-Dali-BubbleEmitter.cpp b/automated-tests/dali-test-suite/bubble-emitter/utc-Dali-BubbleEmitter.cpp new file mode 100644 index 0000000..620504f --- /dev/null +++ b/automated-tests/dali-test-suite/bubble-emitter/utc-Dali-BubbleEmitter.cpp @@ -0,0 +1,423 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const int RENDER_FRAME_INTERVAL = 16; + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +/* + * Simulate time passed by. + * + * @note this will always process at least 1 frame (1/60 sec) + * + * @param application Test application instance + * @param duration Time to pass in milliseconds. + * @return The actual time passed in milliseconds + */ +int Wait(ToolkitTestApplication& application, int duration = 0) +{ + int time = 0; + + for(int i = 0; i <= ( duration / RENDER_FRAME_INTERVAL); i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + time += RENDER_FRAME_INTERVAL; + } + + return time; +} + +Image CreateSolidColorImage( ToolkitTestApplication& application, const Vector4& color, unsigned int width, unsigned int height ) +{ + BitmapImage imageData = BitmapImage::New( width, height, Pixel::RGBA8888 ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + unsigned int size = width * height; + + for( size_t i = 0; i < size; i++ ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + imageData.Update(); + + application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE ); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + application.Render(RENDER_FRAME_INTERVAL); + application.SendNotification(); + + return imageData; +} +}//namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliBubbleEmitterNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterGetRootActor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetBackground, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetShapeImage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetBubbleScale, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetBubbleDensity01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetBubbleDensity02, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterSetBlendMode, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterEmitBubble, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterStartExplosion, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBubbleEmitterRestore, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliBubbleEmitterNew() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBubbleEmitterNew "); + + // Test default constructor + BubbleEmitter emitter; + DALI_TEST_CHECK( !emitter ); + + // Test object creation + Image shapeImage = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage, 200, Vector2( 5.f, 10.f )); + DALI_TEST_CHECK( emitter ); + + // Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage, 200, Vector2( 5.f, 10.f )); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + // Test copy constructor + BubbleEmitter emitterCopy( emitter ); + DALI_TEST_CHECK( emitterCopy ); + + // Test down cast + Handle handleEmitter; + handleEmitter = emitter; + BubbleEmitter downCastEmitter = BubbleEmitter::DownCast( handleEmitter ); + DALI_TEST_CHECK( downCastEmitter ); +} + +static void UtcDaliBubbleEmitterGetRootActor() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterGetRootActor " ); + + Image shapeImage = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage, 200, Vector2( 5.f, 10.f )); + + Actor root = emitter.GetRootActor(); + DALI_TEST_CHECK( root ); + DALI_TEST_CHECK( root.GetChildCount() == 3 ); +} + +static void UtcDaliBubbleEmitterSetBackground() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetBackground " ); + + Image shapeImage = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage, 200, Vector2( 5.f, 10.f )); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + unsigned int taskCount = taskList.GetTaskCount(); + + Image bgImage = CreateSolidColorImage( application, Color::RED, 50, 50 ); + emitter.SetBackground( bgImage, Vector3(0.f, 0.f, 0.5f) ); + + DALI_TEST_CHECK( taskList.GetTaskCount() == taskCount+1 ); + + Wait(application, 500); + DALI_TEST_CHECK( taskList.GetTaskCount() == taskCount ); +} + +static void UtcDaliBubbleEmitterSetShapeImage() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetShapeImage " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + Material material = bubbleMesh.GetMaterial(); + + DALI_TEST_CHECK( material.GetDiffuseTexture() == shapeImage1 ); + + Image shapeImage2 = CreateSolidColorImage( application, Color::RED, 8, 8 ); + emitter.SetShapeImage( shapeImage2 ); + + DALI_TEST_CHECK( material.GetDiffuseTexture() == shapeImage2 ); +} + +static void UtcDaliBubbleEmitterSetBubbleScale() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetBubbleScale " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + ShaderEffect effect = bubbleMesh.GetShaderEffect(); + DALI_TEST_CHECK( effect ); + + Property::Index scalePropertyIndex = effect.GetPropertyIndex( "uDynamicScale" ); + float scaleValue; + (effect.GetProperty(scalePropertyIndex)).Get( scaleValue ); + DALI_TEST_EQUALS(scaleValue, 1.f, TEST_LOCATION ); + + emitter.SetBubbleScale( 2.f ); + application.SendNotification(); + application.Render(); + (effect.GetProperty(scalePropertyIndex)).Get( scaleValue ); + DALI_TEST_EQUALS(scaleValue, 2.f, TEST_LOCATION ); + + emitter.SetBubbleScale( 0.5f ); + application.SendNotification(); + application.Render(); + (effect.GetProperty(scalePropertyIndex)).Get( scaleValue ); + DALI_TEST_EQUALS(scaleValue, 0.5f, TEST_LOCATION ); +} + +static void UtcDaliBubbleEmitterSetBubbleDensity01() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetBubbleDensity " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + try + { + emitter.SetBubbleDensity( 3.f ); + DALI_TEST_CHECK(true); + } + catch(Dali::DaliException& e) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_ASSERT(e, "density>0 && density<=9", TEST_LOCATION ); + } +} + +static void UtcDaliBubbleEmitterSetBubbleDensity02() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetBubbleDensity " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + try + { + emitter.SetBubbleDensity( 10.f ); + } + catch(Dali::DaliException& e) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_ASSERT(e, "density>0 && density<=9", TEST_LOCATION ); + } +} + +static void UtcDaliBubbleEmitterSetBlendMode() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterSetBlendMode " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + + BlendingFactor::Type srcFactorRgb, destFactorRgb, srcFactorAlpha, destFactorAlpha; + + emitter.SetBlendMode( true ); + bubbleMesh.GetBlendFunc( srcFactorRgb, destFactorRgb, srcFactorAlpha, destFactorAlpha ); + DALI_TEST_CHECK( srcFactorRgb == BlendingFactor::SRC_ALPHA ); + DALI_TEST_CHECK( destFactorRgb == BlendingFactor::ONE ); + DALI_TEST_CHECK( srcFactorAlpha == BlendingFactor::ZERO ); + DALI_TEST_CHECK( destFactorAlpha == BlendingFactor::ONE ); + + emitter.SetBlendMode( false ); + bubbleMesh.GetBlendFunc( srcFactorRgb, destFactorRgb, srcFactorAlpha, destFactorAlpha ); + DALI_TEST_CHECK( srcFactorRgb == BlendingFactor::SRC_ALPHA ); + DALI_TEST_CHECK( destFactorRgb == BlendingFactor::ONE_MINUS_SRC_ALPHA ); + DALI_TEST_CHECK( srcFactorAlpha == BlendingFactor::ONE ); + DALI_TEST_CHECK( destFactorAlpha == BlendingFactor::ONE_MINUS_SRC_ALPHA ); +} + +static void UtcDaliBubbleEmitterEmitBubble() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterEmitBubble " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + ShaderEffect effect = bubbleMesh.GetShaderEffect(); + DALI_TEST_CHECK( effect ); + + Property::Index propertyIndex0 = effect.GetPropertyIndex( "uPercentage[0]" ); + Property::Index propertyIndex1 = effect.GetPropertyIndex( "uPercentage[1]" ); + float value0, value1; + + Animation animation = Animation::New( 0.5f ); + emitter.EmitBubble( animation, Vector2(40.f,40.f), Vector2(-5.f,-5.f), Vector2(30.f,30.f) ); + emitter.EmitBubble( animation, Vector2(10.f,10.f), Vector2(5.f,5.f), Vector2(30.f,30.f) ); + (effect.GetProperty(propertyIndex0)).Get( value0 ); + (effect.GetProperty(propertyIndex1)).Get( value1 ); + DALI_TEST_EQUALS(value0, 0.f, TEST_LOCATION ); + DALI_TEST_EQUALS(value1, 0.f, TEST_LOCATION ); + + animation.Play(); + + Wait(application, 300); + (effect.GetProperty(propertyIndex0)).Get( value0 ); + (effect.GetProperty(propertyIndex1)).Get( value1 ); + DALI_TEST_CHECK( value0 >= 0.6f ); + DALI_TEST_CHECK( value1 >= 0.6f ); + + Wait(application, 600); + (effect.GetProperty(propertyIndex0)).Get( value0 ); + (effect.GetProperty(propertyIndex1)).Get( value1 ); + DALI_TEST_EQUALS(value0, 1.f, TEST_LOCATION ); + DALI_TEST_EQUALS(value1, 1.f, TEST_LOCATION ); +} + +static void UtcDaliBubbleEmitterStartExplosion() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterStartExplosion " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + ShaderEffect effect = bubbleMesh.GetShaderEffect(); + DALI_TEST_CHECK( effect ); + + Property::Index propertyIndex = effect.GetPropertyIndex( "uMagnification" ); + float value; + (effect.GetProperty(propertyIndex)).Get( value ); + DALI_TEST_EQUALS(value, 1.f, TEST_LOCATION ); + + emitter.StartExplosion( 0.4, 4.f ); + + Wait(application, 200); // 0.2s + (effect.GetProperty(propertyIndex)).Get( value ); + DALI_TEST_CHECK( value >= 2.f ); + + Wait(application, 100); // 0.3s + (effect.GetProperty(propertyIndex)).Get( value ); + DALI_TEST_CHECK( value >= 3.f ); + + Wait(application, 100); // 0.4s + (effect.GetProperty(propertyIndex)).Get( value ); + DALI_TEST_EQUALS(value, 1.f, TEST_LOCATION ); +} + +static void UtcDaliBubbleEmitterRestore() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliBubbleEmitterRestore " ); + + Image shapeImage1 = CreateSolidColorImage( application, Color::GREEN, 5, 5 ); + BubbleEmitter emitter = BubbleEmitter::New( Vector2(50.f,50.f),shapeImage1, 200, Vector2( 5.f, 10.f )); + Actor root = emitter.GetRootActor(); + MeshActor bubbleMesh = MeshActor::DownCast( root.GetChildAt( 0 ) ); + ShaderEffect effect = bubbleMesh.GetShaderEffect(); + DALI_TEST_CHECK( effect ); + + Property::Index percentagePropertyIndex = effect.GetPropertyIndex( "uPercentage[0]" ); + float percentage; + + Animation animation = Animation::New( 0.5f ); + emitter.EmitBubble( animation, Vector2(40.f,40.f), Vector2(-5.f,-5.f), Vector2(30.f,30.f) ); + (effect.GetProperty(percentagePropertyIndex)).Get( percentage ); + DALI_TEST_EQUALS(percentage, 0.f, TEST_LOCATION ); + + animation.Play(); + Wait(application, 200); + animation.Clear(); + + (effect.GetProperty(percentagePropertyIndex)).Get( percentage ); + DALI_TEST_CHECK( percentage < 0.5f && percentage >= 0.4); + + emitter.Restore(); + application.SendNotification(); + application.Render(); + + (effect.GetProperty(percentagePropertyIndex)).Get( percentage ); + DALI_TEST_EQUALS(percentage, 1.f, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/builder/.gitignore b/automated-tests/dali-test-suite/builder/.gitignore new file mode 100644 index 0000000..12fbec7 --- /dev/null +++ b/automated-tests/dali-test-suite/builder/.gitignore @@ -0,0 +1,2 @@ +utc-Dali-Builder +utc-Dali-JsonParser diff --git a/automated-tests/dali-test-suite/builder/Makefile b/automated-tests/dali-test-suite/builder/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/builder/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/builder/file.list b/automated-tests/dali-test-suite/builder/file.list new file mode 100644 index 0000000..b178c6f --- /dev/null +++ b/automated-tests/dali-test-suite/builder/file.list @@ -0,0 +1,3 @@ +TARGETS += \ + Dali/utc-Dali-Builder \ + Dali/utc-Dali-JsonParser \ diff --git a/automated-tests/dali-test-suite/builder/tslist b/automated-tests/dali-test-suite/builder/tslist new file mode 100644 index 0000000..d5d6ef9 --- /dev/null +++ b/automated-tests/dali-test-suite/builder/tslist @@ -0,0 +1,2 @@ +/dali-test-suite/builder/utc-Dali-Builder +/dali-test-suite/builder/utc-Dali-JsonParser diff --git a/automated-tests/dali-test-suite/builder/utc-Dali-Builder.cpp b/automated-tests/dali-test-suite/builder/utc-Dali-Builder.cpp new file mode 100644 index 0000000..78a4669 --- /dev/null +++ b/automated-tests/dali-test-suite/builder/utc-Dali-Builder.cpp @@ -0,0 +1,393 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +// +// Note: To avoid escaping double quotes single quotes are used and then replaced +// before parsing. JSON uses double quotes +// + std::string JSON_TEXTSTYLE_ONLY("\ +{ \ + 'text-styles': \ + { \ + 'title-text-style':{'font-name': 'Vera', \ + 'font-style': 'Bold', \ + 'point-size': 12.0, \ + 'weight': 'light', \ + 'text-color': [0.0,0.5,0.5,1], \ + 'italic': false, \ + 'underline': false, \ + 'shadow': true, \ + 'glow': true, \ + 'outline': true, \ + 'shadow-color': [0.0,1.0,0.0,1.0], \ + 'shadow-offset': [3.0,2.0], \ + 'shadow-size': 2.0, \ + 'glow-color': [0.9,0.6,0.3,1.0], \ + 'glow-intensity':0.1, \ + 'smooth-edge': 0.45, \ + 'outline-color': [1.0,0.5,0.0,1.0], \ + 'outline-thickness': [0.7,0.6] \ + } \ + } \ +} \ +"); + + std::string JSON_TEXT_ACTOR("\ +{ \ + 'styles': \ + { \ + 'basic-text': \ + { \ + 'type':'TextActor', \ + 'text':'Hello', \ + 'font':'', \ + 'parent-origin':[0.0,0.0,0], \ + 'anchor-point' :[0.5,0.5,0], \ + 'size': [150,170,1], \ + 'position':[-10,10,0] \ + } \ + }, \ + 'animations': \ + { \ + 'rotate': \ + { \ + 'duration': 10, \ + 'properties': \ + [ \ + { \ + 'actor':'text', \ + 'property':'rotation', \ + 'value':[0, 3, 0, 0], \ + 'alpha-function': 'EASE_IN_OUT', \ + 'time-period': {'delay': 0, 'duration': 3 } \ + } \ + ] \ + } \ + }, \ + 'stage': \ + [ \ + { \ + 'name':'text', \ + 'type':'basic-text', \ + 'text':'Hello' \ + } \ + ], \ + 'other': \ + [ \ + { \ + 'name':'other-text', \ + 'type':'basic-text', \ + 'text':'Hello' \ + } \ + ] \ +} \ +"); + + + std::string JSON_CORE_ACTOR_TREE("\ +{ \ + 'styles': \ + { \ + 'my-camera': { \ + 'type':'CameraActor', \ + 'camera-type':'FreeLook', \ + 'field-of-view': 0.125, \ + 'aspect-ratio':5.0, \ + 'near-plane-distance': 100, \ + 'far-plane-distance': 200 \ + }, \ + 'basic-text': { \ + 'type':'TextActor', \ + 'text':'Hello', \ + 'font':'Freesans', \ + 'smooth-edge':0.2, \ + 'position': [-10.0, 10.0, -1000.0], \ + 'size': [300.0, 250.0, 0.0] \ + }, \ + 'theme2-text': { \ + 'type':'TextActor', \ + 'text':'Hello', \ + 'font':'Freesans', \ + 'smooth-edge':0.8 \ + } \ + }, \ + 'stage': \ + [ \ + {'name':'txt1', \ + 'type':'TextActor', \ + 'text':'Hello World', \ + 'font':'freesans', \ + 'parent-origin':'CENTER', \ + 'actors': \ + [ \ + { 'type':'basic-text', 'text':'Hello', 'position-y':50 }, \ + { 'type':'basic-text', 'text':'Hello', 'position-y':100 }, \ + { 'type':'basic-text', 'text':'Hello', 'position-y':150 }, \ + { 'type':'basic-text', 'text':'Hello', 'position-y':200 }, \ + { 'type':'basic-text', 'text':'Hello', 'position-y':250 } \ + ] \ + } \ + ] \ +} \ +"); + + + std::string ReplaceQuotes(const std::string &in_s) + { + std::string s(in_s); + // wrong as no embedded quote but had regex link problems + std::replace(s.begin(), s.end(), '\'', '"'); + return s; + } + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliBuilderTextActorCreateFromStyle(); +static void UtcDaliBuilderTextActorCreateAnimation(); +static void UtcDaliBuilderTextActorApplyFromStyle(); +static void UtcDaliBuilderStyles(); +static void UtcDaliBuilderAddActorsOther(); +static void UtcDaliBuilderAddActors(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliBuilderTextActorCreateFromStyle, POSITIVE_TC_IDX }, + { UtcDaliBuilderTextActorCreateAnimation, POSITIVE_TC_IDX }, + { UtcDaliBuilderTextActorApplyFromStyle, POSITIVE_TC_IDX }, + { UtcDaliBuilderStyles, POSITIVE_TC_IDX }, + { UtcDaliBuilderAddActorsOther, POSITIVE_TC_IDX }, + { UtcDaliBuilderAddActors, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliBuilderTextActorCreateFromStyle() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliBuilderTextActorCreateFromStyle"); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_TEXT_ACTOR)); + + TextActor actor( TextActor::DownCast( builder.CreateFromStyle("basic-text") ) ); + + DALI_TEST_CHECK( actor ); + + stage.GetRootLayer().Add( actor ); + + application.SendNotification(); + application.Render(); + + Vector3 v; + + v = actor.GetCurrentPosition(); + DALI_TEST_CHECK(v.x == -10.0); + DALI_TEST_CHECK(v.y == 10.0); + DALI_TEST_CHECK(v.z == 0.0); + + v = actor.GetCurrentSize(); + DALI_TEST_CHECK(v.x == 150.0); + DALI_TEST_CHECK(v.y == 170.0); + DALI_TEST_CHECK(v.z == 1.0); + + DALI_TEST_CHECK(actor.GetText() == "Hello"); + + actor = TextActor::DownCast( builder.CreateFromStyle("*(&^") ); + DALI_TEST_CHECK(!actor); + +} + +static void UtcDaliBuilderTextActorCreateAnimation() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBuilderTextActorCreateAnimation"); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_TEXT_ACTOR)); + + builder.AddActors( Stage::GetCurrent().GetRootLayer() ); + + Animation anim = builder.CreateAnimation("rotate"); + DALI_TEST_CHECK( anim ); + + DALI_TEST_CHECK( 10.0f == anim.GetDuration() ); + +} + +static void UtcDaliBuilderTextActorApplyFromStyle() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBuilderTextActorApplyFromStyle"); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_TEXT_ACTOR)); + + TextActor actor = TextActor::New("a"); + + builder.ApplyStyle("basic-text", actor); + + DALI_TEST_CHECK( actor ); + + Stage::GetCurrent().GetRootLayer().Add( actor ); + + application.SendNotification(); + application.Render(); + + Vector3 v; + + v = actor.GetCurrentPosition(); + DALI_TEST_CHECK(v.x == -10.0); + DALI_TEST_CHECK(v.y == 10.0); + DALI_TEST_CHECK(v.z == 0.0); + + v = actor.GetCurrentSize(); + DALI_TEST_CHECK(v.x == 150.0); + DALI_TEST_CHECK(v.y == 170.0); + DALI_TEST_CHECK(v.z == 1.0); + + DALI_TEST_CHECK(actor.GetText() == "Hello"); + +} + +static void UtcDaliBuilderAddActors() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBuilderAddActors"); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_TEXT_ACTOR)); + + builder.AddActors( Stage::GetCurrent().GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + TextActor actor = TextActor::DownCast( Stage::GetCurrent().GetRootLayer().FindChildByName("text") ); + + DALI_TEST_CHECK( actor ); + DALI_TEST_CHECK(actor.GetText() == "Hello"); + +} + +static void UtcDaliBuilderAddActorsOther() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBuilderAddActorsOther"); + + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_TEXT_ACTOR)); + + builder.AddActors( "other", rootActor ); + + application.SendNotification(); + application.Render(); + + TextActor actor = TextActor::DownCast( Stage::GetCurrent().GetRootLayer().FindChildByName("other-text") ); + + DALI_TEST_CHECK( actor ); + DALI_TEST_CHECK(actor.GetText() == "Hello"); + +} + + +static void UtcDaliBuilderStyles() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliBuilderStyles"); + + Builder builder = Builder::New(); + + builder.LoadFromString(ReplaceQuotes(JSON_CORE_ACTOR_TREE)); + + BaseHandle handle = builder.CreateFromStyle("my-camera"); + CameraActor camera = CameraActor::DownCast(handle); + + DALI_TEST_CHECK(camera); + + Property::Value v; + + v = camera.GetProperty( camera.GetPropertyIndex("field-of-view") ); + DALI_TEST_CHECK( 0.125f == v.Get() ); + + v = camera.GetProperty( camera.GetPropertyIndex("aspect-ratio") ); + DALI_TEST_CHECK( 5.0f == v.Get() ); + + handle = builder.CreateFromStyle("basic-text"); + TextActor textActor = TextActor::DownCast(handle); + + v = textActor.GetProperty( textActor.GetPropertyIndex("smooth-edge") ); + + DALI_TEST_CHECK( 0.2f == v.Get() ); + + // test ApplyStyle another + builder.ApplyStyle("theme2-text", textActor); + + v = textActor.GetProperty( textActor.GetPropertyIndex("smooth-edge") ); + DALI_TEST_CHECK( 0.8f == v.Get() ); + +} diff --git a/automated-tests/dali-test-suite/builder/utc-Dali-JsonParser.cpp b/automated-tests/dali-test-suite/builder/utc-Dali-JsonParser.cpp new file mode 100644 index 0000000..0fd1aca --- /dev/null +++ b/automated-tests/dali-test-suite/builder/utc-Dali-JsonParser.cpp @@ -0,0 +1,640 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +TEST_FUNCTION( UtcDaliJsonParserMethod01, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod03, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod04, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod05, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod06, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod07, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod08, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod09, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliJsonParserMethod10, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +std::string ReplaceQuotes(const std::string &in_s) +{ + std::string s(in_s); + // wrong as no embedded quote but had regex link problems + std::replace(s.begin(), s.end(), '\'', '"'); + return s; +} + + +static void UtcDaliJsonParserMethod01() +{ + ToolkitTestApplication application; + + tet_infoline("JSON basic test"); + + std::string s1( ReplaceQuotes("\ +{ \ + 'string':'value2', \ + 'integer':2, \ + 'float':2.0, \ + 'boolean':true, \ + 'nil':null, \ + 'array':[1,2,3], \ + 'object':{'key':'value'} \ +} \ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + + DALI_TEST_CHECK(!parser.ParseError()); + + const TreeNode* root = parser.GetRoot(); + + DALI_TEST_CHECK(root); + + DALI_TEST_CHECK(root->Size()); + + TreeNode::ConstIterator iter = root->CBegin(); + DALI_TEST_CHECK(iter != root->CEnd()); + + const TreeNode* node = NULL; + + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::STRING); + DALI_TEST_CHECK(std::string((*iter).first) == std::string("string")); + DALI_TEST_CHECK(std::string(node->GetString()) == std::string("value2")); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::INTEGER); + DALI_TEST_CHECK(std::string((*iter).first) == std::string("integer")); + DALI_TEST_CHECK(node->GetInteger() == 2); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::FLOAT); + DALI_TEST_CHECK(std::string((*iter).first) == std::string("float")); + DALI_TEST_CHECK(node->GetFloat() == 2.0); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::BOOLEAN); + DALI_TEST_CHECK(std::string((*iter).first) == std::string("boolean")); + DALI_TEST_CHECK(node->GetBoolean()); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::IS_NULL); + DALI_TEST_CHECK(std::string((*iter).first) == std::string("nil")); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::ARRAY); + DALI_TEST_CHECK(node->Size() == 3); + TreeNode::ConstIterator iterArray = node->CBegin(); + + DALI_TEST_CHECK(iterArray != node->CEnd()); + DALI_TEST_CHECK( ((*iterArray).second).GetType() == TreeNode::INTEGER); + DALI_TEST_CHECK( (*iterArray).first == NULL ); + DALI_TEST_CHECK( ((*iterArray).second).GetInteger() == 1); + + ++iterArray; + DALI_TEST_CHECK(iterArray != node->CEnd()); + DALI_TEST_CHECK( ((*iterArray).second).GetType() == TreeNode::INTEGER); + DALI_TEST_CHECK( (*iterArray).first == NULL ); + DALI_TEST_CHECK( ((*iterArray).second).GetInteger() == 2); + + ++iterArray; + DALI_TEST_CHECK(iterArray != node->CEnd()); + DALI_TEST_CHECK( ((*iterArray).second).GetType() == TreeNode::INTEGER); + DALI_TEST_CHECK( (*iterArray).first == NULL ); + DALI_TEST_CHECK( ((*iterArray).second).GetInteger() == 3); + + ++iter; + DALI_TEST_CHECK(iter != root->CEnd()); + node = &((*iter).second); + DALI_TEST_CHECK(node); + DALI_TEST_CHECK(node->GetType() == TreeNode::OBJECT); + DALI_TEST_CHECK(node->Size() == 1); + + TreeNode::ConstIterator iterObject = node->CBegin(); + DALI_TEST_CHECK(iterObject != node->CEnd()); + DALI_TEST_CHECK( ((*iterObject).second).GetType() == TreeNode::STRING); + DALI_TEST_CHECK( std::string((*iterObject).first) == std::string("key" )); + DALI_TEST_CHECK( std::string(((*iterObject).second).GetString()) == std::string("value")); + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod02() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Comments"); + + std::string s1( ReplaceQuotes(" \ +// some comments with empty line above \n\ +{ \ + // inline comments \n\ + 'key':'value', // endline comments \n\ + // more inline comments \n\ + 'key2':'value2' \ +} \ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + + DALI_TEST_CHECK(!parser.ParseError()); + + const TreeNode* root = parser.GetRoot(); + + DALI_TEST_CHECK(root); + + DALI_TEST_CHECK(root->Size()); + + const TreeNode& node = (*root->CBegin()).second; + + DALI_TEST_CHECK(node.GetType() == TreeNode::STRING); + + DALI_TEST_CHECK(node.GetString() == std::string("value")); + + DALI_TEST_CHECK((*root->CBegin()).first == std::string("key")); + + tet_result(TET_PASS); +} + + +static void UtcDaliJsonParserMethod03() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Empty line comment"); + + std::string s1( ReplaceQuotes( +"/*\n" \ +"c comment\n" \ +"*/"\ +"// next empty line comment\n"\ +"//\n"\ +"{\n"\ +" 'key':'value'\n"\ +"}\n"\ +)); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + + DALI_TEST_CHECK(!parser.ParseError()); + + const TreeNode* root = parser.GetRoot(); + + DALI_TEST_CHECK(root); + + DALI_TEST_CHECK(root->Size()); + + const TreeNode& node = (*root->CBegin()).second; + + DALI_TEST_CHECK(node.GetType() == TreeNode::STRING); + + DALI_TEST_CHECK(node.GetString() == std::string("value")); + + DALI_TEST_CHECK((*root->CBegin()).first == std::string("key")); + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod04() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Merge"); + + std::string s1( ReplaceQuotes(" \ +{ \ + 'animations': \ + { \ + 'bump': \ + { \ + 'properties': \ + [ \ + { \ + 'actor':'bump-image', \ + 'property':'uLightPosition', \ + 'value':[0.8, 0.0, -1.5], \ + 'alpha-function': 'BOUNCE', \ + 'time-period': { 'duration': 2.5 } \ + } \ + ] \ + } \ + } \ +} \ +")); + + std::string s2( ReplaceQuotes(" \ +{ \ + 'animations': \ + { \ + 'bump': \ + { \ + 'duration': 5.0, \ + 'loop': true, \ + 'end-action':'DISCARD' \ + } \ + } \ +} \ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + DALI_TEST_CHECK(!parser.ParseError()); + + parser.Parse( s2 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + + DALI_TEST_CHECK(!parser.ParseError()); + + const TreeNode* root = parser.GetRoot(); + DALI_TEST_CHECK(root); + + const TreeNode *node = root->Find("bump"); + DALI_TEST_CHECK(node); + + DALI_TEST_CHECK(static_cast(node->Size()) == 4); + + DALI_TEST_CHECK( node->GetChild("duration") ); + DALI_TEST_CHECK( node->GetChild("loop") ); + DALI_TEST_CHECK( node->GetChild("properties") ); + + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod05() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Pack & Write"); + + std::string s1( ReplaceQuotes(" \ +{ \ + 'animations': \ + { \ + 'bump': \ + { \ + 'properties': \ + [ \ + { \ + 'actor':'bump-image', \ + 'property':'uLightPosition', \ + 'value':[0.8, 0.0, -1.5], \ + 'alpha-function': 'BOUNCE', \ + 'time-period': { 'duration': 2.5 } \ + } \ + ] \ + } \ + } \ +} \ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + if(parser.ParseError()) + { + std::cout << "Error: " << parser.GetErrorDescription() << std::endl; + std::cout << " at: " << parser.GetErrorLineNumber() << "(" << parser.GetErrorPosition() << ")" << std::endl; + } + DALI_TEST_CHECK(!parser.ParseError()); + + std::stringstream a; + parser.Write(a, 2); + + parser.Pack(); + + std::stringstream b; + parser.Write(b, 2); + + DALI_TEST_CHECK( a.str() == b.str() ); + + tet_result(TET_PASS); +} + + +static const int NUMBER_OK_TESTS = 36; +char *TEST_OK[NUMBER_OK_TESTS] = { + "{ 'hex': '\u0123\u4567\u89AB\uCDEF\uabcd\uef4A' }", + "{ 'special': '`1~!@#$%^&*()_+-={:[,]}|;.?' }", + "{ 'slash': '/ & \' }", + "{'object with 1 member':['array with 1 element']}", + "[{}, [], -42, true, false, null]", + "{ 'integer': 1234567890 }", + "{ 'integer': 1234567890 }", + "{ 'real': -9876.543210 }", + "{ 'e': 0.123456789e-12 }", + "{ 'E': 1.234567890E+34 }", + "{ '': 23456789012E66 }", + "{ 'zero': 0 }", + "{ 'one': 1 }", + "{ 'space': ' ' }", + "{ 'backslash': '\' }", + "{ 'controls': '\\b\\f\\n\\r\\t' }", + "{ 'alpha': 'abcdefghijklmnopqrstuvwyz' }", + "{ 'ALPHA': 'ABCDEFGHIJKLMNOPQRSTUVWYZ' }", + "{ 'digit': '0123456789' }", + "{ '0123456789': 'digit' }", + "{ 'true': true }", + "{ 'false': false }", + "{ 'null': null }", + "{ 'array':[ ] }", + "{ 'object':{ } }", + "{ 'address': '1 Communication Centre. South Street' }", + "{ 'url': 'http://www.JSON.org/' }", + "{ 'comment': '// /* */': ' ' }", + "{ ' s p a c e d ' :[1,2 , 3,4 , 5 , 6 ,7 ]}", + "{ 'compact':[1,2,3,4,5,6,7]}", + "{ 'quotes': '" \\u0022 %22 0x22 034 "' }", + "{ '\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:': 'A key can be any string'}", + "[ 0.5 ,98.6, 99.44,1066,1e1,0.1e1,1e-1,1e00,2e+00,2e-00, 'rosebud']", + "{'JSON Test Pattern pass3': { 'The outermost value': 'must be an object or array.', 'In this test': 'It is an object.' } }", + "[[[[[[[[[[[[[[[[[[['Not too deep']]]]]]]]]]]]]]]]]]]", +}; + + +static void UtcDaliJsonParserMethod06() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Parse Success"); + + JsonParser parser = JsonParser::New(); + + for(int i = 0; i < NUMBER_OK_TESTS; ++i) + { + parser = JsonParser::New(); + + parser.Parse( ReplaceQuotes(TEST_OK[i]) ); + + if(parser.ParseError()) + { + tet_printf("Valid JSON parse test %d Failed", i); + tet_printf("%s", ReplaceQuotes(TEST_OK[i]).c_str()); + + tet_printf("JSON Error %d:%d: %s (%d)", parser.GetErrorLineNumber(), parser.GetErrorColumn(), parser.GetErrorDescription().c_str(), parser.GetErrorPosition()); + } + + DALI_TEST_CHECK(!parser.ParseError()); + } + + tet_result(TET_PASS); +} + + +static const int NUMBER_FAIL_TESTS = 32; +char *TEST_FAIL[] = { + "[' tab\t character \t in\t string ']", + "['Extra close']]", + "['Colon instead of comma': false]", + "{'Numbers cannot have leading zeroes': 013}", + "['Bad value', truth]", + "['Illegal backslash escape: \017']", + "['Bad value', truth]['Illegal backslash escape: \017']", + "{'Comma instead if closing brace': true,", + "{'Double colon':: null}", + "{'Extra comma': true,}", + "['Unclosed array'", + "{'Illegal invocation': alert()}", + "{'Missing colon' null}", + "[0e]", + "{unquoted_key: 'keys must be quoted'}", + "'A JSON payload should be an object or array, not a string.'", + "[\naked]", + "{'Illegal expression': 1 + 2}", + "{'Extra value after close': true} 'misplaced quoted value'", + "[0e+]", + "[+23456789012E66]", + "['extra comma',]", + "['Comma after the close'],", + "['double extra comma',,]", + "['Illegal backslash escape: \x15']", + "['line\nbreak']", + "{'Comma instead of colon', null}", + "['mismatch'}", + "['line\nbreak']", + "[0e+-1]", + "{'Numbers cannot be hex': 0x14}", + "[ , '<-- missing value']", +}; + +static void UtcDaliJsonParserMethod07() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Fail"); + + JsonParser parser = JsonParser::New(); + + for(int i = 0; i < NUMBER_FAIL_TESTS; ++i) + { + parser = JsonParser::New(); + + parser.Parse( ReplaceQuotes(TEST_FAIL[i]) ); + + if(!parser.ParseError()) + { + tet_printf("Invalid JSON parse test %d Failed", i); + tet_printf("%s", ReplaceQuotes(TEST_FAIL[i]).c_str()); + tet_printf("JSON Error %d:%d %s (%s)", parser.GetErrorLineNumber(), parser.GetErrorColumn(), + parser.GetErrorDescription().c_str(), parser.GetErrorPosition()); + } + + DALI_TEST_CHECK(parser.ParseError()); + } + + + parser = JsonParser::New(); + + parser.Parse( "['single quote']" ); + + if(!parser.ParseError()) + { + tet_printf("['single quote']"); + } + + DALI_TEST_CHECK(parser.ParseError()); + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod08() +{ + ToolkitTestApplication application; + + tet_infoline("JSON error reporting"); + + std::string s1( ReplaceQuotes("\ +{ \n\ + 'float':,], \n\ +} \n\ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + DALI_TEST_CHECK(parser.ParseError()); + + DALI_TEST_CHECK(1 == parser.GetErrorLineNumber()); + DALI_TEST_CHECK(53 == parser.GetErrorPosition()); + DALI_TEST_CHECK(11 == parser.GetErrorColumn()); + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod09() +{ + ToolkitTestApplication application; + + tet_infoline("JSON Pack()"); + + std::string s1( ReplaceQuotes("\ +{ \ + 'string':'value2', \ + 'integer':2, \ + 'float':2.3, \ + 'boolean':true, \ + 'nil':null, \ + 'array':[1,2,3], \ + 'object':{'key':'value'} \ +} \ +")); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + std::stringstream ss1; + parser.Write(ss1, 2); + + parser.Pack(); // Pack() moves strings + + std::stringstream ss2; + parser.Write(ss2, 2); + + DALI_TEST_CHECK(ss1.str() == ss2.str()); + + tet_result(TET_PASS); +} + +static void UtcDaliJsonParserMethod10() +{ + ToolkitTestApplication application; + + tet_infoline("JSON basic test"); + + std::string s1( "" ); + + JsonParser parser = JsonParser::New(); + + parser.Parse( s1 ); + + DALI_TEST_CHECK(parser.ParseError()); + + tet_result(TET_PASS); +} diff --git a/automated-tests/dali-test-suite/buttons/.gitignore b/automated-tests/dali-test-suite/buttons/.gitignore new file mode 100644 index 0000000..f4f0d52 --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/.gitignore @@ -0,0 +1,3 @@ +utc-Dali-CheckBoxButton +utc-Dali-PushButton +utc-Dali-Button diff --git a/automated-tests/dali-test-suite/buttons/Makefile b/automated-tests/dali-test-suite/buttons/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/buttons/file.list b/automated-tests/dali-test-suite/buttons/file.list new file mode 100644 index 0000000..3a8cef4 --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/file.list @@ -0,0 +1,4 @@ +TARGETS += \ + utc-Dali-CheckBoxButton \ + utc-Dali-PushButton \ + utc-Dali-Button \ diff --git a/automated-tests/dali-test-suite/buttons/tslist b/automated-tests/dali-test-suite/buttons/tslist new file mode 100644 index 0000000..b8108d0 --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/tslist @@ -0,0 +1,3 @@ +/dali-test-suite/buttons/utc-Dali-CheckBoxButton +/dali-test-suite/buttons/utc-Dali-PushButton +/dali-test-suite/buttons/utc-Dali-Button diff --git a/automated-tests/dali-test-suite/buttons/utc-Dali-Button.cpp b/automated-tests/dali-test-suite/buttons/utc-Dali-Button.cpp new file mode 100644 index 0000000..41fe94a --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/utc-Dali-Button.cpp @@ -0,0 +1,397 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +static bool gButtonClicked = false; + +static bool ButtonClicked( Button button ) +{ + gButtonClicked = true; + return false; +} + +const Dali::TouchPoint pointDownInside( 0, TouchPoint::Down, 240, 400 ); +const Dali::TouchPoint pointUpInside( 0, TouchPoint::Up, 240, 400 ); +const Dali::TouchPoint pointLeave( 0, TouchPoint::Leave, 240, 400 ); +const Dali::TouchPoint pointEnter( 0, TouchPoint::Motion, 240, 400 ); +const Dali::TouchPoint pointDownOutside( 0, TouchPoint::Down, 10, 10 ); +const Dali::TouchPoint pointUpOutside( 0, TouchPoint::Up, 10, 10 ); + +static bool gObjectCreatedCallBackCalled; + +static void TestObjectCreatedCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +static float ANIMATION_TIME( 0.5f ); +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliButtonNew(); +static void UtcDaliButtonSetProperty(); +static void UtcDaliButtonSetGetDimmed(); +static void UtcDaliButtonSize(); +static void UtcDaliButtonClicked(); +static void UtcDaliButtonConnectSignal(); +static void UtcDaliButtonSetGetAnimationTime(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliButtonNew, POSITIVE_TC_IDX }, + { UtcDaliButtonSetProperty, POSITIVE_TC_IDX }, + { UtcDaliButtonSetGetDimmed, POSITIVE_TC_IDX }, + { UtcDaliButtonSize, POSITIVE_TC_IDX }, + { UtcDaliButtonClicked, POSITIVE_TC_IDX }, + { UtcDaliButtonConnectSignal, POSITIVE_TC_IDX }, + { UtcDaliButtonSetGetAnimationTime, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void UtcDaliButtonNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliButtonNew"); + + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + + DALI_TEST_CHECK( checkBoxButton ); + + PushButton pushButton = PushButton::New(); + + DALI_TEST_CHECK( pushButton ); + + CheckBoxButton checkBoxButton2( checkBoxButton ); + + DALI_TEST_CHECK( checkBoxButton2 ); + + PushButton pushButton2( pushButton ); + + DALI_TEST_CHECK( pushButton2 ); + + checkBoxButton2 = NULL; + pushButton2 = NULL; + + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestObjectCreatedCallback ); + { + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestObjectCreatedCallback ); + { + PushButton pushButton = PushButton::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + // Test down cast + Handle handleButton; + handleButton = pushButton; + Button downCastPushButton = Button::DownCast( handleButton ); + DALI_TEST_CHECK( downCastPushButton ); + PushButton downCastPushButton2 = PushButton::DownCast( handleButton ); + DALI_TEST_CHECK( downCastPushButton2 ); + + handleButton = checkBoxButton; + Button downCastCheckBoxButton = Button::DownCast( handleButton ); + DALI_TEST_CHECK( downCastCheckBoxButton ); + CheckBoxButton downCastCheckBoxButton2 = CheckBoxButton::DownCast( handleButton ); + DALI_TEST_CHECK( downCastCheckBoxButton2 ); +} + +static void UtcDaliButtonSetProperty() +{ + tet_infoline("UtcDaliButtonSetProperty: "); + ToolkitTestApplication application; + + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + PushButton pushButton = PushButton::New(); + + //Test various properties + checkBoxButton.SetProperty(checkBoxButton.GetPropertyIndex(Button::PROPERTY_DIMMED), false); + DALI_TEST_CHECK( false == checkBoxButton.IsDimmed() ); + checkBoxButton.SetProperty(checkBoxButton.GetPropertyIndex(Button::PROPERTY_DIMMED), true); + DALI_TEST_CHECK( true == checkBoxButton.IsDimmed() ); + + pushButton.SetProperty(pushButton.GetPropertyIndex(Button::PROPERTY_DIMMED), false); + DALI_TEST_CHECK( false == pushButton.IsDimmed() ); + pushButton.SetProperty(pushButton.GetPropertyIndex(Button::PROPERTY_DIMMED), true); + DALI_TEST_CHECK( true == pushButton.IsDimmed() ); +} + +static void UtcDaliButtonSetGetDimmed() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliButtonSetGetDimmed"); + + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + PushButton pushButton = PushButton::New(); + + checkBoxButton.SetDimmed( true ); + pushButton.SetDimmed( true ); + + DALI_TEST_CHECK( checkBoxButton.IsDimmed() ); + DALI_TEST_CHECK( pushButton.IsDimmed() ); + + checkBoxButton.SetDimmed( false ); + pushButton.SetDimmed( false ); + + DALI_TEST_CHECK( !checkBoxButton.IsDimmed() ); + DALI_TEST_CHECK( !pushButton.IsDimmed() ); + + checkBoxButton.SetDimmed( true ); + pushButton.SetDimmed( true ); + + DALI_TEST_CHECK( checkBoxButton.IsDimmed() ); + DALI_TEST_CHECK( pushButton.IsDimmed() ); + + checkBoxButton.SetDimmed( false ); + pushButton.SetDimmed( false ); + + DALI_TEST_CHECK( !checkBoxButton.IsDimmed() ); + DALI_TEST_CHECK( !pushButton.IsDimmed() ); +} + +static void UtcDaliButtonSize() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliButtonSize"); + + // Creates 100x50 images. + ImageActor image01 = CreateSolidColorActor( Color::RED ); + image01.SetSize( 100, 50 ); + + CheckBoxButton checkBoxButton; + PushButton pushButton; + + Vector3 size; + + // Test1 Size is set through Actor API + + // First an image is set, then SetSize is called. + pushButton = PushButton::New(); + + pushButton.SetBackgroundImage( image01 ); + pushButton.SetSize( 10.f, 10.f ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 10.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 10.f, TEST_LOCATION ); +} + +static void UtcDaliButtonClicked() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliButtonClicked"); + + PushButton pushButton = PushButton::New(); + pushButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pushButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pushButton.SetPosition( 240, 400 ); + pushButton.SetSize( 100, 100 ); + + Stage::GetCurrent().Add( pushButton ); + + application.SendNotification(); + application.Render(); + + // connect to its touch signal + pushButton.ClickedSignal().Connect( &ButtonClicked ); + + Dali::Integration::TouchEvent event; + + // Test1. Touch point down and up inside the button. + + gButtonClicked = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gButtonClicked ); + + // Test2. Touch point down and up outside the button. + + gButtonClicked = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gButtonClicked ); + + // Test3. Touch point down inside and up outside the button. + + gButtonClicked = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointLeave ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gButtonClicked ); + + // Test4. Touch point down outside and up inside the button. + + gButtonClicked = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointEnter ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gButtonClicked ); +} + +static bool gClickedCallBackCalled; + +static bool TestClickedCallback(Button button) +{ + gClickedCallBackCalled = true; + return true; +} + +static void UtcDaliButtonConnectSignal() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliButtonConnectSignal()"); + + gClickedCallBackCalled = false; + + PushButton pushButton = PushButton::New(); + pushButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pushButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pushButton.SetPosition( 240, 400 ); + pushButton.SetSize( 100, 100 ); + + Stage::GetCurrent().Add( pushButton ); + + application.SendNotification(); + application.Render(); + + // connect to its clicked signal + pushButton.ClickedSignal().Connect(TestClickedCallback); + + Dali::Integration::TouchEvent event; + + // Touch point down and up inside the button. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gClickedCallBackCalled == true ); + + gClickedCallBackCalled = false; + pushButton.ClickedSignal().Disconnect(TestClickedCallback); + + // simulate another touch event + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gClickedCallBackCalled == false ); +} + +static void UtcDaliButtonSetGetAnimationTime() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliButtonSetGetAnimationTime"); + + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + PushButton pushButton = PushButton::New(); + + checkBoxButton.SetAnimationTime( ANIMATION_TIME ); + pushButton.SetAnimationTime( ANIMATION_TIME ); + + DALI_TEST_EQUALS( checkBoxButton.GetAnimationTime(), ANIMATION_TIME, TEST_LOCATION ); + DALI_TEST_EQUALS( pushButton.GetAnimationTime(), ANIMATION_TIME, TEST_LOCATION ); + +} diff --git a/automated-tests/dali-test-suite/buttons/utc-Dali-CheckBoxButton.cpp b/automated-tests/dali-test-suite/buttons/utc-Dali-CheckBoxButton.cpp new file mode 100644 index 0000000..ec5fcda --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/utc-Dali-CheckBoxButton.cpp @@ -0,0 +1,230 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +Image CreateSolidColorImage( const Vector4& color, unsigned int width, unsigned int height ) +{ + BitmapImage imageData = BitmapImage::New( width, height, Pixel::RGBA8888 ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + unsigned int size = width * height; + + for( size_t i = 0; i < size; i++ ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + + imageData.Update(); + + return imageData; +} + +static bool gCheckBoxButtonState = false; +bool CheckBoxButtonClicked( Button button ) +{ + gCheckBoxButtonState = static_cast( button ).IsChecked(); + return true; +} +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliCheckBoxButtonSetGetChecked, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCheckBoxButtonSetImages, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliCheckBoxButtonSetGetChecked() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCheckBoxButtonSetGetChecked"); + + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + checkBoxButton.ClickedSignal().Connect( &CheckBoxButtonClicked ); + + // global var used to check if CheckBoxButtonClicked is called; + gCheckBoxButtonState = false; + + checkBoxButton.SetChecked( true ); + + DALI_TEST_CHECK( checkBoxButton.IsChecked() ); + DALI_TEST_CHECK( gCheckBoxButtonState ); + + checkBoxButton.SetChecked( false ); + + DALI_TEST_CHECK( !checkBoxButton.IsChecked() ); + DALI_TEST_CHECK( !gCheckBoxButtonState ); + + checkBoxButton.SetChecked( true ); + + DALI_TEST_CHECK( checkBoxButton.IsChecked() ); + DALI_TEST_CHECK( gCheckBoxButtonState ); +} + +static void UtcDaliCheckBoxButtonSetImages() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCheckBoxButtonSetImages"); + + Actor imageActor; + + Image image01 = CreateSolidColorImage( Color::RED, 10, 10 ); + ImageActor imageActor01 = CreateSolidColorActor( Color::RED ); + imageActor01.SetSize( 20, 20 ); + + Image image02 = CreateSolidColorImage( Color::RED, 30, 30 ); + ImageActor imageActor02 = CreateSolidColorActor( Color::RED ); + imageActor02.SetSize( 40, 40 ); + + Image image03 = CreateSolidColorImage( Color::RED, 50, 50 ); + ImageActor imageActor03 = CreateSolidColorActor( Color::RED ); + imageActor03.SetSize( 60, 60 ); + + Image image04 = CreateSolidColorImage( Color::RED, 70, 70 ); + ImageActor imageActor04 = CreateSolidColorActor( Color::RED ); + imageActor04.SetSize( 80, 80 ); + + Vector3 size; + CheckBoxButton checkBoxButton = CheckBoxButton::New(); + + application.SendNotification(); + application.Render(); + + // Just check if check box button size changes when a bigger image is set. + + checkBoxButton.SetBackgroundImage( image01 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 10.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 10.f, TEST_LOCATION ); + + checkBoxButton.SetBackgroundImage( imageActor01 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 20.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 20.f, TEST_LOCATION ); + + checkBoxButton.SetCheckedImage( image02 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetCheckedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 30.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 30.f, TEST_LOCATION ); + + checkBoxButton.SetCheckedImage( imageActor02 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetCheckedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 40.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 40.f, TEST_LOCATION ); + + checkBoxButton.SetDimmedBackgroundImage( image03 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetDimmedBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 50.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 50.f, TEST_LOCATION ); + + checkBoxButton.SetDimmedBackgroundImage( imageActor03 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetDimmedBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 60.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 60.f, TEST_LOCATION ); + + checkBoxButton.SetDimmedCheckedImage( image04 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetDimmedCheckedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 70.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 70.f, TEST_LOCATION ); + + checkBoxButton.SetDimmedCheckedImage( imageActor04 ); + + application.SendNotification(); + application.Render(); + + size = checkBoxButton.GetDimmedCheckedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 80.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 80.f, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/buttons/utc-Dali-PushButton.cpp b/automated-tests/dali-test-suite/buttons/utc-Dali-PushButton.cpp new file mode 100644 index 0000000..5e13b83 --- /dev/null +++ b/automated-tests/dali-test-suite/buttons/utc-Dali-PushButton.cpp @@ -0,0 +1,1263 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +Image CreateSolidColorImage( const Vector4& color, unsigned int width, unsigned int height ) +{ + BitmapImage imageData = BitmapImage::New( width, height, Pixel::RGBA8888 ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + unsigned int size = width * height; + + for( size_t i = 0; i < size; i++ ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + + imageData.Update(); + + return imageData; +} + +static bool gPushButtonToggleState = false; +bool PushButtonToggled( Button button, bool toggled ) +{ + gPushButtonToggleState = toggled && ( toggled == static_cast( button ).IsToggled() ); + return true; +} + +static bool gPushButtonPressed = false; + +static bool PushButtonPressed( Button button ) +{ + gPushButtonPressed = true; + return true; +} + +static bool gPushButtonReleased = false; + +static bool PushButtonReleased( Button button ) +{ + gPushButtonReleased = true; + return true; +} + +const Dali::TouchPoint pointDownInside( 0, TouchPoint::Down, 240, 400 ); +const Dali::TouchPoint pointUpInside( 0, TouchPoint::Up, 240, 400 ); +const Dali::TouchPoint pointLeave( 0, TouchPoint::Leave, 240, 400 ); +const Dali::TouchPoint pointEnter( 0, TouchPoint::Motion, 240, 400 ); +const Dali::TouchPoint pointMotionOut( 0, TouchPoint::Motion, 10, 10 ); +const Dali::TouchPoint pointDownOutside( 0, TouchPoint::Down, 10, 10 ); +const Dali::TouchPoint pointUpOutside( 0, TouchPoint::Up, 10, 10 ); +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +////////////////////////////////////////////////////////// + +namespace +{ +static bool gOnTouchPointInterrupted = false; +} //namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +class TETButton; +} + +/** + * Creates a Button to test if interrupt events are handled correctly. + */ +class TETButton : public Button +{ +public: + // PushButton Pressed + typedef SignalV2< bool ( Button ) > PressedSignalV2; + + PressedSignalV2& PressedSignal(); + + /** + * Default constructor. + */ + TETButton(); + + /** + * Copy constructor. + */ + TETButton( const Button& button ); + + /** + * Assignment operator. + */ + TETButton& operator=( const TETButton& button ); + + /** + * Creates and initializes a new button. + */ + static TETButton New(); + + /** + * Down cast to TETButton. + */ + static TETButton DownCast( BaseHandle handle ); + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + TETButton( Internal::TETButton& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + TETButton( Dali::Internal::CustomActor* internal ); +}; + +namespace Internal +{ + +/** + * Internal implementation + */ +class TETButton : public Button +{ +public: + /** + * Construct a new Button. + */ + TETButton(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~TETButton(); + + /** + * Creates an internal button. + */ + static Toolkit::TETButton New(); + + /** + * @return the pressed signal. + */ + Toolkit::TETButton::PressedSignalV2& PressedSignal(); + + /** + * Callback called when an interrupt events is received. + */ + void OnTouchPointInterrupted(); + + /** + * Callback received when a down event is received. + */ + void OnButtonDown(); + + Toolkit::TETButton::PressedSignalV2 mPressedSignal; ///< Signal emitted when the button is pressed. +}; + +} // namespace Internal + +TETButton::TETButton() +{ +} + +TETButton::TETButton( const Button& button ) +: Button( button ) +{ +} + +TETButton& TETButton::operator=( const TETButton& button ) +{ + if( &button != this ) + { + Button::operator=( button ); + } + return *this; +} + +TETButton TETButton::New() +{ + return Internal::TETButton::New(); +} + +TETButton TETButton::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +TETButton::PressedSignalV2& TETButton::PressedSignal() +{ + TETButton button( *this ); + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ).PressedSignal(); +} + +TETButton::TETButton( Internal::TETButton& implementation ) +: Button( implementation ) +{} + +TETButton::TETButton( Dali::Internal::CustomActor* internal ) +: Button( internal ) +{ + VerifyCustomActorPointer(internal); +} + +namespace Internal +{ + +TETButton::TETButton() +: Button(), + mPressedSignal() +{ +} + +TETButton::~TETButton() +{ +} + +Toolkit::TETButton TETButton::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< TETButton > internalTETButton = new TETButton(); + + // Pass ownership to CustomActor + Dali::Toolkit::TETButton tetButton( *internalTETButton ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalTETButton->Initialize(); + + return tetButton; +} + +Toolkit::TETButton::PressedSignalV2& TETButton::PressedSignal() +{ + return mPressedSignal; +} + +void TETButton::OnButtonDown() +{ + Toolkit::TETButton handle( GetOwner() ); + + //Emit signal. + mPressedSignal.Emit( handle ); +} + +void TETButton::OnTouchPointInterrupted() +{ + gOnTouchPointInterrupted = true; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +namespace +{ + +class TETButtonPressed : public Dali::ConnectionTracker +{ +public: + enum Test + { + SENSITIVENESS, + VISIBILITY + }; + + TETButtonPressed( Actor actor, Test test ) + : mActor( actor ), + mTest( test ) + { + } + + bool Callback( Button button ) + { + switch( mTest ) + { + case SENSITIVENESS: + { + mActor.SetSensitive( false ); + break; + } + case VISIBILITY: + { + std::cout <<"VISIBILITY false" << std::endl; + mActor.SetVisible( false ); + break; + } + default: + { + break; + } + } + return true; + } + + Actor mActor; + Test mTest; +}; + +static bool TestCallback(Actor actor, const TouchEvent& event) +{ + return true; +} + +} // namespace + +////////////////////////////////////////////////////////// + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliPushButtonSetGetAutoRepeating, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetToggleButton, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetAutoRepeatingAndToggleButton, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetToggled01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetToggled02, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetAutorepeatingDelayValues01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetGetAutorepeatingDelayValues02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetImages, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonSetLabelText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonPressed, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonReleased, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonToggled, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonInterruptEventWhenInsensitive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPushButtonInterruptEventWhenNonVisible, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliPushButtonSetGetAutoRepeating() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetAutoRepeating"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetAutoRepeating( true ); + + DALI_TEST_CHECK( pushButton.IsAutoRepeating() ); + + pushButton.SetAutoRepeating( false ); + + DALI_TEST_CHECK( !pushButton.IsAutoRepeating() ); + + pushButton.SetAutoRepeating( true ); + + DALI_TEST_CHECK( pushButton.IsAutoRepeating() ); +} + +static void UtcDaliPushButtonSetGetToggleButton() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetToggleButton"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetToggleButton( true ); + + DALI_TEST_CHECK( pushButton.IsToggleButton() ); + + pushButton.SetToggleButton( false ); + + DALI_TEST_CHECK( !pushButton.IsToggleButton() ); + + pushButton.SetToggleButton( true ); + + DALI_TEST_CHECK( pushButton.IsToggleButton() ); +} + +static void UtcDaliPushButtonSetGetAutoRepeatingAndToggleButton() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetAutoRepeatingAndToggleButton"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetAutoRepeating( true ); + pushButton.SetToggleButton( true ); + + DALI_TEST_CHECK( pushButton.IsToggleButton() ); + DALI_TEST_CHECK( !pushButton.IsAutoRepeating() ); + + pushButton.SetToggleButton( true ); + pushButton.SetAutoRepeating( true ); + + DALI_TEST_CHECK( pushButton.IsAutoRepeating() ); + DALI_TEST_CHECK( !pushButton.IsToggleButton() ); +} + +static void UtcDaliPushButtonSetGetToggled01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetToggled01"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetToggleButton( true ); + pushButton.ToggledSignal().Connect( &PushButtonToggled ); + + gPushButtonToggleState = false; + pushButton.SetToggled( true ); + + DALI_TEST_CHECK( pushButton.IsToggled() ); + DALI_TEST_CHECK( gPushButtonToggleState ); + + pushButton.SetToggled( false ); + + DALI_TEST_CHECK( !pushButton.IsToggled() ); + DALI_TEST_CHECK( !gPushButtonToggleState ); + + pushButton.SetToggled( true ); + + DALI_TEST_CHECK( pushButton.IsToggled() ); + DALI_TEST_CHECK( gPushButtonToggleState ); +} + +static void UtcDaliPushButtonSetGetToggled02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetToggled02"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetToggleButton( false ); + pushButton.ToggledSignal().Connect( &PushButtonToggled ); + + gPushButtonToggleState = false; + pushButton.SetToggled( true ); + + DALI_TEST_CHECK( !pushButton.IsToggled() ); + DALI_TEST_CHECK( !gPushButtonToggleState ); + + pushButton.SetToggled( false ); + + DALI_TEST_CHECK( !pushButton.IsToggled() ); + DALI_TEST_CHECK( !gPushButtonToggleState ); + + pushButton.SetToggled( true ); + + DALI_TEST_CHECK( !pushButton.IsToggled() ); + DALI_TEST_CHECK( !gPushButtonToggleState ); +} + +static void UtcDaliPushButtonSetGetAutorepeatingDelayValues01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetAutorepeatingDelayValues01"); + + PushButton pushButton = PushButton::New(); + + pushButton.SetAutoRepeating( true ); + + pushButton.SetInitialAutoRepeatingDelay( 1.f ); + DALI_TEST_EQUALS( pushButton.GetInitialAutoRepeatingDelay(), 1.f, TEST_LOCATION ); + + pushButton.SetNextAutoRepeatingDelay( 1.f ); + DALI_TEST_EQUALS( pushButton.GetNextAutoRepeatingDelay(), 1.f, TEST_LOCATION ); +} + +static void UtcDaliPushButtonSetGetAutorepeatingDelayValues02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetGetAutorepeatingDelayValues02"); + + PushButton pushButton = PushButton::New(); + + bool assert1( false ); + bool assert2( false ); + + pushButton.SetAutoRepeating( true ); + + try + { + pushButton.SetInitialAutoRepeatingDelay( -1.f ); + } + catch( Dali::DaliException& e ) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "initialAutoRepeatingDelay > 0.f", TEST_LOCATION); + assert1 = true; + } + + try + { + pushButton.SetNextAutoRepeatingDelay( -1.f ); + } + catch( Dali::DaliException& e ) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "nextAutoRepeatingDelay > 0.f", TEST_LOCATION); + assert2 = true; + } + + DALI_TEST_CHECK( assert1 && assert2 ); +} + +static void UtcDaliPushButtonSetImages() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetImages"); + + Actor imageActor; + + Image image01 = CreateSolidColorImage( Color::RED, 10, 10 ); + ImageActor imageActor01 = CreateSolidColorActor( Color::RED ); + imageActor01.SetSize( 20.f, 20.f ); + + Image image02 = CreateSolidColorImage( Color::RED, 30, 30 ); + ImageActor imageActor02 = CreateSolidColorActor( Color::RED ); + imageActor02.SetSize( 40.f, 40.f ); + + Image image03 = CreateSolidColorImage( Color::RED, 50, 50 ); + ImageActor imageActor03 = CreateSolidColorActor( Color::RED ); + imageActor03.SetSize( 60.f, 60.f ); + + Image image04 = CreateSolidColorImage( Color::RED, 70, 70 ); + ImageActor imageActor04 = CreateSolidColorActor( Color::RED ); + imageActor04.SetSize( 80.f, 80.f ); + + Image image05 = CreateSolidColorImage( Color::RED, 90, 90 ); + ImageActor imageActor05 = CreateSolidColorActor( Color::RED ); + imageActor05.SetSize( 100.f, 100.f ); + + Vector3 size; + PushButton pushButton = PushButton::New(); + + application.SendNotification(); + application.Render(); + + // Just check if check box button size changes when a bigger image is set. + + pushButton.SetButtonImage( image01 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetButtonImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 10.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 10.f, TEST_LOCATION ); + + pushButton.SetButtonImage( imageActor01 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetButtonImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 20.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 20.f, TEST_LOCATION ); + + pushButton.SetBackgroundImage( image02 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 30.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 30.f, TEST_LOCATION ); + + pushButton.SetBackgroundImage( imageActor02 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 40.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 40.f, TEST_LOCATION ); + + pushButton.SetPressedImage( image03 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetPressedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 50.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 50.f, TEST_LOCATION ); + + pushButton.SetPressedImage( imageActor03 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetPressedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 60.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 60.f, TEST_LOCATION ); + + pushButton.SetDimmedBackgroundImage( image04 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetDimmedBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 70.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 70.f, TEST_LOCATION ); + + pushButton.SetDimmedBackgroundImage( imageActor04 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetDimmedBackgroundImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 80.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 80.f, TEST_LOCATION ); + + pushButton.SetDimmedImage( image05 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetDimmedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 90.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 90.f, TEST_LOCATION ); + + pushButton.SetDimmedImage( imageActor05 ); + + application.SendNotification(); + application.Render(); + + size = pushButton.GetDimmedImage().GetCurrentSize(); + + DALI_TEST_EQUALS( size.width, 100.f, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, 100.f, TEST_LOCATION ); +} + +static void UtcDaliPushButtonSetLabelText() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonSetLabelText"); + + const std::string STR( "Hola!" ); + + PushButton pushButton = PushButton::New(); + + application.SendNotification(); + application.Render(); + + TextView textView; + + pushButton.SetLabelText( STR ); + + textView = TextView::DownCast( pushButton.GetLabelText() ); + DALI_TEST_CHECK( STR == textView.GetText() ); + + TextView text = TextView::New( STR ); + pushButton.SetLabelText( text ); + + textView = TextView::DownCast( pushButton.GetLabelText() ); + DALI_TEST_CHECK( STR == textView.GetText() ); +} + +static void UtcDaliPushButtonPressed() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonPressed"); + + PushButton pushButton = PushButton::New(); + pushButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pushButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pushButton.SetPosition( 240, 400 ); + pushButton.SetSize( 100, 100 ); + + Stage::GetCurrent().Add( pushButton ); + + application.SendNotification(); + application.Render(); + + gPushButtonPressed = false; + + // connect to its touch signal + pushButton.PressedSignal().Connect( &PushButtonPressed ); + + Dali::Integration::TouchEvent eventDown; + eventDown.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( eventDown ); + + DALI_TEST_CHECK( gPushButtonPressed ); +} + +static void UtcDaliPushButtonReleased() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonReleased"); + + PushButton pushButton = PushButton::New(); + pushButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pushButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pushButton.SetPosition( 240, 400 ); + pushButton.SetSize( 100, 100 ); + + Stage::GetCurrent().Add( pushButton ); + + application.SendNotification(); + application.Render(); + + // connect to its touch signal + pushButton.ReleasedSignal().Connect( &PushButtonReleased ); + + Dali::Integration::TouchEvent event; + + // Test1. Touch point down and up inside the button. + + gPushButtonReleased = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gPushButtonReleased ); + + // Test2. Touch point down and up outside the button. + + gPushButtonReleased = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonReleased ); + + // Test3. Touch point down inside and up outside the button. + + gPushButtonReleased = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointLeave ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gPushButtonReleased ); + + // Test4. Touch point down outside and up inside the button. + + gPushButtonReleased = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointEnter ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonReleased ); +} + +static void UtcDaliPushButtonToggled() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonToggled"); + + PushButton pushButton = PushButton::New(); + pushButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pushButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pushButton.SetPosition( 240, 400 ); + pushButton.SetSize( 100, 100 ); + + Stage::GetCurrent().Add( pushButton ); + + application.SendNotification(); + application.Render(); + + // connect to its touch signal + pushButton.ToggledSignal().Connect( &PushButtonToggled ); + + Dali::Integration::TouchEvent event; + + // Test1. No toggle button. + + gPushButtonToggleState = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonToggleState ); + + // Set toggle property. + pushButton.SetToggleButton( true ); + + // Test2. Touch point down and up inside the button twice. + gPushButtonToggleState = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gPushButtonToggleState ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonToggleState ); + + // Test3. Touch point down and up outside the button. + + gPushButtonToggleState = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonToggleState ); + + // Test4. Touch point down inside and up outside the button. + + gPushButtonToggleState = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointLeave ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonToggleState ); + + // Test5. Touch point down outside and up inside the button. + + gPushButtonToggleState = false; + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointEnter ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gPushButtonToggleState ); +} + +static void UtcDaliPushButtonInterruptEventWhenInsensitive() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonInterruptEventWhenInsensitive"); + + // * Creates an actor which contains a button. + // * The size of the actor is bigger than the button. + // * The button's boundary is contained in the actor's one. + Actor actor = Actor::New(); + TETButton tetButton= Toolkit::TETButton::New(); + + actor.SetName( "Actor" ); + tetButton.SetName( "TETButton" ); + + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetPosition( 0, 0 ); + actor.SetSize( 400, 800 ); + + tetButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + tetButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + tetButton.SetPosition( 240, 400 ); + tetButton.SetSize( 100, 100 ); + + actor.Add( tetButton ); + Stage::GetCurrent().Add( actor ); + + // * Actor's touch event is connected to a callback function + // and this callback function consumes the event. + actor.TouchedSignal().Connect( &TestCallback ); + + // * Button's pressed signal is connected to a callback function + // which also consumes the event. + // * Changes the sensitiveness of the button to false. + TETButtonPressed tetButtonPressed( actor, TETButtonPressed::SENSITIVENESS ); + tetButton.PressedSignal().Connect( &tetButtonPressed, &TETButtonPressed::Callback ); + + // Initializes TET state. + gOnTouchPointInterrupted = false; + tetButton.SetSensitive( true ); + + Dali::Integration::TouchEvent event; + + // TET starts. + + // Test a down point inside the button which is also consumed by the actor, and an up point + // consumed only by the actor. gOnTouchPointInterrupted should be true (Button receives an + // interrupt event. + + application.SendNotification(); + application.Render(); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + // An up event is sent outside the button's boundary but inside the actor's one. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gOnTouchPointInterrupted ); + + // Test a down point inside the button which is also consumed by the actor, and a motion point + // consumed only by the actor. gOnTouchPointInterrupted should be true (Button receives an + // interrupt event. + + // Initializes TET state. + gOnTouchPointInterrupted = false; + actor.SetSensitive( true ); + tetButton.SetSensitive( true ); + + application.SendNotification(); + application.Render(); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + // A motion event is sent outside the button's boundary but inside the actor's one. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointMotionOut ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gOnTouchPointInterrupted ); + + // Test a down point inside the button which is also consumed by the actor, and an up point + // also inside the button and consumed by the actor. gOnTouchPointInterrupted should be false. + + // Initializes TET state. + gOnTouchPointInterrupted = false; + actor.SetSensitive( true ); + tetButton.SetSensitive( true ); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + actor.SetSensitive( true ); + // An up event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gOnTouchPointInterrupted ); +} + +static void UtcDaliPushButtonInterruptEventWhenNonVisible() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliPushButtonInterruptEventWhenNonVisible"); + + // Does same test as above but changing the visibility instead the sensitiveness. + + // * Creates an actor which contains a button. + // * The size of the actor is bigger than the button. + // * The button's boundary is contained in the actor's one. + Actor actor = Actor::New(); + TETButton tetButton = Toolkit::TETButton::New(); + + actor.SetName( "Actor" ); + tetButton.SetName( "TETButton" ); + + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetPosition( 0, 0 ); + actor.SetSize( 400, 800 ); + + tetButton.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + tetButton.SetParentOrigin( ParentOrigin::TOP_LEFT ); + tetButton.SetPosition( 240, 400 ); + tetButton.SetSize( 100, 100 ); + + actor.Add( tetButton ); + Stage::GetCurrent().Add( actor ); + + // * Actor's touch event is connected to a callback function + // and this callback function consumes the event. + actor.TouchedSignal().Connect( &TestCallback ); + + // * Button's pressed signal is connected to a callback function + // which also consumes the event. + // * Changes the visibility of the button to false. + TETButtonPressed tetButtonPressed( tetButton, TETButtonPressed::VISIBILITY ); + tetButton.PressedSignal().Connect( &tetButtonPressed, &TETButtonPressed::Callback ); + + // Initializes TET state. + gOnTouchPointInterrupted = false; + tetButton.SetVisible( true ); + + Dali::Integration::TouchEvent event; + + // TET starts. + + // Test a down point inside the button which is also consumed by the actor, and an up point + // consumed only by the actor. gOnTouchPointInterrupted should be true (Button receives an + // interrupt event. + + application.SendNotification(); + application.Render(); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + // More renders are needed in order to allow the node of the actor to become invisible. + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + // An up event is sent outside the button's boundary but inside the actor's one. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gOnTouchPointInterrupted ); + + // Test a down point inside the button which is also consumed by the actor, and a motion point + // consumed only by the actor. gOnTouchPointInterrupted should be true (Button receives an + // interrupt event. + + // Initializes TET state. + gOnTouchPointInterrupted = false; + tetButton.SetVisible( true ); + + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + // More renders are needed in order to allow the node of the actor to become invisible. + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + // A motion event is sent outside the button's boundary but inside the actor's one. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointMotionOut ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( gOnTouchPointInterrupted ); + + // Test a down point inside the button which is also consumed by the actor, and an up point + // also inside the button and consumed by the actor. gOnTouchPointInterrupted should be false. + + // Initializes TET state. + gOnTouchPointInterrupted = false; + tetButton.SetVisible( true ); + + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + // A down event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + tetButton.SetVisible( true ); + + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + // An up event is sent inside the button's boundary. + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.GetCore().SendEvent( event ); + + DALI_TEST_CHECK( !gOnTouchPointInterrupted ); +} diff --git a/automated-tests/dali-test-suite/cluster/.gitignore b/automated-tests/dali-test-suite/cluster/.gitignore new file mode 100644 index 0000000..0f1bba7 --- /dev/null +++ b/automated-tests/dali-test-suite/cluster/.gitignore @@ -0,0 +1 @@ +utc-Dali-Cluster diff --git a/automated-tests/dali-test-suite/cluster/Makefile b/automated-tests/dali-test-suite/cluster/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/cluster/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/cluster/file.list b/automated-tests/dali-test-suite/cluster/file.list new file mode 100644 index 0000000..3025d80 --- /dev/null +++ b/automated-tests/dali-test-suite/cluster/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-Cluster \ diff --git a/automated-tests/dali-test-suite/cluster/tslist b/automated-tests/dali-test-suite/cluster/tslist new file mode 100644 index 0000000..d3e3708 --- /dev/null +++ b/automated-tests/dali-test-suite/cluster/tslist @@ -0,0 +1 @@ +/dali-test-suite/cluster/utc-Dali-Cluster diff --git a/automated-tests/dali-test-suite/cluster/utc-Dali-Cluster.cpp b/automated-tests/dali-test-suite/cluster/utc-Dali-Cluster.cpp new file mode 100644 index 0000000..09721e6 --- /dev/null +++ b/automated-tests/dali-test-suite/cluster/utc-Dali-Cluster.cpp @@ -0,0 +1,224 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ + +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliClusterNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliClusterDownCast, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliClusterAddAndRemoveChild, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliClusterExpandAndCollapseChild, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliClusterSetAndGetStyle, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliClusterNew() +{ + ToolkitTestApplication application; + + // Create the Cluster actor + ClusterStyle style = ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1); + Cluster cluster = Cluster::New(style); + + DALI_TEST_CHECK(cluster); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + ClusterStyle style = ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1); + Cluster cluster = Cluster::New(style); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliClusterDownCast() +{ + ToolkitTestApplication application; + + // Create the Cluster actor + ClusterStyle style = ClusterStyleRandom::New(); + const Cluster clusterConst = Cluster::New(style); + Cluster cluster(clusterConst); + + BaseHandle handle(cluster); + + Cluster newCluster = Cluster::DownCast( handle ); + DALI_TEST_CHECK( cluster ); + DALI_TEST_CHECK( newCluster == cluster ); +} + +static void UtcDaliClusterAddAndRemoveChild() +{ + ToolkitTestApplication application; + + // Create the Cluster actor + ClusterStyle style = ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1); + Cluster cluster = Cluster::New(style); + + Actor childActor1 = Actor::New(); + Actor childActor2 = Actor::New(); + Actor childActor3 = Actor::New(); + Actor childActor4 = Actor::New(); + + // Add the first child and check it is added to the end + cluster.AddChild(childActor1); + DALI_TEST_CHECK( cluster.GetChildAt(0) == childActor1); + DALI_TEST_CHECK( !cluster.GetChildAt(1) ); + DALI_TEST_CHECK( cluster.GetTotalCount() == 1 ); + + // Add the second child to the given position and check it is added + cluster.AddChild(childActor2, 1); + DALI_TEST_CHECK( cluster.GetChildAt(1) == childActor2); + DALI_TEST_CHECK( cluster.GetTotalCount() == 2 ); + + // Add the third child with depth index 1 and check it is added to the end + cluster.AddChildAt(childActor3, 1); + DALI_TEST_CHECK( cluster.GetChildAt(2) == childActor3); + DALI_TEST_CHECK( cluster.GetTotalCount() == 3 ); + + // Add the fourth child with depth index 2 to the given position and check it is added + cluster.AddChildAt(childActor4, 2, 3); + DALI_TEST_CHECK( cluster.GetChildAt(3) == childActor4); + DALI_TEST_CHECK( cluster.GetTotalCount() == 4 ); + + // Remove the child in the given position and check it's removed + cluster.RemoveChildAt(3); + DALI_TEST_CHECK( !cluster.GetChildAt(3) ); + DALI_TEST_CHECK( cluster.GetTotalCount() == 3 ); +} + +static void UtcDaliClusterExpandAndCollapseChild() +{ + ToolkitTestApplication application; + + // Create the Cluster actor + ClusterStyle style = ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1); + Cluster cluster = Cluster::New(style); + + Actor childActor1 = Actor::New(); + Actor childActor2 = Actor::New(); + Actor childActor3 = Actor::New(); + Actor childActor4 = Actor::New(); + + // Add the child actors + cluster.AddChild(childActor1); + cluster.AddChild(childActor2); + cluster.AddChildAt(childActor3, 1); + cluster.AddChildAt(childActor4, 2, 3); + + // Expand child actor 3 + cluster.ExpandChild(2); + DALI_TEST_CHECK( cluster.GetExpandedCount() == 1 ); + + // Expand child actor 4 + cluster.ExpandChild(3); + DALI_TEST_CHECK( cluster.GetExpandedCount() == 2 ); + + // Collapse child actor 3 + cluster.CollapseChild(2); + DALI_TEST_CHECK( cluster.GetExpandedCount() == 1 ); + + // Expand all children + cluster.ExpandAllChildren(); + DALI_TEST_CHECK( cluster.GetExpandedCount() == 4 ); + + // Collpase all children + cluster.CollapseAllChildren(); + DALI_TEST_CHECK( cluster.GetExpandedCount() == 0 ); + + // Transform and restore the child + cluster.TransformChild(1, Vector3(10.0f, 10.0f, 1.0f), Vector3(1.0f, 1.0f, 1.0f), Quaternion(0.0f, Vector3::YAXIS), AlphaFunctions::EaseOut, 0.5f); + cluster.RestoreChild(1, AlphaFunctions::EaseOut, 0.25f, true); +} + +static void UtcDaliClusterSetAndGetStyle() +{ + ToolkitTestApplication application; + + // Create the default cluster style + ClusterStyle defaultStyle = ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1); + DALI_TEST_CHECK( defaultStyle.GetMaximumNumberOfChildren() > 0 ); + + // Add style to background and title + Actor background = Actor::New(); + Actor title = Actor::New(); + defaultStyle.ApplyStyleToBackground(background, AlphaFunctions::EaseOut, 1.0f); + defaultStyle.ApplyStyleToTitle(title, AlphaFunctions::EaseOut, 1.0f); + + // Create the Cluster actor with the default style + Cluster cluster = Cluster::New(defaultStyle); + DALI_TEST_CHECK( cluster.GetStyle() == defaultStyle ); + cluster.SetBackgroundImage(background); + cluster.SetTitle(title); + + // Create a new style and apply it to the cluster + ClusterStyle newStyle = ClusterStyleRandom::New(); + cluster.SetStyle(newStyle); + DALI_TEST_CHECK( cluster.GetStyle() == newStyle ); +} diff --git a/automated-tests/dali-test-suite/control/.gitignore b/automated-tests/dali-test-suite/control/.gitignore new file mode 100644 index 0000000..17a0b4b --- /dev/null +++ b/automated-tests/dali-test-suite/control/.gitignore @@ -0,0 +1,2 @@ +utc-Dali-ControlImpl +utc-Dali-Control diff --git a/automated-tests/dali-test-suite/control/Makefile b/automated-tests/dali-test-suite/control/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/control/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/control/dummy-control.h b/automated-tests/dali-test-suite/control/dummy-control.h new file mode 100644 index 0000000..18c10f9 --- /dev/null +++ b/automated-tests/dali-test-suite/control/dummy-control.h @@ -0,0 +1,237 @@ +#ifndef __DALI_TOOLKIT_TEST_DUMMY_CONTROL_H__ +#define __DALI_TOOLKIT_TEST_DUMMY_CONTROL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class DummyControlImpl; + +/** + * Control does not have a New method so use this dummy class for the handle. + */ +class DummyControl : public Control +{ +public: + + DummyControl() + : mCustomSlot1Called(false) + { + } + + DummyControl(const DummyControl& control) + : Control( control ), + mCustomSlot1Called(false) + { + } + + virtual ~DummyControl() + { + } + + static DummyControl New( bool override = false ); + + static DummyControl DownCast( BaseHandle handle ) + { + return Control::DownCast(handle); + } + + DummyControl& operator=(const DummyControl& control) + { + Control::operator=( control ); + return *this; + } + + // Used to test signal connections + void CustomSlot1( Actor actor, const Vector3& value ) + { + mCustomSlot1Called = true; + mCustomSlot1Value = value; + } + +public: + + bool mCustomSlot1Called; + Vector3 mCustomSlot1Value; + +public: // Not intended for application developers + + DummyControl( DummyControlImpl& implementation ); + DummyControl( Dali::Internal::CustomActor* internal ); +}; + +/** + * Cannot create an instance of ControlImpl, so use this dummy class for the implementation. + * This class does not override any of ControlImpl's behaviour. + */ +class DummyControlImpl : public ControlImpl +{ +public: + + static DummyControl New() + { + IntrusivePtr< DummyControlImpl > impl = new DummyControlImpl; + DummyControl control( *impl ); + impl->Initialize(); + return control; + } + +public: + void EnableGestureDetection(Gesture::Type type) { ControlImpl::EnableGestureDetection(type); } + void DisableGestureDetection(Gesture::Type type) { ControlImpl::DisableGestureDetection(type); } + PinchGestureDetector GetPinchGestureDetector() const { return ControlImpl::GetPinchGestureDetector(); } + PanGestureDetector GetPanGestureDetector() const { return ControlImpl::GetPanGestureDetector(); } + TapGestureDetector GetTapGestureDetector() const { return ControlImpl::GetTapGestureDetector(); } + LongPressGestureDetector GetLongPressGestureDetector() const { return ControlImpl::GetLongPressGestureDetector(); } + +protected: + + DummyControlImpl() + : ControlImpl(true) + { + } + + virtual ~DummyControlImpl() + { + } +}; + +/** + * Cannot create an instance of ControlImpl, so use this dummy class for the implementation. + * This class DOES override ControlImpl's behaviour. + */ +class DummyControlImplOverride : public DummyControlImpl +{ +public: + + static DummyControl New() + { + IntrusivePtr< DummyControlImplOverride > impl = new DummyControlImplOverride; + DummyControl control( *impl ); + impl->Initialize(); + return control; + } + +private: + + DummyControlImplOverride() + : DummyControlImpl(), + initializeCalled(false), + styleChangeCalled(false), + pinchCalled(false), + panCalled(false), + tapCalled(false), + longPressCalled(false), + stageConnectionCalled(false), + stageDisconnectionCalled(false), + childAddCalled(false), + childRemoveCalled(false), + sizeSetCalled(false), + sizeAnimationCalled(false), + touchEventCalled(false), + mouseWheelEventCalled(false), + keyEventCalled(false), + keyInputFocusGained(false), + keyInputFocusLost(false) + { + } + + virtual ~DummyControlImplOverride() { } + +private: // From ControlImpl + + virtual void OnInitialize() { initializeCalled = true; } + virtual void OnStyleChange(StyleChange change) { styleChangeCalled = true;} + virtual void OnPinch(PinchGesture pinch) { pinchCalled = true; } + virtual void OnPan(PanGesture pan) { panCalled = true; } + virtual void OnTap(TapGesture tap) { tapCalled = true; } + virtual void OnLongPress(LongPressGesture longPress) { longPressCalled = true; } + +private: // From CustomActorImpl + + virtual void OnStageConnection() { stageConnectionCalled = true; } + virtual void OnStageDisconnection() { stageDisconnectionCalled = true; } + virtual void OnChildAdd(Actor& child) { childAddCalled = true; } + virtual void OnChildRemove(Actor& child) { childRemoveCalled = true; } + virtual void OnSizeSet(const Vector3& targetSize) { sizeSetCalled = true; } + virtual void OnSizeAnimation(Animation& animation, const Vector3& targetSize) { sizeAnimationCalled = true; } + virtual bool OnTouchEvent(const TouchEvent& event) { touchEventCalled = true; return false; } + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event) { mouseWheelEventCalled = true; return false; } + virtual bool OnKeyEvent(const KeyEvent& event) { keyEventCalled = true; return false;} + virtual void OnKeyInputFocusGained() { keyInputFocusGained = true; } + virtual void OnKeyInputFocusLost() { keyInputFocusLost = true; } + +public: + + bool initializeCalled; + bool styleChangeCalled; + bool pinchCalled; + bool panCalled; + bool tapCalled; + bool longPressCalled; + bool stageConnectionCalled; + bool stageDisconnectionCalled; + bool childAddCalled; + bool childRemoveCalled; + bool sizeSetCalled; + bool sizeAnimationCalled; + bool touchEventCalled; + bool mouseWheelEventCalled; + bool keyEventCalled; + bool keyInputFocusGained; + bool keyInputFocusLost; +}; + +DummyControl DummyControl::New( bool override ) +{ + DummyControl control; + + if (override) + { + control = DummyControlImplOverride::New(); + } + else + { + control = DummyControlImpl::New(); + } + + return control; +} + +DummyControl::DummyControl( DummyControlImpl& implementation ) +: Control( implementation ) +{ +} + +DummyControl::DummyControl( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TEST_DUMMY_CONTROL_H__ diff --git a/automated-tests/dali-test-suite/control/file.list b/automated-tests/dali-test-suite/control/file.list new file mode 100644 index 0000000..7efd89d --- /dev/null +++ b/automated-tests/dali-test-suite/control/file.list @@ -0,0 +1,3 @@ +TARGETS += \ + utc-Dali-ControlImpl \ + utc-Dali-Control \ diff --git a/automated-tests/dali-test-suite/control/tslist b/automated-tests/dali-test-suite/control/tslist new file mode 100644 index 0000000..e2ff777 --- /dev/null +++ b/automated-tests/dali-test-suite/control/tslist @@ -0,0 +1,2 @@ +/dali-test-suite/control/utc-Dali-ControlImpl +/dali-test-suite/control/utc-Dali-Control diff --git a/automated-tests/dali-test-suite/control/utc-Dali-Control.cpp b/automated-tests/dali-test-suite/control/utc-Dali-Control.cpp new file mode 100644 index 0000000..f437d84 --- /dev/null +++ b/automated-tests/dali-test-suite/control/utc-Dali-Control.cpp @@ -0,0 +1,368 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +#include "dummy-control.h" + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliControlConstructor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlRegister, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlCopyAndAssignment, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlDownCast, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlDownCastTemplate, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlKeyInputFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlGetImplementation, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlSignalConnectDisconnect, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlSignalAutomaticDisconnect, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlTestParameters, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliControlConstructor() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + + DummyControl dummy; + + DALI_TEST_CHECK( !Control::DownCast(dummy) ); + + dummy = DummyControl::New(); + + DALI_TEST_CHECK( Control::DownCast(dummy) ); +} + +static void UtcDaliControlNew() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + + Control control; + + DALI_TEST_CHECK( !Control::DownCast(control) ); + + control = Control::New(); + + DALI_TEST_CHECK( Control::DownCast(control) ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +static void UtcDaliControlRegister() +{ + ToolkitTestApplication application; + + // Ensure the object is registered after creation + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + Alignment alignment = Alignment::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void UtcDaliControlCopyAndAssignment() +{ + ToolkitTestApplication application; + + DummyControl control = DummyControl::New(); + Control emptyControl; + + Control controlCopy( control ); + DALI_TEST_CHECK( control == controlCopy ); + + Control emptyControlCopy( emptyControl ); + DALI_TEST_CHECK( emptyControl == emptyControlCopy ); + + Control controlEquals; + controlEquals = control; + DALI_TEST_CHECK( control == controlEquals ); + + Control emptyControlEquals; + emptyControlEquals = emptyControl; + DALI_TEST_CHECK( emptyControl == emptyControlEquals ); + + // Self assignment + control = control; + DALI_TEST_CHECK( control == controlCopy ); +} + +static void UtcDaliControlDownCast() +{ + ToolkitTestApplication application; + + DummyControl control; + + DALI_TEST_CHECK( !Control::DownCast( control ) ); + + control = DummyControl::New(); + + DALI_TEST_CHECK( Control::DownCast( control ) ); + + Actor actor; + + DALI_TEST_CHECK( !Control::DownCast( actor ) ); + + actor = Actor::New(); + + DALI_TEST_CHECK( !Control::DownCast( actor ) ); +} + +static void UtcDaliControlDownCastTemplate() +{ + ToolkitTestApplication application; + + DummyControl control; + + DALI_TEST_CHECK( !DummyControl::DownCast( control )); + + control = DummyControl::New(); + + DALI_TEST_CHECK( DummyControl::DownCast( control ) ); + + Actor actor; + + DALI_TEST_CHECK( !DummyControl::DownCast( actor ) ); + + actor = Actor::New(); + + DALI_TEST_CHECK( !DummyControl::DownCast( actor ) ); +} + +static void UtcDaliControlKeyInputFocus() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + DummyControl control; + + PushButton pushButton1 = PushButton::New(); + stage.Add( pushButton1 ); + + pushButton1.SetKeyInputFocus(); + DALI_TEST_CHECK( pushButton1.HasKeyInputFocus() ); + + pushButton1.ClearKeyInputFocus(); + DALI_TEST_CHECK( !pushButton1.HasKeyInputFocus() ); +} + +static void UtcDaliControlGetImplementation() +{ + ToolkitTestApplication application; + + DummyControl control; + + // Get Empty + { + try + { + ControlImpl& controlImpl = control.GetImplementation(); + (void)controlImpl; // Avoid unused warning + tet_result(TET_FAIL); + } + catch (DaliException &exception) + { + tet_result(TET_PASS); + } + } + + // Get Const Empty + { + try + { + const DummyControl constControl(control); + const ControlImpl& controlImpl = constControl.GetImplementation(); + (void)controlImpl; // Avoid unused warning + tet_result(TET_FAIL); + } + catch (DaliException &exception) + { + tet_result(TET_PASS); + } + } + + control = DummyControl::New(); + + // Get + { + try + { + ControlImpl& controlImpl = control.GetImplementation(); + (void)controlImpl; // Avoid unused warning + tet_result(TET_PASS); + } + catch (DaliException &exception) + { + tet_result(TET_FAIL); + } + } + + // Get Const + { + try + { + const DummyControl constControl(control); + const ControlImpl& controlImpl = constControl.GetImplementation(); + (void)controlImpl; // Avoid unused warning + tet_result(TET_PASS); + } + catch (DaliException &exception) + { + tet_result(TET_FAIL); + } + } +} + +static void UtcDaliControlSignalConnectDisconnect() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControlImpl::New(); + + Actor actor = Actor::New(); + DALI_TEST_EQUALS( actor.SetSizeSignal().GetConnectionCount(), 0u, TEST_LOCATION ); + actor.SetSizeSignal().Connect( &dummy, &DummyControl::CustomSlot1 ); + DALI_TEST_EQUALS( actor.SetSizeSignal().GetConnectionCount(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Called, false, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Value, Vector3::ZERO, TEST_LOCATION ); + + const Vector3 newSize( 10, 10, 0 ); + actor.SetSize( newSize ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Called, true, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Value, newSize, TEST_LOCATION ); + + dummy.mCustomSlot1Called = false; + actor.SetSizeSignal().Disconnect( &dummy, &DummyControl::CustomSlot1 ); + DALI_TEST_EQUALS( actor.SetSizeSignal().GetConnectionCount(), 0u, TEST_LOCATION ); + const Vector3 ignoredSize( 20, 20, 0 ); + actor.SetSize( ignoredSize ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Called, false, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Value, newSize/*not ignoredSize*/, TEST_LOCATION ); + } +} + +static void UtcDaliControlSignalAutomaticDisconnect() +{ + ToolkitTestApplication application; + + Actor actor = Actor::New(); + + { + DummyControl dummy = DummyControlImpl::New(); + + actor.SetSizeSignal().Connect( &dummy, &DummyControl::CustomSlot1 ); + DALI_TEST_EQUALS( actor.SetSizeSignal().GetConnectionCount(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Called, false, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Value, Vector3::ZERO, TEST_LOCATION ); + + const Vector3 newSize( 10, 10, 0 ); + actor.SetSize( newSize ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Called, true, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.mCustomSlot1Value, newSize, TEST_LOCATION ); + } + // dummyControl automatically disconnects + + DALI_TEST_EQUALS( actor.SetSizeSignal().GetConnectionCount(), 0u, TEST_LOCATION ); + + const Vector3 ignoredSize( 20, 20, 0 ); + actor.SetSize( ignoredSize ); +} + +static void UtcDaliControlTestParameters() +{ + ToolkitTestApplication application; + DummyControl test = DummyControl::New(); + + Vector3 maxSize = test.GetNaturalSize(); + Vector3 minSize = maxSize / 2.0f; + + Toolkit::Control::SizePolicy widthPolicy( Control::Fixed ); + Toolkit::Control::SizePolicy heightPolicy( Control::Fixed ); + test.SetSizePolicy( widthPolicy, heightPolicy ); + test.GetSizePolicy( widthPolicy, heightPolicy ); + + DALI_TEST_CHECK( widthPolicy == Control::Fixed && heightPolicy == Control::Fixed ); + + test.SetSize( 0.7f, 0.7f, 0.7f ); + float width = 640.0f; + float height = test.GetHeightForWidth( width ); + DALI_TEST_CHECK( test.GetWidthForHeight( height ) == width ); + + test.SetMinimumSize( minSize ); + DALI_TEST_CHECK( test.GetMinimumSize() == minSize ); + + test.SetMaximumSize( maxSize ); + DALI_TEST_CHECK( test.GetMaximumSize() == maxSize ); + + test.KeyEventSignal(); + DummyControl test2 = DummyControl::New(); + dynamic_cast< ConnectionTrackerInterface& >( test2 ).GetConnectionCount(); + + // Provide coverage for pointer destructor + Control* testControlPtr = new Control; + DALI_TEST_CHECK( testControlPtr ); + delete testControlPtr; +} diff --git a/automated-tests/dali-test-suite/control/utc-Dali-ControlImpl.cpp b/automated-tests/dali-test-suite/control/utc-Dali-ControlImpl.cpp new file mode 100644 index 0000000..1264f60 --- /dev/null +++ b/automated-tests/dali-test-suite/control/utc-Dali-ControlImpl.cpp @@ -0,0 +1,829 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dummy-control.h" + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliControlImplNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplTypeRegistry, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplEnableGestureDetector, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplDisableGestureDetector, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplOnGestureMethods, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplChildAddAndRemove, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplStageConnection, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplSizeSet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplSizeAnimation, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplTouchEvent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplMouseWheelEvent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplKeyEvent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplStyleChange, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplKeyInputFocusGained, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliControlImplKeyInputFocusLost, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliControlImplNew() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + + Control control; + + DALI_TEST_CHECK( !Control::DownCast(control) ); + + control = ControlImpl::New(); + + DALI_TEST_CHECK( Control::DownCast(control) ); +} + +static void UtcDaliControlImplTypeRegistry() +{ + ToolkitTestApplication application; + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "Control" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + // Check if it's a control + DALI_TEST_CHECK( Control::DownCast(handle) ); +} + +static void UtcDaliControlImplEnableGestureDetector() +{ + ToolkitTestApplication application; + + // Enable individually + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + dummyImpl.EnableGestureDetection(Gesture::Pinch); + DALI_TEST_CHECK( dummyImpl.GetPinchGestureDetector() ); + + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + dummyImpl.EnableGestureDetection(Gesture::Pan); + DALI_TEST_CHECK( dummyImpl.GetPanGestureDetector() ); + + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + dummyImpl.EnableGestureDetection(Gesture::Tap); + DALI_TEST_CHECK( dummyImpl.GetTapGestureDetector() ); + + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + dummyImpl.EnableGestureDetection(Gesture::LongPress); + DALI_TEST_CHECK( dummyImpl.GetLongPressGestureDetector() ); + } + + // Enable All + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetLongPressGestureDetector() ); + + // Enable when already enabled + + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetLongPressGestureDetector() ); + } +} + +static void UtcDaliControlImplDisableGestureDetector() +{ + ToolkitTestApplication application; + + // Disable individually + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummyImpl.GetPinchGestureDetector() ); + dummyImpl.DisableGestureDetection(Gesture::Pinch); + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + + DALI_TEST_CHECK( dummyImpl.GetPanGestureDetector() ); + dummyImpl.DisableGestureDetection(Gesture::Pan); + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + + DALI_TEST_CHECK( dummyImpl.GetTapGestureDetector() ); + dummyImpl.DisableGestureDetection(Gesture::Tap); + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + + DALI_TEST_CHECK( dummyImpl.GetLongPressGestureDetector() ); + dummyImpl.DisableGestureDetection(Gesture::LongPress); + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + } + + // Disable All + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( dummyImpl.GetLongPressGestureDetector() ); + + dummyImpl.DisableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + } + + // Disable When not enabled + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + + dummyImpl.DisableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( !dummyImpl.GetPinchGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetPanGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetTapGestureDetector() ); + DALI_TEST_CHECK( !dummyImpl.GetLongPressGestureDetector() ); + } + + // Ensure control is detached if gesture detector is not deleted + { + DummyControl dummy = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + PinchGestureDetector pinch = dummyImpl.GetPinchGestureDetector(); + PanGestureDetector pan = dummyImpl.GetPanGestureDetector(); + TapGestureDetector tap = dummyImpl.GetTapGestureDetector(); + LongPressGestureDetector longPress = dummyImpl.GetLongPressGestureDetector(); + + DALI_TEST_EQUALS( pinch.GetAttachedActors().empty(), false, TEST_LOCATION ); + DALI_TEST_EQUALS( pan.GetAttachedActors().empty(), false, TEST_LOCATION ); + DALI_TEST_EQUALS( tap.GetAttachedActors().empty(), false, TEST_LOCATION ); + DALI_TEST_EQUALS( longPress.GetAttachedActors().empty(), false, TEST_LOCATION ); + + dummyImpl.DisableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_EQUALS( pinch.GetAttachedActors().empty(), true, TEST_LOCATION ); + DALI_TEST_EQUALS( pan.GetAttachedActors().empty(), true, TEST_LOCATION ); + DALI_TEST_EQUALS( tap.GetAttachedActors().empty(), true, TEST_LOCATION ); + DALI_TEST_EQUALS( longPress.GetAttachedActors().empty(), true, TEST_LOCATION ); + } +} + +static void UtcDaliControlImplOnGestureMethods() +{ + ToolkitTestApplication application; + + // Check gesture actually happens + { + DummyControl dummy = DummyControl::New(true); + dummy.SetSize( Vector3(100.0f, 100.0f, 100.0f) ); + + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + // Render and notify a couple of times + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummyImpl.pinchCalled == false ); + Integration::PinchGestureEvent pinch(Gesture::Started); + pinch.scale = 10.0f; + pinch.speed = 50.0f; + pinch.centerPoint = Vector2(20.0f, 20.0f); + application.GetCore().SendEvent(pinch); + DALI_TEST_CHECK( dummyImpl.pinchCalled == true ); + + DALI_TEST_CHECK( dummyImpl.panCalled == false ); + Integration::PanGestureEvent pan(Gesture::Possible); + pan.previousPosition = Vector2(10.0f, 20.0f); + pan.currentPosition = Vector2(20.0f, 20.0f); + pan.timeDelta = 10; + pan.numberOfTouches = 1u; + application.GetCore().SendEvent(pan); + pan.state = Gesture::Started; + application.GetCore().SendEvent(pan); + DALI_TEST_CHECK( dummyImpl.panCalled == true ); + + DALI_TEST_CHECK( dummyImpl.tapCalled == false ); + Integration::TapGestureEvent tap(Gesture::Possible); + tap.numberOfTaps = 1u; + tap.numberOfTouches = 1u; + tap.point = Vector2(50.0f, 50.0f); + application.GetCore().SendEvent(tap); + tap.state = Gesture::Started; + application.GetCore().SendEvent(tap); + DALI_TEST_CHECK( dummyImpl.tapCalled == true ); + + DALI_TEST_CHECK( dummyImpl.longPressCalled == false ); + Integration::LongPressGestureEvent longPress(Gesture::Possible); + longPress.numberOfTouches = 1u; + longPress.point = Vector2(50.0f, 50.0f); + application.GetCore().SendEvent(longPress); + longPress.state = Gesture::Started; + application.GetCore().SendEvent(longPress); + DALI_TEST_CHECK( dummyImpl.longPressCalled == true ); + longPress.state = Gesture::Finished; + application.GetCore().SendEvent(longPress); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + dummy.SetSize( Vector3(100.0f, 100.0f, 100.0f) ); + + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + // Render and notify a couple of times + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + + DummyControlImpl& dummyImpl = static_cast(dummy.GetImplementation()); + dummyImpl.EnableGestureDetection( Gesture::Type(Gesture::Pinch | Gesture::Pan | Gesture::Tap | Gesture::LongPress) ); + + DALI_TEST_CHECK( dummy.GetCurrentScale().x != 10.0f ); + Integration::PinchGestureEvent pinch(Gesture::Started); + pinch.scale = 10.0f; + pinch.speed = 50.0f; + pinch.centerPoint = Vector2(20.0f, 20.0f); + application.GetCore().SendEvent(pinch); + + // Render and notify a couple of times + application.SendNotification(); + application.Render(); + application.SendNotification(); + application.Render(); + DALI_TEST_CHECK( dummy.GetCurrentScale().x == 10.0f ); + + Integration::PanGestureEvent pan(Gesture::Possible); + pan.previousPosition = Vector2(10.0f, 20.0f); + pan.currentPosition = Vector2(20.0f, 20.0f); + pan.timeDelta = 10; + pan.numberOfTouches = 1u; + application.GetCore().SendEvent(pan); + pan.state = Gesture::Started; + application.GetCore().SendEvent(pan); + + Integration::TapGestureEvent tap(Gesture::Possible); + tap.numberOfTaps = 1u; + tap.numberOfTouches = 1u; + tap.point = Vector2(50.0f, 50.0f); + application.GetCore().SendEvent(tap); + tap.state = Gesture::Started; + application.GetCore().SendEvent(tap); + + Integration::LongPressGestureEvent longPress(Gesture::Possible); + longPress.numberOfTouches = 1u; + longPress.point = Vector2(50.0f, 50.0f); + application.GetCore().SendEvent(longPress); + longPress.state = Gesture::Started; + application.GetCore().SendEvent(longPress); + longPress.state = Gesture::Finished; + application.GetCore().SendEvent(longPress); + + Stage::GetCurrent().Remove(dummy); + } +} + +static void UtcDaliControlImplChildAddAndRemove() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + Stage::GetCurrent().Add(dummy); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.childAddCalled, false, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.GetChildCount(), 0u, TEST_LOCATION ); + Actor actor = RenderableActor::New(); + dummy.Add(actor); + DALI_TEST_EQUALS( dummyImpl.childAddCalled, true, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.GetChildCount(), 1u, TEST_LOCATION ); + + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.childRemoveCalled, false, TEST_LOCATION ); + dummy.Remove( actor ); + DALI_TEST_EQUALS( dummyImpl.childRemoveCalled, true, TEST_LOCATION ); + DALI_TEST_EQUALS( dummy.GetChildCount(), 0u, TEST_LOCATION ); + + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + Stage::GetCurrent().Add(dummy); + + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummy.GetChildCount(), 0u, TEST_LOCATION ); + Actor actor = RenderableActor::New(); + dummy.Add(actor); + DALI_TEST_EQUALS( dummy.GetChildCount(), 1u, TEST_LOCATION ); + + application.Render(); + application.SendNotification(); + + dummy.Remove( actor ); + DALI_TEST_EQUALS( dummy.GetChildCount(), 0u, TEST_LOCATION ); + + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(dummy); + } +} + +static void UtcDaliControlImplStageConnection() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + DALI_TEST_EQUALS( dummyImpl.stageConnectionCalled, false, TEST_LOCATION ); + Stage::GetCurrent().Add(dummy); + application.Render(); + application.SendNotification(); + DALI_TEST_EQUALS( dummyImpl.stageConnectionCalled, true, TEST_LOCATION ); + + DALI_TEST_EQUALS( dummyImpl.stageDisconnectionCalled, false, TEST_LOCATION ); + Stage::GetCurrent().Remove(dummy); + application.Render(); + application.SendNotification(); + DALI_TEST_EQUALS( dummyImpl.stageDisconnectionCalled, true, TEST_LOCATION ); + } + + // Ensure full code coverage + { + unsigned int stageChildren = Stage::GetCurrent().GetLayer(0).GetChildCount(); + DummyControl dummy = DummyControl::New(); + + DALI_TEST_EQUALS( Stage::GetCurrent().GetLayer(0).GetChildCount(), stageChildren, TEST_LOCATION ); + Stage::GetCurrent().Add(dummy); + application.Render(); + application.SendNotification(); + DALI_TEST_EQUALS( Stage::GetCurrent().GetLayer(0).GetChildCount(), stageChildren + 1, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + application.Render(); + application.SendNotification(); + DALI_TEST_EQUALS( Stage::GetCurrent().GetLayer(0).GetChildCount(), stageChildren, TEST_LOCATION ); + } +} + +static void UtcDaliControlImplSizeSet() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.sizeSetCalled, false, TEST_LOCATION ); + Vector3 size(100.0f, 200.0f, 0.0f); + dummy.SetSize(size); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(size, dummy.GetCurrentSize(), TEST_LOCATION); + DALI_TEST_EQUALS( dummyImpl.sizeSetCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + Stage::GetCurrent().Add(dummy); + + Vector3 size(100.0f, 200.0f, 0.0f); + DALI_TEST_CHECK( size != dummy.GetCurrentSize() ); + + application.Render(); + application.SendNotification(); + + dummy.SetSize(size); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS(size, dummy.GetCurrentSize(), TEST_LOCATION); + + Stage::GetCurrent().Remove(dummy); + } +} + +static void UtcDaliControlImplSizeAnimation() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + + DALI_TEST_EQUALS( dummyImpl.sizeAnimationCalled, false, TEST_LOCATION ); + Animation animation = Animation::New(1.0f); + animation.Resize(dummy, Vector3(100.0f, 150.0f, 200.0f)); + animation.Play(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.sizeAnimationCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + Stage::GetCurrent().Add(dummy); + + Animation animation = Animation::New(1.0f); + animation.Resize(dummy, Vector3(100.0f, 150.0f, 200.0f)); + animation.Play(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + Stage::GetCurrent().Remove(dummy); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void UtcDaliControlImplTouchEvent() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + dummy.SetSize(100.0f, 100.0f); + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.touchEventCalled, false, TEST_LOCATION ); + Integration::TouchEvent touchEvent(1); + TouchPoint point(1, TouchPoint::Down, 20.0f, 20.0f); + touchEvent.AddPoint(point); + application.GetCore().SendEvent(touchEvent); + DALI_TEST_EQUALS( dummyImpl.touchEventCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + dummy.SetSize(100.0f, 100.0f); + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + Integration::TouchEvent touchEvent(1); + TouchPoint point(1, TouchPoint::Down, 20.0f, 20.0f); + touchEvent.AddPoint(point); + application.GetCore().SendEvent(touchEvent); + + Stage::GetCurrent().Remove(dummy); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool MouseWheelEventCallback(Actor actor, const MouseWheelEvent& event) +{ + return false; +} + +static void UtcDaliControlImplMouseWheelEvent() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + dummy.SetSize(100.0f, 100.0f); + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + dummy.MouseWheelEventSignal().Connect(&MouseWheelEventCallback); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.mouseWheelEventCalled, false, TEST_LOCATION ); + + // simulate a mouse wheel event + Vector2 screenCoordinates( 10.0f, 10.0f ); + Integration::MouseWheelEvent event(0, 0u, screenCoordinates, 1, 1000u); + application.GetCore().SendEvent(event); + DALI_TEST_EQUALS( dummyImpl.mouseWheelEventCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + dummy.SetSize(100.0f, 100.0f); + dummy.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummy); + + dummy.MouseWheelEventSignal().Connect(&MouseWheelEventCallback); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + // simulate a mouse wheel event + Vector2 screenCoordinates( 20.0f, 20.0f ); + Integration::MouseWheelEvent event(0, 0u, screenCoordinates, 1, 1000u); + application.GetCore().SendEvent(event); + + Stage::GetCurrent().Remove(dummy); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void UtcDaliControlImplKeyEvent() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + dummy.SetKeyInputFocus(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + DALI_TEST_EQUALS( dummyImpl.keyEventCalled, false, TEST_LOCATION ); + Integration::KeyEvent keyEvent; + application.GetCore().SendEvent(keyEvent); + DALI_TEST_EQUALS( dummyImpl.keyEventCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + Stage::GetCurrent().Add(dummy); + dummy.SetKeyInputFocus(); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + Integration::KeyEvent keyEvent; + application.GetCore().SendEvent(keyEvent); + + Stage::GetCurrent().Remove(dummy); + } +} + +static void UtcDaliControlImplStyleChange() +{ + ToolkitTestApplication application; + + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + // Add a Control and normal Actor as children + DummyControl dummyChild = DummyControl::New(); + dummy.Add(dummyChild); + + Actor actor = Actor::New(); + dummy.Add(actor); + + DALI_TEST_EQUALS( dummyImpl.styleChangeCalled, false, TEST_LOCATION ); + StyleChange styleChange; + styleChange.defaultFontChange = true; + application.GetAdaptor().GetToolkitStyleMonitor().EmitSignalStyleChange(styleChange); + DALI_TEST_EQUALS( dummyImpl.styleChangeCalled, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); +} + +static void UtcDaliControlImplKeyInputFocusGained() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + + DALI_TEST_EQUALS( dummyImpl.keyInputFocusGained, false, TEST_LOCATION ); + + dummy.SetKeyInputFocus(); + + DALI_TEST_EQUALS( dummyImpl.keyInputFocusGained, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + Stage::GetCurrent().Add(dummy); + dummy.SetKeyInputFocus(); + Stage::GetCurrent().Remove(dummy); + } +} + +static void UtcDaliControlImplKeyInputFocusLost() +{ + ToolkitTestApplication application; + + { + DummyControl dummy = DummyControl::New( true ); + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + Stage::GetCurrent().Add(dummy); + + DALI_TEST_EQUALS( dummyImpl.keyInputFocusLost, false, TEST_LOCATION ); + + dummy.SetKeyInputFocus(); + dummy.ClearKeyInputFocus(); + + DALI_TEST_EQUALS( dummyImpl.keyInputFocusLost, true, TEST_LOCATION ); + + Stage::GetCurrent().Remove(dummy); + } + + // Ensure full code coverage + { + DummyControl dummy = DummyControl::New(); + + Stage::GetCurrent().Add(dummy); + dummy.SetKeyInputFocus(); + dummy.ClearKeyInputFocus(); + + DummyControlImplOverride& dummyImpl = static_cast(dummy.GetImplementation()); + + dummyImpl.OnAccessibilityValueChange( true ); + dummyImpl.IsKeyboardNavigationSupported(); + dummyImpl.IsKeyboardFocusGroup(); + + Stage::GetCurrent().Remove(dummy); + } +} diff --git a/automated-tests/dali-test-suite/default-controls/.gitignore b/automated-tests/dali-test-suite/default-controls/.gitignore new file mode 100644 index 0000000..fa84f53 --- /dev/null +++ b/automated-tests/dali-test-suite/default-controls/.gitignore @@ -0,0 +1 @@ +utc-Dali-DefaultControls diff --git a/automated-tests/dali-test-suite/default-controls/Makefile b/automated-tests/dali-test-suite/default-controls/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/default-controls/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/default-controls/file.list b/automated-tests/dali-test-suite/default-controls/file.list new file mode 100644 index 0000000..91c147c --- /dev/null +++ b/automated-tests/dali-test-suite/default-controls/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-DefaultControls \ diff --git a/automated-tests/dali-test-suite/default-controls/tslist b/automated-tests/dali-test-suite/default-controls/tslist new file mode 100644 index 0000000..7dc630b --- /dev/null +++ b/automated-tests/dali-test-suite/default-controls/tslist @@ -0,0 +1 @@ +/dali-test-suite/default-controls/utc-Dali-DefaultControls diff --git a/automated-tests/dali-test-suite/default-controls/utc-Dali-DefaultControls.cpp b/automated-tests/dali-test-suite/default-controls/utc-Dali-DefaultControls.cpp new file mode 100644 index 0000000..49ce54c --- /dev/null +++ b/automated-tests/dali-test-suite/default-controls/utc-Dali-DefaultControls.cpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliDefaultControlsCreateSolidColorActor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliDefaultControlsCreatePushButton, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliDefaultControlsCreateCheckBoxButton, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliDefaultControlsCreateSolidColorActor() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliDefaultControlsCreateSolidColorActor"); + + ImageActor image1 = CreateSolidColorActor( Color::RED ); + ImageActor image2 = CreateSolidColorActor( Color::RED, true, Color::BLUE, 2 ); + ImageActor image3 = CreateSolidColorActor( Color::RED, true, Color::BLUE, 12 ); + + DALI_TEST_CHECK(image1); + DALI_TEST_CHECK(image2); + DALI_TEST_CHECK(!image3); +} + +static void UtcDaliDefaultControlsCreatePushButton() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliDefaultControlsCreatePushButton"); + + const std::string imagePath( "Facke image path" ); + const std::string voidImagePath( "" ); + + ImageActor image = CreateSolidColorActor( Color::RED ); + ImageActor voidImage; + + PushButton button0, button1, button2, button3, button4, button5, button6; + + try + { + button0 = CreatePushButton( voidImagePath, voidImagePath, voidImagePath, voidImagePath, voidImagePath ); + button1 = CreatePushButton( imagePath, voidImagePath, voidImagePath, voidImagePath, voidImagePath ); + button2 = CreatePushButton( voidImagePath, imagePath, voidImagePath, voidImagePath, voidImagePath ); + button3 = CreatePushButton( voidImagePath, voidImagePath, imagePath, voidImagePath, voidImagePath ); + button4 = CreatePushButton( voidImagePath, voidImagePath, voidImagePath, imagePath, voidImagePath ); + button5 = CreatePushButton( voidImagePath, voidImagePath, voidImagePath, voidImagePath, imagePath ); + button6 = CreatePushButton( imagePath, imagePath, imagePath, imagePath, imagePath ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); + DALI_TEST_CHECK( button4 ); + DALI_TEST_CHECK( button5 ); + DALI_TEST_CHECK( button6 ); + + try + { + button0 = CreatePushButton( voidImage, voidImage, voidImage, voidImage, voidImage ); + button1 = CreatePushButton( image, voidImage, voidImage, voidImage, voidImage ); + button2 = CreatePushButton( voidImage, image, voidImage, voidImage, voidImage ); + button3 = CreatePushButton( voidImage, voidImage, image, voidImage, voidImage ); + button4 = CreatePushButton( voidImage, voidImage, voidImage, image, voidImage ); + button5 = CreatePushButton( voidImage, voidImage, voidImage, voidImage, image ); + button6 = CreatePushButton( image, image, image, image, image ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); + DALI_TEST_CHECK( button4 ); + DALI_TEST_CHECK( button5 ); + DALI_TEST_CHECK( button6 ); + + try + { + button0 = CreatePushButton( voidImagePath ); + button1 = CreatePushButton( imagePath ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + + try + { + button0 = CreatePushButton( voidImage ); + button1 = CreatePushButton( image ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); +} + +static void UtcDaliDefaultControlsCreateCheckBoxButton() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliDefaultControlsCreateCheckBoxButton"); + + const std::string imagePath( "Facke image path" ); + const std::string voidImagePath( "" ); + + ImageActor image = CreateSolidColorActor( Color::RED ); + ImageActor voidImage; + + CheckBoxButton button0, button1, button2, button3, button4, button5; + + try + { + button0 = CreateCheckBoxButton( voidImagePath, voidImagePath, voidImagePath, voidImagePath ); + button1 = CreateCheckBoxButton( imagePath, voidImagePath, voidImagePath, voidImagePath ); + button2 = CreateCheckBoxButton( voidImagePath, imagePath, voidImagePath, voidImagePath ); + button3 = CreateCheckBoxButton( voidImagePath, voidImagePath, imagePath, voidImagePath ); + button4 = CreateCheckBoxButton( voidImagePath, voidImagePath, voidImagePath, imagePath ); + button5 = CreateCheckBoxButton( imagePath, imagePath, imagePath, imagePath ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); + DALI_TEST_CHECK( button4 ); + DALI_TEST_CHECK( button5 ); + + try + { + button0 = CreateCheckBoxButton( voidImage, voidImage, voidImage, voidImage ); + button1 = CreateCheckBoxButton( image, voidImage, voidImage, voidImage ); + button2 = CreateCheckBoxButton( voidImage, image, voidImage, voidImage ); + button3 = CreateCheckBoxButton( voidImage, voidImage, image, voidImage ); + button4 = CreateCheckBoxButton( voidImage, voidImage, voidImage, image ); + button5 = CreateCheckBoxButton( image, image, image, image ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); + DALI_TEST_CHECK( button4 ); + DALI_TEST_CHECK( button5 ); + + try + { + button0 = CreateCheckBoxButton( voidImagePath, voidImagePath ); + button1 = CreateCheckBoxButton( voidImagePath, imagePath ); + button2 = CreateCheckBoxButton( imagePath, voidImagePath ); + button3 = CreateCheckBoxButton( imagePath, imagePath ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); + + try + { + button0 = CreateCheckBoxButton( voidImage, voidImage ); + button2 = CreateCheckBoxButton( voidImage, image ); + button3 = CreateCheckBoxButton( voidImage, image ); + button4 = CreateCheckBoxButton( image, image ); + } + catch( ... ) + { + tet_result( TET_FAIL ); + } + + DALI_TEST_CHECK( button0 ); + DALI_TEST_CHECK( button1 ); + DALI_TEST_CHECK( button2 ); + DALI_TEST_CHECK( button3 ); +} diff --git a/automated-tests/dali-test-suite/focus-manager/.gitignore b/automated-tests/dali-test-suite/focus-manager/.gitignore new file mode 100644 index 0000000..000ce4a --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/.gitignore @@ -0,0 +1,3 @@ +utc-Dali-FocusManager +utc-Dali-KeyInputFocusManager +utc-Dali-KeyboardFocusManager diff --git a/automated-tests/dali-test-suite/focus-manager/Makefile b/automated-tests/dali-test-suite/focus-manager/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/focus-manager/file.list b/automated-tests/dali-test-suite/focus-manager/file.list new file mode 100644 index 0000000..13851a6 --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/file.list @@ -0,0 +1,4 @@ +TARGETS += \ + utc-Dali-FocusManager \ + utc-Dali-KeyInputFocusManager \ + utc-Dali-KeyboardFocusManager diff --git a/automated-tests/dali-test-suite/focus-manager/tslist b/automated-tests/dali-test-suite/focus-manager/tslist new file mode 100644 index 0000000..409996a --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/tslist @@ -0,0 +1,3 @@ +/dali-test-suite/focus-manager/utc-Dali-FocusManager +/dali-test-suite/focus-manager/utc-Dali-KeyInputFocusManager +/dali-test-suite/focus-manager/utc-Dali-KeyboardFocusManager diff --git a/automated-tests/dali-test-suite/focus-manager/utc-Dali-FocusManager.cpp b/automated-tests/dali-test-suite/focus-manager/utc-Dali-FocusManager.cpp new file mode 100644 index 0000000..c614f53 --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/utc-Dali-FocusManager.cpp @@ -0,0 +1,1070 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ + +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +// Functors to test whether focus changed signal is emitted when the focus is changed +class FocusChangedCallback : public Dali::ConnectionTracker +{ +public: + FocusChangedCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mOriginalFocusedActor(), + mCurrentFocusedActor() + { + } + + void Callback(Actor originalFocusedActor, Actor currentFocusedActor) + { + tet_infoline("Verifying FocusChangedCallback()"); + + if(originalFocusedActor == mCurrentFocusedActor) + { + mSignalVerified = true; + } + + mOriginalFocusedActor = originalFocusedActor; + mCurrentFocusedActor = currentFocusedActor; + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + Actor mOriginalFocusedActor; + Actor mCurrentFocusedActor; +}; + +// Functors to test whether focus overshot signal is emitted when there is no way to move focus further. +class FocusOvershotCallback : public Dali::ConnectionTracker +{ +public: + FocusOvershotCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mCurrentFocusedActor(), + mFocusOvershotDirection(Toolkit::FocusManager::OVERSHOT_NEXT) + { + } + + void Callback(Actor currentFocusedActor, Toolkit::FocusManager::FocusOvershotDirection direction) + { + tet_infoline("Verifying FocusOvershotCallback()"); + + if(currentFocusedActor == mCurrentFocusedActor && direction == mFocusOvershotDirection) + { + mSignalVerified = true; + } + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + Actor mCurrentFocusedActor; + Toolkit::FocusManager::FocusOvershotDirection mFocusOvershotDirection; +}; + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliFocusManagerGet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSetAndGetAccessibilityAttribute, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSetAndGetFocusOrder, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerGenerateNewFocusOrder, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerGetActorByFocusOrder, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSetAndGetCurrentFocusActor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerGetCurrentFocusGroup, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerGetCurrentFocusOrder, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerMoveFocusForward, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerMoveFocusBackward, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerClearFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerReset, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerFocusGroup, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSetAndGetFocusIndicator, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSignalFocusChanged, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliFocusManagerSignalFocusOvershot, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliFocusManagerGet() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerGet"); + + FocusManager manager; + + //Ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK(registry); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + FocusManager newManager = FocusManager::Get(); + DALI_TEST_CHECK(newManager); + + // Check that focus manager is a singleton + DALI_TEST_CHECK(manager == newManager); +} + +static void UtcDaliFocusManagerSetAndGetAccessibilityAttribute() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSetAndGetAccessibilityAttribute"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor actor = Actor::New(); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(actor, FocusManager::ACCESSIBILITY_LABEL) == ""); + + manager.SetAccessibilityAttribute(actor, FocusManager::ACCESSIBILITY_LABEL, "Description"); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(actor, FocusManager::ACCESSIBILITY_LABEL) == "Description"); + + manager.SetAccessibilityAttribute(actor, FocusManager::ACCESSIBILITY_LABEL, "New description"); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(actor, FocusManager::ACCESSIBILITY_LABEL) == "New description"); +} + +static void UtcDaliFocusManagerSetAndGetFocusOrder() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSetAndGetFocusOrder"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor first = Actor::New(); + Actor second = Actor::New(); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 0); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 0); + + // Set the focus order and description for the first actor + manager.SetFocusOrder(first, 1); + manager.SetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 1); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(second, 2); + manager.SetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL, "second"); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // check that the focus order of the first actor is changed + manager.SetFocusOrder(first, 2); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 2); + // make sure the change of focus order doesn't affect the actor's description + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // check that the focus order of the second actor is increased to 3 + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 3); + // make sure the change of focus order doesn't affect the actor's description + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // check that the focus order of the second actor is changed to 1 + manager.SetFocusOrder(second, 1); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 1); + // make sure the change of focus order doesn't affect the actor's description + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Set the focus order and description for the third actor + Actor third = Actor::New(); + manager.SetFocusOrder(third, 1); + manager.SetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL, "third"); + DALI_TEST_CHECK(manager.GetFocusOrder(third) == 1); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // check that the focus order of the second actor is increased to 2. + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); + // make sure the change of focus order doesn't affect the actor's description + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // check that the focus order of the first actor is increased to 3. + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 3); + // make sure the change of focus order doesn't affect the actor's description + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); +} + +static void UtcDaliFocusManagerGenerateNewFocusOrder() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerGenerateNewFocusOrder"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + DALI_TEST_CHECK(1 == manager.GenerateNewFocusOrder()); + DALI_TEST_CHECK(1 == manager.GenerateNewFocusOrder()); + + Actor first = Actor::New(); + Actor second = Actor::New(); + + // Set the focus order for the first actor + manager.SetFocusOrder(first, 1); + manager.SetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 1); + + //Test for new focus order + DALI_TEST_CHECK(2 == manager.GenerateNewFocusOrder()); + + // Set the focus order for the first actor + manager.SetFocusOrder(second, 2); + manager.SetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); +} + +static void UtcDaliFocusManagerGetActorByFocusOrder() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerGetActorByFocusOrder"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the actors and set their focus orders + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + + Actor third = Actor::New(); + manager.SetFocusOrder(third, 3); + + // Check that we get an empty handle as no actor is added to the stage yet. + DALI_TEST_CHECK(manager.GetActorByFocusOrder(1) == Actor()); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(2) == Actor()); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(3) == Actor()); + + // Add the actors to the stage + Stage::GetCurrent().Add(first); + Stage::GetCurrent().Add(second); + Stage::GetCurrent().Add(third); + + // Check that we get an empty handle because focus order 0 means undefined. + DALI_TEST_CHECK(manager.GetActorByFocusOrder(0) == Actor()); + + // Check that we get correct actors for the specified focus orders + DALI_TEST_CHECK(manager.GetActorByFocusOrder(1) == first); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(2) == second); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(3) == third); + + // Change the focus order of the third actor to 1 + manager.SetFocusOrder(third, 1); + + // Check that we still get correct actors after changing their focus orders + DALI_TEST_CHECK(manager.GetActorByFocusOrder(1) == third); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(2) == first); + DALI_TEST_CHECK(manager.GetActorByFocusOrder(3) == second); + + // Check that we get an empty handle because no actor has a focus order of 4 + DALI_TEST_CHECK(manager.GetActorByFocusOrder(4) == Actor()); +} + +static void UtcDaliFocusManagerSetAndGetCurrentFocusActor() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSetAndGetCurrentFocusActor"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + Stage::GetCurrent().Add(second); + + // Create the third actor but don't add it to the stage + Actor third = Actor::New(); + manager.SetFocusOrder(third, 3); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that it will fail to set focus on an invalid actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(Actor()) == false); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Check that it will fail to set focus on the third actor as it's not in the stage + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == false); + + // Add the third actor to the stage + Stage::GetCurrent().Add(third); + + // make the third actor invisible + third.SetVisible(false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Check that it will fail to set focus on the third actor as it's invisible + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == false); + + // Make the third actor visible + third.SetVisible(true); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Make the third actor not focusable + Property::Index propertyActorFocusable = third.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + third.SetProperty(propertyActorFocusable, false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Check that it will fail to set focus on the third actor as it's not focusable + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == false); + + // Make the third actor focusable + third.SetProperty(propertyActorFocusable, true); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Check that the focus is successfully moved to the third actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == true); + + // Make the current focused actor to be not focusable by setting its focus order to be 0 + manager.SetFocusOrder(third, 0); + + // Check that the focus is automatically cleared + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Set the focus order of the third actor again + manager.SetFocusOrder(third, 3); + + // Check that the third actor can be focused successfully now + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == true); +} + +static void UtcDaliFocusManagerGetCurrentFocusGroup() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerGetCurrentFocusGroup"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create an actor with two child actors and add it to the stage + Actor parent = Actor::New(); + Actor firstChild = Actor::New(); + Actor secondChild = Actor::New(); + parent.Add(firstChild); + parent.Add(secondChild); + Stage::GetCurrent().Add(parent); + + // Create three actors and add them as the children of the first child actor + Actor firstGrandChild = Actor::New(); + Actor secondGrandChild = Actor::New(); + Actor thirdGrandChild = Actor::New(); + firstChild.Add(firstGrandChild); + firstChild.Add(secondGrandChild); + firstChild.Add(thirdGrandChild); + + // Set focus order to the actors + manager.SetFocusOrder(parent, 1); + manager.SetFocusOrder(firstChild, 2); + manager.SetFocusOrder(firstGrandChild, 3); + manager.SetFocusOrder(secondGrandChild, 4); + manager.SetFocusOrder(thirdGrandChild, 5); + manager.SetFocusOrder(secondChild, 6); + + // Set the parent and the first child actor as focus groups + manager.SetFocusGroup(parent, true); + DALI_TEST_CHECK(manager.IsFocusGroup(parent) == true); + + // Set focus to the first grand child actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(firstGrandChild) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstGrandChild); + + // The current focus group should be the parent, As it is the immediate parent which is also a focus group. + DALI_TEST_CHECK(manager.GetCurrentFocusGroup() == parent); + + manager.SetFocusGroup(firstChild, true); + DALI_TEST_CHECK(manager.IsFocusGroup(firstChild) == true); + + // The current focus group should be the firstChild, As it is the immediate parent which is also a focus group. + DALI_TEST_CHECK(manager.GetCurrentFocusGroup() == firstChild); + + manager.SetFocusGroup(firstGrandChild, true); + DALI_TEST_CHECK(manager.IsFocusGroup(firstGrandChild) == true); + + // The current focus group should be itself, As it is also a focus group. + DALI_TEST_CHECK(manager.GetCurrentFocusGroup() == firstGrandChild); + + // Set focus to the second grand child actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(secondGrandChild) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == secondGrandChild); + + // The current focus group should be the firstChild, As it is the immediate parent which is also a + // focus group for the current focus actor. + DALI_TEST_CHECK(manager.GetCurrentFocusGroup() == firstChild); + +} + +static void UtcDaliFocusManagerGetCurrentFocusOrder() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerGetCurrentFocusOrder"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor first = Actor::New(); + Stage::GetCurrent().Add(first); + + Actor second = Actor::New(); + Stage::GetCurrent().Add(second); + + Actor third = Actor::New(); + Stage::GetCurrent().Add(third); + + // Set the focus order and description for the first actor + manager.SetFocusOrder(first, 1); + manager.SetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 1); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(second, 2); + manager.SetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL, "second"); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(third, 3); + manager.SetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL, "third"); + DALI_TEST_CHECK(manager.GetFocusOrder(third) == 3); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusOrder() == 0); + + // Set the focus on the first actor and test + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusOrder() == 1); + + // Move the focus forward to the second actor and test + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusOrder() == 2); + + // Move the focus forward to the third actor and test + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusOrder() == 3); + + // Clear focus and test + manager.ClearFocus(); + DALI_TEST_CHECK(manager.GetCurrentFocusOrder() == 0); +} + +static void UtcDaliFocusManagerMoveFocusForward() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerMoveFocusForward"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor first = Actor::New(); + Stage::GetCurrent().Add(first); + + Actor second = Actor::New(); + Stage::GetCurrent().Add(second); + + Actor third = Actor::New(); + Stage::GetCurrent().Add(third); + + // Set the focus order and description for the first actor + manager.SetFocusOrder(first, 1); + manager.SetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 1); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(second, 2); + manager.SetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL, "second"); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(third, 3); + manager.SetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL, "third"); + DALI_TEST_CHECK(manager.GetFocusOrder(third) == 3); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Set the focus on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Test the non-wrapped move first + manager.SetWrapMode(false); + DALI_TEST_CHECK(manager.GetWrapMode() == false); + + // Move the focus forward to the second actor + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Move the focus forward to the third actor + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Check that it will fail to move the focus forward again as the third actor is the last + // focusable actor in the focus chain + manager.MoveFocusForward(); + // The focus should still be set on the third actor + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Now test the wrapped move + manager.SetWrapMode(true); + DALI_TEST_CHECK(manager.GetWrapMode() == true); + + // Move the focus forward recursively and this time the first actor should be focused + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Make the second actor not focusable + Property::Index propertyActorFocusable = second.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + second.SetProperty(propertyActorFocusable, false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Move the focus forward and check that the second actor should be skipped and + // the third actor should be focused now. + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Make the first actor invisible + first.SetVisible(false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Move the focus forward and check that the first actor should be skipped as it's + // invisible and the second actor should also be skipped as it's not focusable, + // so the focus will still be on the third actor + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Make the third actor invisible so that no actor can be focused. + third.SetVisible(false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Check that the focus move is failed as all the three actors can not be focused + manager.MoveFocusForward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); +} + +static void UtcDaliFocusManagerMoveFocusBackward() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerMoveFocusBackward"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor first = Actor::New(); + Stage::GetCurrent().Add(first); + + Actor second = Actor::New(); + Stage::GetCurrent().Add(second); + + Actor third = Actor::New(); + Stage::GetCurrent().Add(third); + + // Set the focus order and description for the first actor + manager.SetFocusOrder(first, 1); + manager.SetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL, "first"); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 1); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(first, FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(second, 2); + manager.SetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL, "second"); + DALI_TEST_CHECK(manager.GetFocusOrder(second) == 2); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(second, FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Set the focus order and description for the second actor + manager.SetFocusOrder(third, 3); + manager.SetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL, "third"); + DALI_TEST_CHECK(manager.GetFocusOrder(third) == 3); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(third, FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Set the focus on the third actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Test the non-wrapped move first + manager.SetWrapMode(false); + DALI_TEST_CHECK(manager.GetWrapMode() == false); + + // Move the focus backward to the second actor + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "second"); + + // Move the focus backward to the first actor + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Check that it will fail to move the focus backward again as the first actor is the first + // focusable actor in the focus chain + manager.MoveFocusBackward(); + // The focus should still be set on the first actor + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Now test the wrapped move + manager.SetWrapMode(true); + DALI_TEST_CHECK(manager.GetWrapMode() == true); + + // Move the focus backward recursively and this time the third actor should be focused + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "third"); + + // Make the second actor not focusable + Property::Index propertyActorFocusable = second.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + second.SetProperty(propertyActorFocusable, false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Move the focus backward and check that the second actor should be skipped and + // the first actor should be focused now. + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Make the third actor invisible + third.SetVisible(false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Move the focus backward and check that the third actor should be skipped as it's + // invisible and the second actor should also be skipped as it's not focusable, + // so the focus will still be on the first actor + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); + + // Make the first actor invisible so that no actor can be focused. + first.SetVisible(false); + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Check that the focus move is failed as all the three actors can not be focused + manager.MoveFocusBackward(); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(manager.GetAccessibilityAttribute(manager.GetCurrentFocusActor(), FocusManager::ACCESSIBILITY_LABEL) == "first"); +} + +static void UtcDaliFocusManagerClearFocus() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerClearFocus"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + Stage::GetCurrent().Add(second); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Clear the focus + manager.ClearFocus(); + + // Check that no actor is being focused now. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); +} + +static void UtcDaliFocusManagerReset() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerReset"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + Stage::GetCurrent().Add(second); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Clear the focus + manager.Reset(); + + // Check that no actor is being focused now and the focus order of actors have been cleared + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 0); + DALI_TEST_CHECK(manager.GetFocusOrder(first) == 0); +} + +static void UtcDaliFocusManagerFocusGroup() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerFocusGroup"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create an actor with two child actors and add it to the stage + Actor parent = Actor::New(); + Actor firstChild = Actor::New(); + Actor secondChild = Actor::New(); + parent.Add(firstChild); + parent.Add(secondChild); + Stage::GetCurrent().Add(parent); + + // Create three actors and add them as the children of the first child actor + Actor firstGrandChild = Actor::New(); + Actor secondGrandChild = Actor::New(); + Actor thirdGrandChild = Actor::New(); + firstChild.Add(firstGrandChild); + firstChild.Add(secondGrandChild); + firstChild.Add(thirdGrandChild); + + // Set focus order to the actors + manager.SetFocusOrder(parent, 1); + manager.SetFocusOrder(firstChild, 2); + manager.SetFocusOrder(firstGrandChild, 3); + manager.SetFocusOrder(secondGrandChild, 4); + manager.SetFocusOrder(thirdGrandChild, 5); + manager.SetFocusOrder(secondChild, 6); + + // Set the parent and the first child actor as focus groups + manager.SetFocusGroup(parent, true); + DALI_TEST_CHECK(manager.IsFocusGroup(parent) == true); + + // The focus group of the parent should be itself, as it is set to be a focus group. + DALI_TEST_CHECK(manager.GetFocusGroup(parent) == parent); + + // The focus group of the firstChild should be its parent, as it is the immediate parent which is also a group. + DALI_TEST_CHECK(manager.GetFocusGroup(firstChild) == parent); + + manager.SetFocusGroup(firstChild, true); + DALI_TEST_CHECK(manager.IsFocusGroup(firstChild) == true); + + // The focus group of the firstChild should be itself, as it is set to be a focus group now. + DALI_TEST_CHECK(manager.GetFocusGroup(firstChild) == firstChild); + + // Enable wrap mode for focus movement. + manager.SetWrapMode(true); + DALI_TEST_CHECK(manager.GetWrapMode() == true); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that the focus is set on the parent actor. + DALI_TEST_CHECK(manager.SetCurrentFocusActor(parent) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == parent); + + // Check that group mode is disabled. + DALI_TEST_CHECK(manager.GetGroupMode() == false); + + // Check that the focus movement is wrapped as normal. + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstGrandChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == secondGrandChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == thirdGrandChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == secondChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == parent); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstGrandChild); + + // Enable the group mode. + manager.SetGroupMode(true); + DALI_TEST_CHECK(manager.GetGroupMode() == true); + + // Check that the focus movement is now limited to the current focus group. + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == secondGrandChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == thirdGrandChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstChild); + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstGrandChild); +} + +static void UtcDaliFocusManagerSetAndGetFocusIndicator() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSetAndGetFocusIndicator"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor defaultFocusIndicatorActor = manager.GetFocusIndicatorActor(); + DALI_TEST_CHECK(defaultFocusIndicatorActor); + + Actor newFocusIndicatorActor = Actor::New(); + manager.SetFocusIndicatorActor(newFocusIndicatorActor); + DALI_TEST_CHECK(manager.GetFocusIndicatorActor() == newFocusIndicatorActor); +} + +static void UtcDaliFocusManagerSignalFocusChanged() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSignalFocusChanged"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + bool signalVerified = false; + FocusChangedCallback callback(signalVerified); + manager.FocusChangedSignal().Connect( &callback, &FocusChangedCallback::Callback ); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + Stage::GetCurrent().Add(second); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + // Clear the focus + manager.ClearFocus(); + + // Check that no actor is being focused now. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + DALI_TEST_CHECK(callback.mSignalVerified); +} + +static void UtcDaliFocusManagerSignalFocusOvershot() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliFocusManagerSignalFocusOvershot"); + + FocusManager manager = FocusManager::Get(); + DALI_TEST_CHECK(manager); + + bool signalVerified = false; + FocusOvershotCallback callback(signalVerified); + manager.FocusOvershotSignal().Connect(&callback, &FocusOvershotCallback::Callback); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + manager.SetFocusOrder(first, 1); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + manager.SetFocusOrder(second, 2); + Stage::GetCurrent().Add(second); + + // Check that the wrap mode is disabled + DALI_TEST_CHECK(manager.GetWrapMode() == false); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is moved to the second actor successfully. + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Check that the forward focus movement is overshot. + callback.mCurrentFocusedActor = second; + callback.mFocusOvershotDirection = Toolkit::FocusManager::OVERSHOT_NEXT; + DALI_TEST_CHECK(manager.MoveFocusForward() == false); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(signalVerified); + callback.Reset(); + + // Enable the wrap mode + manager.SetWrapMode(true); + DALI_TEST_CHECK(manager.GetWrapMode() == true); + + // Check that the forward focus movement is wrapped and no overshot happens. + DALI_TEST_CHECK(manager.MoveFocusForward() == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(signalVerified == false); + + // Disable the wrap mode + manager.SetWrapMode(false); + DALI_TEST_CHECK(manager.GetWrapMode() == false); + + // Check that the backward focus movement is overshot. + callback.mCurrentFocusedActor = first; + callback.mFocusOvershotDirection = Toolkit::FocusManager::OVERSHOT_PREVIOUS; + DALI_TEST_CHECK(manager.MoveFocusBackward() == false); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(signalVerified); +} diff --git a/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyInputFocusManager.cpp b/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyInputFocusManager.cpp new file mode 100644 index 0000000..68dd8b3 --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyInputFocusManager.cpp @@ -0,0 +1,336 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ + +/** + * Callback class for KeyInputFocusChanged signal. + */ +class KeyInputFocusChangedCallback : public Dali::ConnectionTracker +{ +public: + /** + * Constructor + * @param[in] gainActor Ref to the actor that should be set as the one that gains key input focus. + * @param[in] lostActor Ref to the actor that should be set as the one that loses key input focus. + */ + KeyInputFocusChangedCallback( Control& gainActor, Control& lostActor ) + : mActorGain( gainActor ), + mActorLost( lostActor ) + { + } + + void Callback( Control gainingActor, Control lostActor ) + { + mActorGain = gainingActor; + mActorLost = lostActor; + } + + Control& mActorGain; + Control& mActorLost; +}; + +// Stores data that is populated in the callback and will be read by the TET cases +struct SignalData +{ + SignalData() + : functorCalled(false) + {} + + void Reset() + { + functorCalled = false; + + receivedKeyEvent.keyModifier = 0; + receivedKeyEvent.keyPressedName.clear(); + receivedKeyEvent.keyPressed.clear(); + + } + + bool functorCalled; + KeyEvent receivedKeyEvent; +}; + +/** + * Callback class to test SignalUnhandledKeyEvent signal + */ +class SignalUnhandledKeyEventCallback : public Dali::ConnectionTracker +{ +public: + SignalUnhandledKeyEventCallback( SignalData& data ) : mSignalData( data ) { } + + void Callback(const KeyEvent& event) + { + mSignalData.functorCalled = true; + mSignalData.receivedKeyEvent = event; + } + + SignalData& mSignalData; +}; + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliKeyInputFocusManagerGet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerSetFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerGetCurrentFocusControl, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerRemoveFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerIsKeyboardListener, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerSignalKeyInputFocusChanged, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyInputFocusManagerSignalUnhandledKeyEvent, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliKeyInputFocusManagerGet() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyInputFocusManagerGet"); + + KeyInputFocusManager manager; + { + manager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(manager); + } + + KeyInputFocusManager newManager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(newManager); + + // Check that focus manager is a singleton + DALI_TEST_CHECK(manager == newManager); +} + +static void UtcDaliKeyInputFocusManagerSetFocus() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliKeyInputFocusManagerSetFocus"); + + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(manager); + + PushButton pushButton1 = PushButton::New(); + stage.Add( pushButton1 ); + + manager.SetFocus(pushButton1); + DALI_TEST_CHECK(pushButton1.HasKeyInputFocus()); +} + +static void UtcDaliKeyInputFocusManagerGetCurrentFocusControl() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliKeyInputFocusManagerGetCurrentFocusControl"); + + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(manager); + + PushButton pushButton1 = PushButton::New(); + PushButton pushButton2 = PushButton::New(); + stage.Add( pushButton1 ); + stage.Add( pushButton2 ); + + manager.SetFocus(pushButton1); + DALI_TEST_CHECK(pushButton1 == manager.GetCurrentFocusControl()); + + manager.SetFocus(pushButton2); + DALI_TEST_CHECK(pushButton2 == manager.GetCurrentFocusControl()); + + manager.SetFocus(pushButton1); + DALI_TEST_CHECK(pushButton1 == manager.GetCurrentFocusControl()); +} + +static void UtcDaliKeyInputFocusManagerRemoveFocus() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliKeyInputFocusManagerRemoveFocus"); + + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(manager); + + PushButton pushButton1 = PushButton::New(); + PushButton pushButton2 = PushButton::New(); + stage.Add( pushButton1 ); + stage.Add( pushButton2 ); + + manager.SetFocus(pushButton1); + DALI_TEST_CHECK(pushButton1 == manager.GetCurrentFocusControl()); + + manager.SetFocus(pushButton2); + DALI_TEST_CHECK(pushButton2 == manager.GetCurrentFocusControl()); + + manager.RemoveFocus(pushButton2); + DALI_TEST_CHECK(pushButton1 == manager.GetCurrentFocusControl()); + + manager.RemoveFocus(pushButton1); + DALI_TEST_CHECK(Control() == manager.GetCurrentFocusControl()); +} + +static void UtcDaliKeyInputFocusManagerIsKeyboardListener() +{ + ToolkitTestApplication application; + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliKeyInputFocusManagerIsKeyboardListener"); + + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + DALI_TEST_CHECK(manager); + + PushButton pushButton1 = PushButton::New(); + PushButton pushButton2 = PushButton::New(); + stage.Add( pushButton1 ); + stage.Add( pushButton2 ); + + manager.SetFocus(pushButton1); + DALI_TEST_CHECK(pushButton1 == manager.GetCurrentFocusControl()); + + manager.SetFocus(pushButton2); + DALI_TEST_CHECK(pushButton2 == manager.GetCurrentFocusControl()); + + DALI_TEST_CHECK(manager.IsKeyboardListener(pushButton1)); + DALI_TEST_CHECK(manager.IsKeyboardListener(pushButton2)); + + manager.RemoveFocus(pushButton2); + DALI_TEST_CHECK(!manager.IsKeyboardListener(pushButton2)); + + manager.RemoveFocus(pushButton1); + DALI_TEST_CHECK(!manager.IsKeyboardListener(pushButton1)); + + manager.SetFocus(pushButton2); + DALI_TEST_CHECK(manager.IsKeyboardListener(pushButton2)); + pushButton2.ClearKeyInputFocus(); + DALI_TEST_CHECK(!manager.IsKeyboardListener(pushButton2)); +} + +static void UtcDaliKeyInputFocusManagerSignalKeyInputFocusChanged() +{ + ToolkitTestApplication application; + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + Stage stage = Stage::GetCurrent(); + + tet_infoline(" UtcDaliKeyInputFocusManagerSignalKeyInputFocusChanged"); + + PushButton pushButton1 = PushButton::New(); + PushButton pushButton2 = PushButton::New(); + + stage.Add( pushButton1 ); + stage.Add( pushButton2 ); + + PushButton gainActor, lostActor; + KeyInputFocusChangedCallback callback( gainActor, lostActor ); + manager.KeyInputFocusChangedSignal().Connect( &callback, &KeyInputFocusChangedCallback::Callback ); + + manager.SetFocus(pushButton1); + + DALI_TEST_CHECK( gainActor == pushButton1 ); + DALI_TEST_CHECK( lostActor == Control() ); + + gainActor = lostActor = NULL; + + manager.SetFocus(pushButton2); + + DALI_TEST_CHECK( gainActor == pushButton2 ); + DALI_TEST_CHECK( lostActor == pushButton1 ); + + gainActor = lostActor = NULL; + + // Removing the focus actor from the stage would also result in signal emission. + stage.Remove( pushButton1 ); + stage.Remove( pushButton2 ); + + DALI_TEST_CHECK( gainActor == Control() ); + DALI_TEST_CHECK( lostActor == Control() ); +} + +static void UtcDaliKeyInputFocusManagerSignalUnhandledKeyEvent() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliKeyInputFocusManagerSignalUnhandledKeyEvent"); + + SignalData data; + SignalUnhandledKeyEventCallback callback( data ); + + KeyInputFocusManager manager = KeyInputFocusManager::Get(); + manager.UnhandledKeyEventSignal().Connect( &callback, &SignalUnhandledKeyEventCallback::Callback ); + + Dali::Integration::Core& core = application.GetCore(); + + Integration::KeyEvent event("a", "a", 0, 0, 0, Integration::KeyEvent::Up); + core.SendEvent(event); + + DALI_TEST_CHECK(data.functorCalled); + DALI_TEST_CHECK(event.keyName == data.receivedKeyEvent.keyPressedName ); + DALI_TEST_CHECK(event.keyCode == data.receivedKeyEvent.keyCode); + DALI_TEST_CHECK(event.keyString == data.receivedKeyEvent.keyPressed ); + DALI_TEST_CHECK(event.state == data.receivedKeyEvent.state ); + + data.Reset(); + + Integration::KeyEvent event2("v", "v", 0, 0, 0, Integration::KeyEvent::Up); + core.SendEvent(event2); + + DALI_TEST_CHECK(data.functorCalled); + DALI_TEST_CHECK(event2.keyName == data.receivedKeyEvent.keyPressedName ); + DALI_TEST_CHECK(event2.keyCode == data.receivedKeyEvent.keyCode); + DALI_TEST_CHECK(event2.keyString == data.receivedKeyEvent.keyPressed ); +} diff --git a/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyboardFocusManager.cpp b/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyboardFocusManager.cpp new file mode 100644 index 0000000..6f4d447 --- /dev/null +++ b/automated-tests/dali-test-suite/focus-manager/utc-Dali-KeyboardFocusManager.cpp @@ -0,0 +1,738 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ + +// Functors to test whether PreFocusChange signal is emitted when the keyboard focus is about to change +class PreFocusChangeCallback : public Dali::ConnectionTracker +{ +public: + PreFocusChangeCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mCurrentFocusedActor(), + mProposedActorToFocus(), + mDirection(Control::Left) + { + } + + Actor Callback(Actor currentFocusedActor, Actor proposedActorToFocus, Control::KeyboardFocusNavigationDirection direction) + { + tet_infoline("Verifying PreFocusChangeCallback()"); + + mSignalVerified = true; + + mCurrentFocusedActor = currentFocusedActor; + mProposedActorToFocus = proposedActorToFocus; + mDirection = direction; + + return mProposedActorToFocus; + } + + void Reset() + { + mSignalVerified = false; + mCurrentFocusedActor = Actor(); + mProposedActorToFocus = Actor(); + mDirection = Control::Left; + } + + bool& mSignalVerified; + Actor mCurrentFocusedActor; + Actor mProposedActorToFocus; + Control::KeyboardFocusNavigationDirection mDirection; +}; + +// Functors to test whether focus changed signal is emitted when the keyboard focus is changed +class FocusChangedCallback : public Dali::ConnectionTracker +{ +public: + FocusChangedCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mOriginalFocusedActor(), + mCurrentFocusedActor() + { + } + + void Callback(Actor originalFocusedActor, Actor currentFocusedActor) + { + tet_infoline("Verifying FocusChangedCallback()"); + + if(originalFocusedActor == mCurrentFocusedActor) + { + mSignalVerified = true; + } + + mOriginalFocusedActor = originalFocusedActor; + mCurrentFocusedActor = currentFocusedActor; + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + Actor mOriginalFocusedActor; + Actor mCurrentFocusedActor; +}; + +// Functors to test whether focus group changed signal is emitted when the keyboard focus group is changed +class FocusGroupChangedCallback : public Dali::ConnectionTracker +{ +public: + FocusGroupChangedCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mCurrentFocusedActor(), + mForward(true) + { + } + + void Callback(Actor currentFocusedActor, bool forward) + { + tet_infoline("Verifying FocusGroupChangedCallback()"); + + mSignalVerified = true; + + mCurrentFocusedActor = currentFocusedActor; + mForward = forward; + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + Actor mCurrentFocusedActor; + bool mForward; +}; + +// Functors to test whether focused actor activated signal is emitted when the focused actor is activated +class FocusedActorActivatedCallback : public Dali::ConnectionTracker +{ +public: + FocusedActorActivatedCallback(bool& signalReceived) + : mSignalVerified(signalReceived), + mActivatedActor() + { + } + + void Callback(Actor activatedActor) + { + tet_infoline("Verifying FocusedActorActivatedCallback()"); + + mSignalVerified = true; + + mActivatedActor = activatedActor; + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + Actor mActivatedActor; +}; + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliKeyboardFocusManagerGet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSetAndGetCurrentFocusActor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerMoveFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerClearFocus, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSetAndGetFocusGroupLoop, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSetAsFocusGroup, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerGetFocusGroup, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSetAndGetFocusIndicator, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSignalFocusGroupChanged, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliKeyboardFocusManagerSignalFocusedActorActivated, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliKeyboardFocusManagerGet() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardKeyboardFocusManagerGet"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager; + + manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + KeyboardFocusManager newManager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(newManager); + + // Check that focus manager is a singleton + DALI_TEST_CHECK(manager == newManager); +} + +static void UtcDaliKeyboardFocusManagerSetAndGetCurrentFocusActor() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSetAndGetCurrentFocusActor"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + first.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + second.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(second); + + // Create the third actor but don't add it to the stage + Actor third = Actor::New(); + + // Check that no actor is being focused yet. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); + + // Check that it will fail to set focus on an invalid actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(Actor()) == false); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Check that it will fail to set focus on the third actor as it's not in the stage + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == false); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Add the third actor to the stage + Stage::GetCurrent().Add(third); + + // Check that it will fail to set focus on the third actor as it's not focusable + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == false); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Make the third actor focusable + third.SetKeyboardFocusable(true); + + // Check that the focus is successfully moved to the third actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(third) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); +} + +static void UtcDaliKeyboardFocusManagerMoveFocus() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerMoveFocus"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + bool preFocusChangeSignalVerified = false; + PreFocusChangeCallback preFocusChangeCallback(preFocusChangeSignalVerified); + manager.PreFocusChangeSignal().Connect( &preFocusChangeCallback, &PreFocusChangeCallback::Callback ); + + bool focusChangedSignalVerified = false; + FocusChangedCallback focusChangedCallback(focusChangedSignalVerified); + manager.FocusChangedSignal().Connect( &focusChangedCallback, &FocusChangedCallback::Callback ); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + first.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + second.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(second); + + // Move the focus to the right + DALI_TEST_CHECK(manager.MoveFocus(Control::Right) == false); + + // Because no layout control in the stage and no actor is focused, it should emit the PreFocusChange signal + DALI_TEST_CHECK(preFocusChangeCallback.mSignalVerified); + DALI_TEST_CHECK(preFocusChangeCallback.mCurrentFocusedActor == Actor()); + DALI_TEST_CHECK(preFocusChangeCallback.mProposedActorToFocus == Actor()); + DALI_TEST_CHECK(preFocusChangeCallback.mDirection == Control::Right); + preFocusChangeCallback.Reset(); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == Actor()); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first); + focusChangedCallback.Reset(); + + // Move the focus towards right + DALI_TEST_CHECK(manager.MoveFocus(Control::Right) == false); + + // Because no layout control in the stage and the first actor is focused, it should emit the PreFocusChange signal + DALI_TEST_CHECK(preFocusChangeCallback.mSignalVerified); + DALI_TEST_CHECK(preFocusChangeCallback.mCurrentFocusedActor == first); + DALI_TEST_CHECK(preFocusChangeCallback.mProposedActorToFocus == Actor()); + DALI_TEST_CHECK(preFocusChangeCallback.mDirection == Control::Right); + preFocusChangeCallback.Reset(); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == first); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == second); + focusChangedCallback.Reset(); + + // Move the focus towards up + DALI_TEST_CHECK(manager.MoveFocus(Control::Up) == false); + + // Because no layout control in the stage and no actor is focused, it should emit the PreFocusChange signal + DALI_TEST_CHECK(preFocusChangeCallback.mSignalVerified); + DALI_TEST_CHECK(preFocusChangeCallback.mCurrentFocusedActor == second); + DALI_TEST_CHECK(preFocusChangeCallback.mProposedActorToFocus == Actor()); + DALI_TEST_CHECK(preFocusChangeCallback.mDirection == Control::Up); + preFocusChangeCallback.Reset(); + DALI_TEST_CHECK(!focusChangedCallback.mSignalVerified); + + // Create a 2x2 table view and try to move focus inside it + TableView tableView = TableView::New( 2, 2 ); + Stage::GetCurrent().Add(tableView); + + // Create the third actor + Actor third = Actor::New(); + third.SetKeyboardFocusable(true); + + // Create the fourth actor + Actor fourth = Actor::New(); + fourth.SetKeyboardFocusable(true); + + // Add the four children to table view + tableView.AddChild(first, TableView::CellPosition(0, 0)); + tableView.AddChild(second, TableView::CellPosition(0, 1)); + tableView.AddChild(third, TableView::CellPosition(1, 0)); + tableView.AddChild(fourth, TableView::CellPosition(1, 1)); + + // Set the focus to the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == second); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first); + focusChangedCallback.Reset(); + + // Move the focus towards right + DALI_TEST_CHECK(manager.MoveFocus(Control::Right) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == first); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == second); + focusChangedCallback.Reset(); + + // Move the focus towards down + DALI_TEST_CHECK(manager.MoveFocus(Control::Down) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == fourth); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == second); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == fourth); + focusChangedCallback.Reset(); + + // Move the focus towards left + DALI_TEST_CHECK(manager.MoveFocus(Control::Left) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == third); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == fourth); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == third); + focusChangedCallback.Reset(); + + // Move the focus towards up + DALI_TEST_CHECK(manager.MoveFocus(Control::Up) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == third); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == first); + focusChangedCallback.Reset(); + + // Move the focus towards left. The focus move will fail as no way to move it upwards + DALI_TEST_CHECK(manager.MoveFocus(Control::Left) == false); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + DALI_TEST_CHECK(preFocusChangeCallback.mSignalVerified); + DALI_TEST_CHECK(preFocusChangeCallback.mCurrentFocusedActor == first); + DALI_TEST_CHECK(preFocusChangeCallback.mProposedActorToFocus == Actor()); + DALI_TEST_CHECK(preFocusChangeCallback.mDirection == Control::Left); + preFocusChangeCallback.Reset(); + DALI_TEST_CHECK(!focusChangedCallback.mSignalVerified); + + // Enable the loop + manager.SetFocusGroupLoop(true); + DALI_TEST_CHECK(manager.GetFocusGroupLoop() == true); + + // Move the focus towards left again. The focus should move to the fourth actor. + DALI_TEST_CHECK(manager.MoveFocus(Control::Left) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == fourth); + DALI_TEST_CHECK(focusChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusChangedCallback.mOriginalFocusedActor == first); + DALI_TEST_CHECK(focusChangedCallback.mCurrentFocusedActor == fourth); + focusChangedCallback.Reset(); +} + +static void UtcDaliKeyboardFocusManagerClearFocus() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerClearFocus"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create the first actor and add it to the stage + Actor first = Actor::New(); + first.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(first); + + // Create the second actor and add it to the stage + Actor second = Actor::New(); + second.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(second); + + // Check that the focus is set on the first actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(first) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == first); + + // Check that the focus is set on the second actor + DALI_TEST_CHECK(manager.SetCurrentFocusActor(second) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == second); + + // Clear the focus + manager.ClearFocus(); + + // Check that no actor is being focused now. + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == Actor()); +} + +static void UtcDaliKeyboardFocusManagerSetAndGetFocusGroupLoop() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSetAndGetFocusGroupLoop"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Check that the focus movement is not looped within the same focus group by default + DALI_TEST_CHECK(manager.GetFocusGroupLoop() == false); + + // Enable the loop + manager.SetFocusGroupLoop(true); + DALI_TEST_CHECK(manager.GetFocusGroupLoop() == true); +} + +static void UtcDaliKeyboardFocusManagerSetAsFocusGroup() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSetAsFocusGroup"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create an actor and check that it is not a focus group by default + Actor actor = Actor::New(); + DALI_TEST_CHECK(manager.IsFocusGroup(actor) == false); + + // Set the actor as focus group + manager.SetAsFocusGroup(actor, true); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(manager.IsFocusGroup(actor) == true); + + // Set the actor not as focus group + manager.SetAsFocusGroup(actor, false); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(manager.IsFocusGroup(actor) == false); +} + +static void UtcDaliKeyboardFocusManagerGetFocusGroup() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerGetFocusGroup"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + // Create an actor with two child actors and add it to the stage + Actor parent = Actor::New(); + Actor child = Actor::New(); + parent.Add(child); + Stage::GetCurrent().Add(parent); + + // Create three actors and add them as the children of the first child actor + Actor grandChild = Actor::New(); + child.Add(grandChild); + + // Set the parent and the first child actor as focus groups + manager.SetAsFocusGroup(parent, true); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(manager.IsFocusGroup(parent) == true); + + // The current focus group should be the parent, As it is the immediate parent which is also a focus group. + DALI_TEST_CHECK(manager.GetFocusGroup(grandChild) == parent); + + manager.SetAsFocusGroup(child, true); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(manager.IsFocusGroup(child) == true); + + // The focus group should be the child, As it is the immediate parent which is also a focus group. + DALI_TEST_CHECK(manager.GetFocusGroup(grandChild) == child); + + manager.SetAsFocusGroup(grandChild, true); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(manager.IsFocusGroup(grandChild) == true); + + // The current focus group should be itself, As it is also a focus group. + DALI_TEST_CHECK(manager.GetFocusGroup(grandChild) == grandChild); +} + +static void UtcDaliKeyboardFocusManagerSetAndGetFocusIndicator() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSetAndGetFocusIndicator"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + Actor defaultFocusIndicatorActor = manager.GetFocusIndicatorActor(); + DALI_TEST_CHECK(defaultFocusIndicatorActor); + + Actor newFocusIndicatorActor = Actor::New(); + manager.SetFocusIndicatorActor(newFocusIndicatorActor); + DALI_TEST_CHECK(manager.GetFocusIndicatorActor() == newFocusIndicatorActor); +} + +static void UtcDaliKeyboardFocusManagerSignalFocusGroupChanged() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSignalFocusGroupChanged"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + bool focusGroupChangedSignalVerified = false; + FocusGroupChangedCallback focusGroupChangedCallback(focusGroupChangedSignalVerified); + manager.FocusGroupChangedSignal().Connect( &focusGroupChangedCallback, &FocusGroupChangedCallback::Callback ); + + Dali::Integration::Core& core = application.GetCore(); + Integration::KeyEvent tabEvent("Tab", "", 0, 0, 0, Integration::KeyEvent::Down); + Integration::KeyEvent shiftTabEvent("Tab", "", 1, 0, 0, Integration::KeyEvent::Down); + + // Send the tab event to change focus group in the forward direction + core.SendEvent(tabEvent); + DALI_TEST_CHECK(focusGroupChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusGroupChangedCallback.mCurrentFocusedActor == Actor()); + DALI_TEST_CHECK(focusGroupChangedCallback.mForward == true); + focusGroupChangedCallback.Reset(); + + // Send the shift tab event to change focus group in the backward direction + core.SendEvent(shiftTabEvent); + DALI_TEST_CHECK(focusGroupChangedCallback.mSignalVerified); + DALI_TEST_CHECK(focusGroupChangedCallback.mCurrentFocusedActor == Actor()); + DALI_TEST_CHECK(focusGroupChangedCallback.mForward == false); + focusGroupChangedCallback.Reset(); +} + +static void UtcDaliKeyboardFocusManagerSignalFocusedActorActivated() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliKeyboardFocusManagerSignalFocusedActorActivated"); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "KeyboardFocusManager" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + + KeyboardFocusManager manager = KeyboardFocusManager::Get(); + DALI_TEST_CHECK(manager); + + bool focusedActorActivatedSignalVerified = false; + FocusedActorActivatedCallback focusedActorActivatedCallback(focusedActorActivatedSignalVerified); + manager.FocusedActorActivatedSignal().Connect( &focusedActorActivatedCallback, &FocusedActorActivatedCallback::Callback ); + + Dali::Integration::Core& core = application.GetCore(); + Integration::KeyEvent returnEvent("Return", "", 0, 0, 0, Integration::KeyEvent::Up); + + // Create the first button and add it to the stage + PushButton firstPushButton = PushButton::New(); + firstPushButton.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(firstPushButton); + + // Create the second button and add it to the stage + PushButton secondPushButton = PushButton::New(); + secondPushButton.SetKeyboardFocusable(true); + Stage::GetCurrent().Add(secondPushButton); + + // Check that the focus is set on the first button + DALI_TEST_CHECK(manager.SetCurrentFocusActor(firstPushButton) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == firstPushButton); + + // Send the return event to activate the first button + core.SendEvent(returnEvent); + DALI_TEST_CHECK(focusedActorActivatedCallback.mSignalVerified); + DALI_TEST_CHECK(focusedActorActivatedCallback.mActivatedActor == firstPushButton); + focusedActorActivatedCallback.Reset(); + + // Check that the focus is set on the second button + DALI_TEST_CHECK(manager.SetCurrentFocusActor(secondPushButton) == true); + DALI_TEST_CHECK(manager.GetCurrentFocusActor() == secondPushButton); + + // Send the return event again to activate the second button + core.SendEvent(returnEvent); + DALI_TEST_CHECK(focusedActorActivatedCallback.mSignalVerified); + DALI_TEST_CHECK(focusedActorActivatedCallback.mActivatedActor == secondPushButton); + focusedActorActivatedCallback.Reset(); +} diff --git a/automated-tests/dali-test-suite/item-view/.gitignore b/automated-tests/dali-test-suite/item-view/.gitignore new file mode 100644 index 0000000..390c9e4 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/.gitignore @@ -0,0 +1,8 @@ +utc-Dali-ItemView +utc-Dali-ItemLayout +utc-Dali-GridLayout +utc-Dali-DepthLayout +utc-Dali-SpiralLayout +utc-Dali-NavigationLayout +utc-Dali-AlbumLayout +utc-Dali-RollLayout diff --git a/automated-tests/dali-test-suite/item-view/Makefile b/automated-tests/dali-test-suite/item-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/item-view/file.list b/automated-tests/dali-test-suite/item-view/file.list new file mode 100644 index 0000000..37417ed --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/file.list @@ -0,0 +1,9 @@ +TARGETS += \ + utc-Dali-ItemView \ + utc-Dali-ItemLayout \ + utc-Dali-GridLayout \ + utc-Dali-DepthLayout \ + utc-Dali-SpiralLayout \ + utc-Dali-NavigationLayout \ + utc-Dali-AlbumLayout \ + utc-Dali-RollLayout \ diff --git a/automated-tests/dali-test-suite/item-view/tslist b/automated-tests/dali-test-suite/item-view/tslist new file mode 100644 index 0000000..e6beeab --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/tslist @@ -0,0 +1,8 @@ +/dali-test-suite/item-view/utc-Dali-ItemView +/dali-test-suite/item-view/utc-Dali-ItemLayout +/dali-test-suite/item-view/utc-Dali-GridLayout +/dali-test-suite/item-view/utc-Dali-DepthLayout +/dali-test-suite/item-view/utc-Dali-SpiralLayout +/dali-test-suite/item-view/utc-Dali-NavigationLayout +/dali-test-suite/item-view/utc-Dali-AlbumLayout +/dali-test-suite/item-view/utc-Dali-RollLayout diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-AlbumLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-AlbumLayout.cpp new file mode 100755 index 0000000..4569164 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-AlbumLayout.cpp @@ -0,0 +1,402 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +using namespace Toolkit; + +using namespace std; + + +namespace +{ + +Vector3 AlbumLayoutItemSizeFunction(const Vector3& layoutSize) +{ + float width = layoutSize.width * 0.2f; + return Vector3(width, width, width); +} + +float AlbumLayoutAlbumRadiusFunction(const Vector3& layoutSize) +{ + return layoutSize.width * 0.5f; +} +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliAlbumLayoutNew(); +static void UtcDaliAlbumSetAndGetItemSizeFunction(); +static void UtcDaliAlbumSetAndGetScrollSpeedFactor(); +static void UtcDaliAlbumSetAndGetMaximumSwipeSpeed(); +static void UtcDaliAlbumLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliAlbumSetNumOfItems(); +static void UtcDaliAlbumSetStackNum(); +static void UtcDaliAlbumSetPosition(); +static void UtcDaliAlbumSetRotationX(); +static void UtcDaliAlbumSetRotationZ(); +static void UtcDaliAlbumSetScale(); +static void UtcDaliAlbumSetColor(); +static void UtcDaliAlbumSetCenterPosition(); +static void UtcDaliAlbumSetSetCenterScale(); +static void UtcDaliAlbumSetSetCenterColor(); +static void UtcDaliAlbumSetStackPosition(); +static void UtcDaliAlbumSetSetStackScale(); +static void UtcDaliAlbumSetStackColor(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliAlbumLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetAndGetItemSizeFunction, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetAndGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetAndGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliAlbumLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetNumOfItems, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetStackNum, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetPosition, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetRotationX, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetRotationZ, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetScale, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetColor, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetCenterPosition, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetSetCenterScale, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetSetCenterColor, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetStackPosition, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetSetStackScale, POSITIVE_TC_IDX }, + { UtcDaliAlbumSetStackColor, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliAlbumLayoutNew() +{ + ToolkitTestApplication application; + + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + DALI_TEST_CHECK(albumLayout); +} + +static void UtcDaliAlbumSetAndGetItemSizeFunction() +{ + ToolkitTestApplication application; + + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + // Set the item size function + albumLayout->SetItemSizeFunction(AlbumLayoutItemSizeFunction); + + // Check whether we get the correct item size function + DALI_TEST_CHECK(albumLayout->GetItemSizeFunction() == AlbumLayoutItemSizeFunction); +} + +static void UtcDaliAlbumSetAndGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + // Set the scroll speed factor + albumLayout->SetScrollSpeedFactor(0.05f); + + // Check whether we get the correct scroll speed factor + DALI_TEST_EQUALS( albumLayout->GetScrollSpeedFactor(), 0.05f, TEST_LOCATION ); +} + +static void UtcDaliAlbumSetAndGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + // Set the maximum swipe speed + albumLayout->SetMaximumSwipeSpeed(50.0f); + + // Check whether we get the correct maximum swipe speed + DALI_TEST_EQUALS( albumLayout->GetMaximumSwipeSpeed(), 50.0f, TEST_LOCATION ); +} + +static void UtcDaliAlbumLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + // Set the flick animaiton duration + albumLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( albumLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliAlbumSetNumOfItems() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetNumOfItems(15); + + DALI_TEST_CHECK(albumLayout->GetNumOfItems() == 15); +} + +static void UtcDaliAlbumSetStackNum() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetStackNum(30); + + DALI_TEST_CHECK(albumLayout->GetStackNum() == 30); +} + +static void UtcDaliAlbumSetPosition() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + std::vector position_list; + + /*(0.0f)*/ + Vector3 pos = Vector3(850.0f,-250.0f,0.0); + position_list.push_back(pos); + + /*(1.0f)*/ + pos = Vector3(700.0f,50.0f,0.0); + position_list.push_back(pos); + + /*(2.0f)*/ + pos = Vector3(440.0f,227.0f,0.0); + position_list.push_back(pos); + + /*(4.0f)*/ + pos = Vector3(-440.0f,227.0f,0.0); + position_list.push_back(pos); + + /*(5.0f)*/ + pos = Vector3(-700.0f,50.0f,0.0); + position_list.push_back(pos); + + /*(6.0f)*/ + pos = Vector3(-850.0f,-250.0f,0.0); + position_list.push_back(pos); + + albumLayout->SetPosition(position_list); + + DALI_TEST_CHECK(albumLayout->GetPosition() == position_list); +} + +static void UtcDaliAlbumSetRotationX() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetRotationX(Math::PI/4.0f); + + DALI_TEST_CHECK(albumLayout->GetRotationX() == Math::PI/4.0f); +} + +static void UtcDaliAlbumSetRotationZ() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + std::vector rotation_list; + + /*(0.0f)*/ + float rotate = Math::PI/6.0f; + rotation_list.push_back(rotate); + + /*(1.0f)*/ + rotate = 0.0f; + rotation_list.push_back(rotate); + + /*(2.0f)*/ + rotate = Math::PI/6.0f; + rotation_list.push_back(rotate); + + /*(4.0f)*/ + rotate = -Math::PI/6.0f; + rotation_list.push_back(rotate); + + /*(5.0f)*/ + rotate = 0.0f; + rotation_list.push_back(rotate); + + /*(6.0f)*/ + rotate = -Math::PI/6.0f; + rotation_list.push_back(rotate); + + albumLayout->SetRotationZ(rotation_list); + + DALI_TEST_CHECK(albumLayout->GetRotationZ() == rotation_list); +} + +static void UtcDaliAlbumSetScale() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + std::vector scale_list; + + /*(0.0f)*/ + float scale = 1.0f; + scale_list.push_back(scale); + + /*(1.0f)*/ + scale = 0.0f; + scale_list.push_back(scale); + + /*(2.0f)*/ + scale = Math::PI/6.0f; + scale_list.push_back(scale); + + /*(4.0f)*/ + scale = -Math::PI/6.0f; + scale_list.push_back(scale); + + /*(5.0f)*/ + scale = 0.0f; + scale_list.push_back(scale); + + /*(6.0f)*/ + scale = -Math::PI/6.0f; + scale_list.push_back(scale); + + albumLayout->SetScale(scale_list); + + DALI_TEST_CHECK(albumLayout->GetScale() == scale_list); +} + +static void UtcDaliAlbumSetColor() +{ + // Create a album layout + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + std::vector color_list; + + /*(0.0f)*/ + Vector2 color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + /*(1.0f)*/ + color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + /*(2.0f)*/ + color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + /*(4.0f)*/ + color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + /*(5.0f)*/ + color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + /*(6.0f)*/ + color = Vector2(1.0f,1.0f); + color_list.push_back(color); + + albumLayout->SetColor(color_list); + + DALI_TEST_CHECK(albumLayout->GetColor() == color_list); +} + +static void UtcDaliAlbumSetCenterPosition() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetCenterPosition(Vector3( 0.0f,-80.0f,100.0f)); + + DALI_TEST_CHECK(albumLayout->GetCenterPosition() == Vector3( 0.0f,-80.0f,100.0f)); +} + +static void UtcDaliAlbumSetSetCenterScale() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetCenterScale(1.75f); + + DALI_TEST_CHECK(albumLayout->GetCenterScale() == 1.75f); +} + +static void UtcDaliAlbumSetSetCenterColor() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetCenterColor(Vector2(1.0f,1.0f)); + + DALI_TEST_CHECK(albumLayout->GetCenterColor() == Vector2(1.0f,1.0f)); +} + +static void UtcDaliAlbumSetStackPosition() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetStackPosition(Vector3(750.0f,-500.0f,0.0f),Vector3(-750.0f,-500.0f,0.0f)); + + DALI_TEST_CHECK(albumLayout->GetRightStackPosition() == Vector3(750.0f,-500.0f,0.0f) && albumLayout->GetLeftStackPosition() == Vector3(-750.0f,-500.0f,0.0f)); +} + +static void UtcDaliAlbumSetSetStackScale() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetStackScale(1.0f,1.0f); + + DALI_TEST_CHECK(albumLayout->GetRightStackScale() == 1.0f && albumLayout->GetLeftStackScale() == 1.0f); +} + +static void UtcDaliAlbumSetStackColor() +{ + AlbumLayoutPtr albumLayout = AlbumLayout::New(); + + albumLayout->SetStackColor(Vector2(1.0f,1.0f),Vector2(1.0f,1.0f)); + + DALI_TEST_CHECK(albumLayout->GetRightStackColor() == Vector2(1.0f,1.0f) && albumLayout->GetLeftStackColor() == Vector2(1.0f,1.0f)); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-DepthLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-DepthLayout.cpp new file mode 100644 index 0000000..bedbc19 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-DepthLayout.cpp @@ -0,0 +1,658 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include // for FLT_MAX +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; + +Vector3 DepthLayoutItemSizeFunction(unsigned int numberOfColumns, float layoutWidth) +{ + float width = (layoutWidth / static_cast(numberOfColumns + 1)) * 0.8f; + return Vector3(width, width, width); +} + +float DepthLayoutBottomMarginFunction(float layoutHeight) +{ + return layoutHeight * 0.25f; +} + +float DepthLayoutColumnPositionFunction(unsigned int numberOfColumns, unsigned int columnNumber, const Vector3& itemSize, float layoutWidth) +{ + float availableSpace = layoutWidth - itemSize.width * numberOfColumns; + float leftMargin = availableSpace / numberOfColumns * 0.5f; + float columnPosition = leftMargin + itemSize.width * 0.5f + columnNumber * (itemSize.width + availableSpace / numberOfColumns); + return columnPosition - layoutWidth * 0.5f; +} +} // namespace + +static void Startup(); +static void Cleanup(); + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an test actor for this item + ImageActor actor = CreateSolidColorActor(Color::RED); + actor.SetSize(64.0f, 64.0f); + + return actor; + } +}; + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliDepthLayoutNew(); +static void UtcDaliDepthLayoutSetAndGetNumberOfColumns(); +static void UtcDaliDepthLayoutSetAndGetNumberOfRows(); +static void UtcDaliDepthLayoutSetAndGetRowSpacing(); +static void UtcDaliDepthLayoutSetAndGetTiltAngle(); +static void UtcDaliDepthLayoutSetAndGetItemSizeFunction(); +static void UtcDaliDepthLayoutSetAndGetBottomMarginFunction(); +static void UtcDaliDepthLayoutSetAndGetItemTiltAngle(); +static void UtcDaliDepthLayoutSetAndGetColumnPositionFunction(); +static void UtcDaliDepthLayoutSetAndGetScrollSpeedFactor(); +static void UtcDaliDepthLayoutSetAndGetMaximumSwipeSpeed(); +static void UtcDaliDepthLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliDepthLayoutConstraintLeft(); +static void UtcDaliDepthLayoutConstraintRight(); +static void UtcDaliDepthLayoutConstraintUp(); +static void UtcDaliDepthLayoutConstraintDown(); +static void UtcDaliDepthLayoutGetScrollToPosition(); +static void UtcDaliDepthLayoutScrollDirection(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliDepthLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutScrollDirection, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetNumberOfColumns, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetNumberOfRows, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetRowSpacing, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetTiltAngle, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetItemSizeFunction, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetBottomMarginFunction, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetItemTiltAngle, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetColumnPositionFunction, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutConstraintLeft, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutConstraintRight, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutConstraintUp, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutConstraintDown, POSITIVE_TC_IDX }, + { UtcDaliDepthLayoutGetScrollToPosition, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliDepthLayoutNew() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + DALI_TEST_CHECK(depthLayout); +} + +static void UtcDaliDepthLayoutSetAndGetNumberOfColumns() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the number of columns + depthLayout->SetNumberOfColumns(5); + + // Check whether we get the correct number of columns + DALI_TEST_CHECK(depthLayout->GetNumberOfColumns() == 5); +} + +static void UtcDaliDepthLayoutSetAndGetNumberOfRows() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the number of rows + depthLayout->SetNumberOfRows(15); + + // Check whether we get the correct number of rows + DALI_TEST_CHECK(depthLayout->GetNumberOfRows() == 15); +} + +static void UtcDaliDepthLayoutSetAndGetRowSpacing() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the row spacing + depthLayout->SetRowSpacing(30.0f); + + // Check whether we get the correct row spacing + DALI_TEST_EQUALS(depthLayout->GetRowSpacing(), 30.0f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutSetAndGetTiltAngle() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the tilt angle + depthLayout->SetTiltAngle(Degree(25.0f)); + + // Check whether we get the correct tilt angle + DALI_TEST_EQUALS(float(depthLayout->GetTiltAngle()), 25.0f, 0.001f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutSetAndGetItemSizeFunction() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the item size function + depthLayout->SetItemSizeFunction(DepthLayoutItemSizeFunction); + + // Check whether we get the correct item size function + DALI_TEST_CHECK(depthLayout->GetItemSizeFunction() == DepthLayoutItemSizeFunction); +} + +static void UtcDaliDepthLayoutSetAndGetBottomMarginFunction() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the bottom margin function + depthLayout->SetBottomMarginFunction(DepthLayoutBottomMarginFunction); + + // Check whether we get the correct bottom margin function + DALI_TEST_CHECK(depthLayout->GetBottomMarginFunction() == DepthLayoutBottomMarginFunction); +} + +static void UtcDaliDepthLayoutSetAndGetItemTiltAngle() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the item's tilt angle + depthLayout->SetItemTiltAngle(Degree(5.0f)); + + // Check whether we get the correct item's tilt angle + DALI_TEST_EQUALS(float(depthLayout->GetItemTiltAngle()), 5.0f, 0.001f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutSetAndGetColumnPositionFunction() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the column position function + depthLayout->SetColumnPositionFunction(DepthLayoutColumnPositionFunction); + + // Check whether we get the correct column position function + DALI_TEST_CHECK(depthLayout->GetColumnPositionFunction() == DepthLayoutColumnPositionFunction); +} + +static void UtcDaliDepthLayoutSetAndGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the scroll speed factor + depthLayout->SetScrollSpeedFactor(0.05f); + + // Check whether we get the correct scroll speed factor + DALI_TEST_EQUALS(depthLayout->GetScrollSpeedFactor(), 0.05f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutSetAndGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the maximum swipe speed + depthLayout->SetMaximumSwipeSpeed(50.0f); + + // Check whether we get the correct maximum swipe speed + DALI_TEST_EQUALS(depthLayout->GetMaximumSwipeSpeed(), 50.0f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a depth layout + DepthLayoutPtr depthLayout = DepthLayout::New(); + + // Set the flick animaiton duration + depthLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( depthLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliDepthLayoutConstraintLeft() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr navigationLayout = DepthLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliDepthLayoutConstraintRight() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr navigationLayout = DepthLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliDepthLayoutConstraintUp() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr navigationLayout = DepthLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliDepthLayoutConstraintDown() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr navigationLayout = DepthLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliDepthLayoutGetScrollToPosition() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr layout = DepthLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view. + std::vector indices; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + indices.push_back(i); + } + } + + try + { + if (!indices.empty()) + { + const unsigned int firstTargetIndex = indices[indices.size()-1]; + // scroll to last item + view.ScrollToItem(firstTargetIndex, 0.00f); + application.Render(16); // 60hz frames + + std::size_t moveCount = 0; + for(std::size_t i = 0; i < indices.size(); i++) + { + float layoutPosBefore = view.GetCurrentLayoutPosition(i); + view.ScrollToItem(indices[i], 0.0f); + + application.Render(16); // 60hz frame + + float layoutPosAfter = view.GetCurrentLayoutPosition(i); + + if (fabs(layoutPosBefore-layoutPosAfter) <= FLT_EPSILON) + { + ++moveCount; + } + } + + DALI_TEST_CHECK((moveCount == indices.size())); + } + } + catch(...) + { + tet_result(TET_FAIL); + } + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliDepthLayoutScrollDirection() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + DepthLayoutPtr navigationLayout = DepthLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + ItemLayoutPtr layout = navigationLayout; + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + navigationLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + Degree deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 180.0f); + + navigationLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK((deg == 0.0f)); + + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 270.0f); + + navigationLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 90.0f); + + Stage::GetCurrent().Remove(view); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-GridLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-GridLayout.cpp new file mode 100644 index 0000000..34c2083 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-GridLayout.cpp @@ -0,0 +1,579 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include // for FLT_MAX +#include + +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; + +Vector3 GridLayoutItemSizeFunction(unsigned int numberOfColumns, float layoutWidth, float sideMargin, float columnSpacing) +{ + float width = (layoutWidth - sideMargin * 2.0f - columnSpacing * static_cast(numberOfColumns - 1)) / static_cast(numberOfColumns); + + return Vector3(width, width, width); +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an test actor for this item + ImageActor actor = CreateSolidColorActor(Color::RED); + actor.SetSize(64.0f, 64.0f); + return actor; + } +}; + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliGridLayoutNew(); +static void UtcDaliGridLayoutSetAndGetNumberOfColumns(); +static void UtcDaliGridLayoutSetAndGetRowSpacing(); +static void UtcDaliGridLayoutSetAndGetColumnSpacing(); +static void UtcDaliGridLayoutSetAndGetTopMargin(); +static void UtcDaliGridLayoutSetAndGetBottomMargin(); +static void UtcDaliGridLayoutSetAndGetSideMargin(); +static void UtcDaliGridLayoutSetAndGetZGap(); +static void UtcDaliGridLayoutSetAndGetItemSizeFunction(); +static void UtcDaliGridLayoutSetAndGetScrollSpeedFactor(); +static void UtcDaliGridLayoutSetAndGetMaximumSwipeSpeed(); +static void UtcDaliGridLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliGridLayoutConstraintLeft(); +static void UtcDaliGridLayoutConstraintRight(); +static void UtcDaliGridLayoutConstraintUp(); +static void UtcDaliGridLayoutConstraintDown(); +static void UtcDaliGridLayoutScrollDirection(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliGridLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetNumberOfColumns, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetRowSpacing, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetColumnSpacing, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetTopMargin, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetBottomMargin, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetSideMargin, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetZGap, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetItemSizeFunction, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutConstraintLeft, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutConstraintRight, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutConstraintUp, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutConstraintDown, POSITIVE_TC_IDX }, + { UtcDaliGridLayoutScrollDirection, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliGridLayoutNew() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + DALI_TEST_CHECK(gridLayout); +} + +static void UtcDaliGridLayoutSetAndGetNumberOfColumns() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the number of columns + gridLayout->SetNumberOfColumns(6); + + // Check whether we get the correct number of columns + DALI_TEST_CHECK(gridLayout->GetNumberOfColumns() == 6); +} + +static void UtcDaliGridLayoutSetAndGetRowSpacing() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the row spacing + gridLayout->SetRowSpacing(10.0f); + + // Check whether we get the correct row spacing + DALI_TEST_EQUALS(gridLayout->GetRowSpacing(), 10.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetColumnSpacing() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the column spacing + gridLayout->SetColumnSpacing(10.0f); + + // Check whether we get the correct column spacing + DALI_TEST_EQUALS(gridLayout->GetColumnSpacing(), 10.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetTopMargin() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the top margin + gridLayout->SetTopMargin(30.0f); + + // Check whether we get the correct top margin + DALI_TEST_EQUALS(gridLayout->GetTopMargin(), 30.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetBottomMargin() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the bottom margin + gridLayout->SetBottomMargin(30.0f); + + // Check whether we get the correct bottom margin + DALI_TEST_EQUALS(gridLayout->GetBottomMargin(), 30.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetSideMargin() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the side margin + gridLayout->SetSideMargin(10.0f); + + // Check whether we get the correct side margin + DALI_TEST_EQUALS(gridLayout->GetSideMargin(), 10.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetZGap() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the gap of items in the Z axis in different columns + gridLayout->SetZGap(5.0f); + + // Check whether we get the correct Z gap + DALI_TEST_EQUALS(gridLayout->GetZGap(), 5.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetItemSizeFunction() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the item size function + gridLayout->SetItemSizeFunction(GridLayoutItemSizeFunction); + + // Check whether we get the correct item size function + DALI_TEST_CHECK(gridLayout->GetItemSizeFunction() == GridLayoutItemSizeFunction); +} + +static void UtcDaliGridLayoutSetAndGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the scroll speed factor + gridLayout->SetScrollSpeedFactor(0.05f); + + // Check whether we get the correct scroll speed factor + DALI_TEST_EQUALS(gridLayout->GetScrollSpeedFactor(), 0.05f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the maximum swipe speed + gridLayout->SetMaximumSwipeSpeed(50.0f); + + // Check whether we get the correct maximum swipe speed + DALI_TEST_EQUALS(gridLayout->GetMaximumSwipeSpeed(), 50.0f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a grid layout + GridLayoutPtr gridLayout = GridLayout::New(); + + // Set the flick animaiton duration + gridLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( gridLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliGridLayoutConstraintLeft() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr gridLayout = GridLayout::New(); + gridLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*gridLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + gridLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliGridLayoutConstraintRight() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr gridLayout = GridLayout::New(); + gridLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*gridLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + gridLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliGridLayoutConstraintUp() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr gridLayout = GridLayout::New(); + gridLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*gridLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + gridLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + + ItemLayoutPtr layout = gridLayout; + layout->GetClosestOnScreenLayoutPosition(0, 0.0f, vec); + int nextItem = layout->GetNextFocusItemID(0, 10, Dali::Toolkit::Control::Right, false); + DALI_TEST_CHECK(nextItem == 1); + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliGridLayoutConstraintDown() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr gridLayout = GridLayout::New(); + gridLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*gridLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + gridLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliGridLayoutScrollDirection() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr gridLayout = GridLayout::New(); + gridLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*gridLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + gridLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + ItemLayoutPtr layout = gridLayout; + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + gridLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + Degree deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 0.0f); + + gridLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK((deg == 180.0f)); + + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 90.f); + + gridLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 270.0f); + + Stage::GetCurrent().Remove(view); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-ItemLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-ItemLayout.cpp new file mode 100644 index 0000000..133da1e --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-ItemLayout.cpp @@ -0,0 +1,171 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include + +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; +const char* TEST_IMAGE_FILE_NAME = DALI_IMAGE_DIR "gallery_image_01.jpg"; +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliItemLayoutSetAndGetOrientation(); +static void UtcDaliItemLayoutGetScrollHints(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliItemLayoutSetAndGetOrientation, POSITIVE_TC_IDX }, + { UtcDaliItemLayoutGetScrollHints, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an image actor for this item + Image image = Image::New( TEST_IMAGE_FILE_NAME ); + Actor actor = ImageActor::New(image); + + return actor; + } +}; + +static void UtcDaliItemLayoutSetAndGetOrientation() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Set the orientation of the layout to be horizontal from left to right + ItemLayoutPtr layout = view.GetLayout(0); + layout->SetOrientation(ControlOrientation::Left); + + // Check the orientation of the layout is horizontal from left to right + DALI_TEST_CHECK(layout->GetOrientation() == ControlOrientation::Left); +} + +static void UtcDaliItemLayoutGetScrollHints() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Set the orientation of the layout to be horizontal from left to right + ItemLayoutPtr layout = view.GetLayout(0); + + Vector2 axisScrollHint; + + layout->SetOrientation(ControlOrientation::Up); + layout->GetXAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::ZERO, Math::MACHINE_EPSILON_1, TEST_LOCATION); + layout->GetYAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::YAXIS, Math::MACHINE_EPSILON_1, TEST_LOCATION); + + layout->SetOrientation(ControlOrientation::Down); + layout->GetXAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::ZERO, Math::MACHINE_EPSILON_1, TEST_LOCATION); + layout->GetYAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::YAXIS, Math::MACHINE_EPSILON_1, TEST_LOCATION); + + layout->SetOrientation(ControlOrientation::Left); + layout->GetXAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::XAXIS, Math::MACHINE_EPSILON_1, TEST_LOCATION); + layout->GetYAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::ZERO, Math::MACHINE_EPSILON_1, TEST_LOCATION); + + layout->SetOrientation(ControlOrientation::Right); + layout->GetXAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::XAXIS, Math::MACHINE_EPSILON_1, TEST_LOCATION); + layout->GetYAxisScrollHint(axisScrollHint); + DALI_TEST_EQUALS(axisScrollHint, Vector2::ZERO, Math::MACHINE_EPSILON_1, TEST_LOCATION); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-ItemView.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-ItemView.cpp new file mode 100644 index 0000000..98f2a56 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-ItemView.cpp @@ -0,0 +1,575 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include // for FLT_MAX +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 100; +const char* TEST_IMAGE_FILE_NAME = DALI_IMAGE_DIR "gallery_image_01.jpg"; + +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliItemViewNew(); +static void UtcDaliItemViewDownCast(); +static void UtcDaliItemViewAddAndGetLayout(); +static void UtcDaliItemViewAddAndRemoveLayout(); +static void UtcDaliItemViewActivateLayoutAndGetActiveLayout(); +static void UtcDaliItemViewDeactivateCurrentLayout(); +static void UtcDaliItemViewGetItemAndGetItemId(); +static void UtcDaliItemViewRemoveItem(); +static void UtcDaliItemViewGetCurrentLayoutPosition(); +static void UtcDaliItemViewSetAndGetMinimumSwipeSpeed(); +static void UtcDaliItemViewSetAndGetMinimumSwipeDistance(); +static void UtcDaliItemViewSetAndGetAnchoring(); +static void UtcDaliItemViewSetAndGetAnchoringDuration(); +static void UtcDaliItemViewSetAndGetRefreshInterval(); +static void UtcDaliItemViewScrollToItem(); +static void UtcDaliItemViewSetAndGetMouseWheelScrollDistanceStep(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliItemViewNew, POSITIVE_TC_IDX }, + { UtcDaliItemViewDownCast, POSITIVE_TC_IDX }, + { UtcDaliItemViewAddAndGetLayout, POSITIVE_TC_IDX }, + { UtcDaliItemViewAddAndRemoveLayout, POSITIVE_TC_IDX }, + { UtcDaliItemViewActivateLayoutAndGetActiveLayout, POSITIVE_TC_IDX }, + { UtcDaliItemViewDeactivateCurrentLayout, POSITIVE_TC_IDX }, + { UtcDaliItemViewGetItemAndGetItemId, POSITIVE_TC_IDX }, + { UtcDaliItemViewRemoveItem, POSITIVE_TC_IDX }, + { UtcDaliItemViewGetCurrentLayoutPosition, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetMinimumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetMinimumSwipeDistance, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetAnchoring, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetAnchoringDuration, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetRefreshInterval, POSITIVE_TC_IDX }, + { UtcDaliItemViewScrollToItem, POSITIVE_TC_IDX }, + { UtcDaliItemViewSetAndGetMouseWheelScrollDistanceStep, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an image actor for this item + Image image = Image::New( TEST_IMAGE_FILE_NAME ); + Actor actor = ImageActor::New(image); + + return actor; + } +}; + +static void UtcDaliItemViewNew() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + DALI_TEST_CHECK(view); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + TestItemFactory factory; + ItemView view = ItemView::New(factory); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliItemViewDownCast() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + const ItemView itemViewConst = ItemView::New(factory); + ItemView itemView(itemViewConst); + + BaseHandle handle(itemView); + + ItemView newItemView = ItemView::DownCast( handle ); + DALI_TEST_CHECK( itemView ); + DALI_TEST_CHECK( newItemView == itemView ); +} + +static void UtcDaliItemViewAddAndGetLayout() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // As we have added one layout, check the number of layout is now 1 + DALI_TEST_CHECK(view.GetLayoutCount() == 1); + + // Create a depth layout and add it to ItemView + DepthLayoutPtr depthLayout = DepthLayout::New(); + view.AddLayout(*depthLayout); + + // As we have added another layout, check the number of layout is now 2 + DALI_TEST_CHECK(view.GetLayoutCount() == 2); + + // Create a spiral layout and add it to ItemView + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + view.AddLayout(*spiralLayout); + + // As we have added another layout, check the number of layout is now 3 + DALI_TEST_CHECK(view.GetLayoutCount() == 3); + + // Check we are getting the correct layout from ItemView + DALI_TEST_CHECK(view.GetLayout(0) == gridLayout); + DALI_TEST_CHECK(view.GetLayout(1) == depthLayout); + DALI_TEST_CHECK(view.GetLayout(2) == spiralLayout); +} + +static void UtcDaliItemViewAddAndRemoveLayout() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // As we have added one layout, check the number of layout is now 1 + DALI_TEST_CHECK(view.GetLayoutCount() == 1); + + // Create a depth layout and add it to ItemView + DepthLayoutPtr depthLayout = DepthLayout::New(); + view.AddLayout(*depthLayout); + + // As we have added another layout, check the number of layout is now 2 + DALI_TEST_CHECK(view.GetLayoutCount() == 2); + + // Check we are getting the correct layout from ItemView + DALI_TEST_CHECK(view.GetLayout(0) == gridLayout); + DALI_TEST_CHECK(view.GetLayout(1) == depthLayout); + + // Remove the grid layout + view.RemoveLayout(0); + + // As we have removed the grid layout, check the number of layout is now 1 + DALI_TEST_CHECK(view.GetLayoutCount() == 1); + + // Check we are getting the correct layout from ItemView + DALI_TEST_CHECK(view.GetLayout(0) == depthLayout); + + // Remove the depth layout + view.RemoveLayout(0); + + // As we also removed the depth layout, check the number of layout is now 0 + DALI_TEST_CHECK(view.GetLayoutCount() == 0); +} + +static void UtcDaliItemViewActivateLayoutAndGetActiveLayout() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Create a depth layout and add it to ItemView + DepthLayoutPtr depthLayout = DepthLayout::New(); + view.AddLayout(*depthLayout); + + // Create a spiral layout and add it to ItemView + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + view.AddLayout(*spiralLayout); + + // As we have added three layouts, check the number of layout is now 3 + DALI_TEST_CHECK(view.GetLayoutCount() == 3); + + // Check there is no active layout at the moment + DALI_TEST_CHECK(view.GetActiveLayout() == NULL); + + // Activate the depth layout + Vector3 stageSize(Dali::Stage::GetCurrent().GetSize()); + view.ActivateLayout(1, stageSize, 0.5f); + + // Check the current active layout is the depth layout + DALI_TEST_CHECK(view.GetActiveLayout() == depthLayout); + + // Activate the grid layout + view.ActivateLayout(0, stageSize, 0.5f); + + // Check the current active layout is the grid layout + DALI_TEST_CHECK(view.GetActiveLayout() == gridLayout); + + // Activate the spiral layout + view.ActivateLayout(2, stageSize, 0.5f); + + // Check the current active layout is the spiral layout + DALI_TEST_CHECK(view.GetActiveLayout() == spiralLayout); +} + +static void UtcDaliItemViewDeactivateCurrentLayout() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Check there is no active layout at the moment + DALI_TEST_CHECK(view.GetActiveLayout() == NULL); + + // Activate the grid layout + Vector3 stageSize(Dali::Stage::GetCurrent().GetSize()); + view.ActivateLayout(0, stageSize, 0.5f); + + // Check the current active layout is the grid layout + DALI_TEST_CHECK(view.GetActiveLayout() == gridLayout); + + // Deactivate the current layout + view.DeactivateCurrentLayout(); + + // Check there is no active layout at the moment + DALI_TEST_CHECK(view.GetActiveLayout() == NULL); +} + +static void UtcDaliItemViewGetItemAndGetItemId() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Activate the grid layout so that the items will be created and added to ItemView + Vector3 stageSize(Dali::Stage::GetCurrent().GetSize()); + view.ActivateLayout(0, stageSize, 0.5f); + + // Get the item given the item ID + Actor itemActor = view.GetItem(2); + + // Check we are getting the correct Item ID given the specified actor + DALI_TEST_CHECK(view.GetItemId(itemActor) == 2); +} + +static void UtcDaliItemViewRemoveItem() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Activate the grid layout so that the items will be created and added to ItemView + Vector3 stageSize(Dali::Stage::GetCurrent().GetSize()); + view.ActivateLayout(0, stageSize, 0.5f); + + // Get the item given the item ID 2 and 3 + Actor oldItemActorID2 = view.GetItem(2); + Actor oldItemActorID3 = view.GetItem(3); + + // Remove the item with ID 2 + view.RemoveItem(2, 0.0f); + + // Get the new item given the item ID 2 + Actor newItemActorID2 = view.GetItem(2); + + // Check the original item with item ID 2 was deleted and now item ID 2 represents the original item with ID 3 + DALI_TEST_CHECK(view.GetItemId(newItemActorID2) == 2); + DALI_TEST_CHECK(oldItemActorID2 != newItemActorID2); + DALI_TEST_CHECK(newItemActorID2 = oldItemActorID3); +} + +static void UtcDaliItemViewGetCurrentLayoutPosition() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Create a grid layout and add it to ItemView + GridLayoutPtr gridLayout = GridLayout::New(); + view.AddLayout(*gridLayout); + + // Activate the grid layout so that the items will be created and added to ItemView + Vector3 stageSize(Dali::Stage::GetCurrent().GetSize()); + view.ActivateLayout(0, stageSize, 0.0f); + + // Check the current layout position for the 10th items is 9.0f + DALI_TEST_EQUALS(view.GetCurrentLayoutPosition(9), 9.0f, TEST_LOCATION ); +} + +static void UtcDaliItemViewSetAndGetMinimumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Set the minimum swipe speed to be 1.5f + view.SetMinimumSwipeSpeed(1.5f); + + // Check the minimum swipe speed is 1.5f + DALI_TEST_EQUALS(view.GetMinimumSwipeSpeed(), 1.5f, TEST_LOCATION ); +} + +static void UtcDaliItemViewSetAndGetMinimumSwipeDistance() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Set the minimum swipe distance to be 2.5f + view.SetMinimumSwipeDistance(2.5f); + + // Check the minimum swipe distance is 2.5f + DALI_TEST_EQUALS(view.GetMinimumSwipeDistance(), 2.5f, TEST_LOCATION ); +} + +static void UtcDaliItemViewSetAndGetAnchoring() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Disable the anchor animation + view.SetAnchoring(false); + + // Check the anchor animation is disabled + DALI_TEST_CHECK(view.GetAnchoring() == false); +} + +static void UtcDaliItemViewSetAndGetAnchoringDuration() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Set the duration of anchor animation to be 1.5f + view.SetAnchoringDuration(1.5f); + + // Check the duration of anchor animation is 1.5f + DALI_TEST_EQUALS(view.GetAnchoringDuration(), 1.5f, TEST_LOCATION ); +} + +static void UtcDaliItemViewSetAndGetRefreshInterval() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Set the interval between refreshes to be 20 + view.SetRefreshInterval(20); + + // Check the interval between refreshes is 20 + DALI_TEST_CHECK(view.GetRefreshInterval() == 20); +} + +static void UtcDaliItemViewScrollToItem() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + GridLayoutPtr layout = GridLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view. + std::vector indices; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + indices.push_back(i); + } + } + + try + { + if (!indices.empty()) + { + const unsigned int firstTargetIndex = indices[indices.size()-1]; + // scroll to last item + view.ScrollToItem(firstTargetIndex, 0.00f); + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + std::size_t moveCount = 0; + for(std::size_t i = 0; i < indices.size(); i++) + { + float layoutPosBefore = view.GetCurrentLayoutPosition(i); + view.ScrollToItem(indices[i], 0.0f); + float layoutPosAfter = view.GetCurrentLayoutPosition(i); + + if (fabs(layoutPosBefore-layoutPosAfter) <= FLT_EPSILON) + { + ++moveCount; + } + } + + DALI_TEST_CHECK((moveCount == indices.size())); + } + } + catch(...) + { + tet_result(TET_FAIL); + } + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliItemViewSetAndGetMouseWheelScrollDistanceStep() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + + // Set the scroll distance step for the mouse wheel event to be 100.0f + view.SetMouseWheelScrollDistanceStep(100.0f); + + // Check the scroll distance step is 100.0f + DALI_TEST_EQUALS(view.GetMouseWheelScrollDistanceStep(), 100.0f, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-NavigationLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-NavigationLayout.cpp new file mode 100644 index 0000000..7f2a168 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-NavigationLayout.cpp @@ -0,0 +1,616 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include // for FLT_MAX +#include + +#include +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; +} // namespace + +static void Startup(); +static void Cleanup(); + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an test actor for this item + ImageActor actor = CreateSolidColorActor(Color::RED); + actor.SetSize(64.0f, 64.0f); + + return actor; + } +}; + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliNavigationLayoutNew(); +static void UtcDaliNavigationLayoutColumns(); +static void UtcDaliNavigationLayoutSetGetOrientation(); +static void UtcDaliNavigationLayoutTestConstraintLeft(); +static void UtcDaliNavigationLayoutTestConstraintRight(); +static void UtcDaliNavigationLayoutTestConstraintUp(); +static void UtcDaliNavigationLayoutTestConstraintDown(); +static void UtcDaliNavigationLayoutScrollDirection(); +static void UtcDaliNavigationLayoutSetGetColumnSpacing(); +static void UtcDaliNavigationLayoutSetGetTopMargin(); +static void UtcDaliNavigationLayoutSetGetBottomMargin(); +static void UtcDaliNavigationLayoutSetGetScrollSpeedFactor(); +static void UtcDaliNavigationLayoutSetGetMaximumSwipeSpeed(); +static void UtcDaliNavigationLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliNavigationLayoutGetScrollToPosition(); + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliNavigationLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutColumns, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetOrientation, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutTestConstraintLeft, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutTestConstraintRight, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutTestConstraintUp, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutTestConstraintDown, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutScrollDirection, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetColumnSpacing, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetTopMargin, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetBottomMargin, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliNavigationLayoutGetScrollToPosition, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void UtcDaliNavigationLayoutNew() +{ + ToolkitTestApplication application; + + // Create a navigation layout + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + DALI_TEST_CHECK(navigationLayout); +} + +static void UtcDaliNavigationLayoutColumns() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + + navigationLayout->SetNumberOfColumns(6); + // Check whether we get the correct number of columns + DALI_TEST_CHECK(navigationLayout->GetNumberOfColumns() == 6); +} + +static void UtcDaliNavigationLayoutSetGetOrientation() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetOrientation(ControlOrientation::Right); + DALI_TEST_CHECK(navigationLayout->GetOrientation() == ControlOrientation::Right); +} + +static void UtcDaliNavigationLayoutTestConstraintLeft() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and all of them is positioned at X = 0 + // and the series is monotonely decreasing. + int nonZeroXCount = 0; + int elementsFound = 0; + int wrongDirectionCount = 0; + float prevY = FLT_MAX; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.x != 0.0f) + { + nonZeroXCount++; + } + + if (pos.y >= prevY) + { + wrongDirectionCount++; + } + + prevY = pos.y; + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroXCount == 0) && (wrongDirectionCount == 0)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliNavigationLayoutTestConstraintRight() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and all of them is positioned at X = 0 + // and the series is monotonely increasing. + int nonZeroXCount = 0; + int elementsFound = 0; + int wrongDirectionCount = 0; + float prevY = -FLT_MAX; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.x != 0.0f) + { + nonZeroXCount++; + } + + if (pos.y <= prevY) + { + wrongDirectionCount++; + } + + prevY = pos.y; + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroXCount == 0) && (wrongDirectionCount == 0)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliNavigationLayoutTestConstraintUp() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and all of them is positioned at X = 0 + // and the series is monotonely decreasing. + int nonZeroYCount = 0; + int elementsFound = 0; + int wrongDirectionCount = 0; + float prevX = -FLT_MAX; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.y != 0.0f) + { + nonZeroYCount++; + } + + if (pos.x <= prevX) + { + wrongDirectionCount++; + } + + prevX = pos.x; + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroYCount == 0) && (wrongDirectionCount == 0)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliNavigationLayoutTestConstraintDown() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and all of them is positioned at X = 0 + // and the series is monotonely decreasing. + int nonZeroYCount = 0; + int elementsFound = 0; + int wrongDirectionCount = 0; + float prevX = FLT_MAX; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.y != 0.0f) + { + nonZeroYCount++; + } + + if (pos.x > prevX) + { + wrongDirectionCount++; + } + + prevX = pos.x; + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroYCount == 0) && (wrongDirectionCount == 0)); + Stage::GetCurrent().Remove(view); +} + + +static void UtcDaliNavigationLayoutScrollDirection() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + navigationLayout->SetNumberOfColumns(6); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + ItemLayoutPtr layout = navigationLayout; + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + navigationLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + Degree deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == (180.0f - 45.0f)); + + navigationLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK((deg == -45.0f)); + + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == (270.0f - 45.0f)); + + navigationLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == (90.0f - 45.0f)); + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliNavigationLayoutSetGetColumnSpacing() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + const float testValue = 11.0f; + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetColumnSpacing(testValue); + DALI_TEST_CHECK(navigationLayout->GetColumnSpacing() == testValue); +} + +static void UtcDaliNavigationLayoutSetGetTopMargin() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + const float testValue = 11.0f; + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetTopMargin(testValue); + DALI_TEST_CHECK(navigationLayout->GetTopMargin() == testValue); +} + +static void UtcDaliNavigationLayoutSetGetBottomMargin() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + const float testValue = 12.0f; + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetBottomMargin(testValue); + DALI_TEST_CHECK(navigationLayout->GetBottomMargin() == testValue); +} + +static void UtcDaliNavigationLayoutSetGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + const float testValue = 15.0f; + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetScrollSpeedFactor(testValue); + DALI_TEST_CHECK(navigationLayout->GetScrollSpeedFactor() == testValue); +} + +static void UtcDaliNavigationLayoutSetGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + const float testValue = 10.0f; + + navigationLayout->SetNumberOfColumns(6); + navigationLayout->SetMaximumSwipeSpeed(testValue); + DALI_TEST_CHECK(navigationLayout->GetMaximumSwipeSpeed() == testValue); +} + +static void UtcDaliNavigationLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a navigation layout + NavigationLayoutPtr navigationLayout = NavigationLayout::New(); + + // Set the flick animaiton duration + navigationLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( navigationLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliNavigationLayoutGetScrollToPosition() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + NavigationLayoutPtr layout = NavigationLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view. + std::vector indices; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + indices.push_back(i); + } + } + + try + { + if (!indices.empty()) + { + const unsigned int firstTargetIndex = indices[indices.size()-1]; + // scroll to last item + view.ScrollToItem(firstTargetIndex, 0.00f); + application.Render(16); // 60hz frames + + std::size_t moveCount = 0; + for(std::size_t i = 0; i < indices.size(); i++) + { + float layoutPosBefore = view.GetCurrentLayoutPosition(i); + view.ScrollToItem(indices[i], 0.0f); + + application.Render(16); // 60hz frame + + float layoutPosAfter = view.GetCurrentLayoutPosition(i); + + if (fabs(layoutPosBefore-layoutPosAfter) <= FLT_EPSILON) + { + ++moveCount; + } + } + + DALI_TEST_CHECK((moveCount == indices.size())); + } + } + catch(...) + { + tet_result(TET_FAIL); + } + + Stage::GetCurrent().Remove(view); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-RollLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-RollLayout.cpp new file mode 100644 index 0000000..53c4e58 --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-RollLayout.cpp @@ -0,0 +1,471 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; + +Vector3 RollLayoutItemSizeFunction(float layoutWidth, float layoutHeight, float rowSpacing) +{ + float height = (layoutHeight - rowSpacing) * 0.5f; + return Vector3(layoutWidth, height, height); +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an test actor for this item + ImageActor actor = CreateSolidColorActor(Color::RED); + actor.SetSize(64.0f, 64.0f); + return actor; + } +}; + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliRollLayoutNew(); +static void UtcDaliRollLayoutSetAndGetRowSpacing(); +static void UtcDaliRollLayoutSetAndGetItemSizeFunction(); +static void UtcDaliRollLayoutSetAndGetScrollSpeedFactor(); +static void UtcDaliRollLayoutSetAndGetMaximumSwipeSpeed(); +static void UtcDaliRollLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliRollLayoutConstraintLeft(); +static void UtcDaliRollLayoutConstraintRight(); +static void UtcDaliRollLayoutConstraintUp(); +static void UtcDaliRollLayoutConstraintDown(); +static void UtcDaliRollLayoutScrollDirection(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliRollLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutSetAndGetRowSpacing, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutSetAndGetItemSizeFunction, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutSetAndGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutSetAndGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutConstraintLeft, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutConstraintRight, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutConstraintUp, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutConstraintDown, POSITIVE_TC_IDX }, + { UtcDaliRollLayoutScrollDirection, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliRollLayoutNew() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + DALI_TEST_CHECK(rollLayout); +} + +static void UtcDaliRollLayoutSetAndGetRowSpacing() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + // Set the row spacing + rollLayout->SetRowSpacing(10.0f); + + // Check whether we get the correct row spacing + DALI_TEST_EQUALS(rollLayout->GetRowSpacing(), 10.0f, TEST_LOCATION ); +} + +static void UtcDaliRollLayoutSetAndGetItemSizeFunction() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + // Set the item size function + rollLayout->SetItemSizeFunction(RollLayoutItemSizeFunction); + + // Check whether we get the correct item size function + DALI_TEST_CHECK(rollLayout->GetItemSizeFunction() == RollLayoutItemSizeFunction); +} + +static void UtcDaliRollLayoutSetAndGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + // Set the scroll speed factor + rollLayout->SetScrollSpeedFactor(0.05f); + + // Check whether we get the correct scroll speed factor + DALI_TEST_EQUALS(rollLayout->GetScrollSpeedFactor(), 0.05f, TEST_LOCATION ); +} + +static void UtcDaliRollLayoutSetAndGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + // Set the maximum swipe speed + rollLayout->SetMaximumSwipeSpeed(50.0f); + + // Check whether we get the correct maximum swipe speed + DALI_TEST_EQUALS(rollLayout->GetMaximumSwipeSpeed(), 50.0f, TEST_LOCATION ); +} + +static void UtcDaliRollLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a roll layout + RollLayoutPtr rollLayout = RollLayout::New(); + + // Set the flick animaiton duration + rollLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( rollLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliRollLayoutConstraintLeft() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + RollLayoutPtr rollLayout = RollLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*rollLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + rollLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliRollLayoutConstraintRight() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + RollLayoutPtr rollLayout = RollLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*rollLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + rollLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliRollLayoutConstraintUp() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + RollLayoutPtr rollLayout = RollLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*rollLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + rollLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliRollLayoutConstraintDown() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + RollLayoutPtr rollLayout = RollLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*rollLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + rollLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliRollLayoutScrollDirection() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + RollLayoutPtr rollLayout = RollLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*rollLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + rollLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + ItemLayoutPtr layout = rollLayout; + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + rollLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + Degree deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 0.0f); + + rollLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK((deg == 180.0f)); + + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 90.f); + + rollLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 270.0f); + + Stage::GetCurrent().Remove(view); +} diff --git a/automated-tests/dali-test-suite/item-view/utc-Dali-SpiralLayout.cpp b/automated-tests/dali-test-suite/item-view/utc-Dali-SpiralLayout.cpp new file mode 100644 index 0000000..57ba1fe --- /dev/null +++ b/automated-tests/dali-test-suite/item-view/utc-Dali-SpiralLayout.cpp @@ -0,0 +1,606 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include // for FLT_MAX +#include + +#include +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ +const unsigned int TOTAL_ITEM_NUMBER = 200; + +Vector3 SpiralLayoutItemSizeFunction(const Vector3& layoutSize) +{ + float width = layoutSize.width * 0.2f; + return Vector3(width, width, width); +} + +float SpiralLayoutSpiralRadiusFunction(const Vector3& layoutSize) +{ + return layoutSize.width * 0.5f; +} +} // namespace + +static void Startup(); +static void Cleanup(); + +// Implementation of ItemFactory for providing actors to ItemView +class TestItemFactory : public ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + TestItemFactory() + { + } + +public: // From 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 TOTAL_ITEM_NUMBER; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + // Create an test actor for this item + ImageActor actor = CreateSolidColorActor(Color::RED); + actor.SetSize(64.0f, 64.0f); + + return actor; + } +}; + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSpiralLayoutNew(); +static void UtcDaliSpiralSetAndGetItemSizeFunction(); +static void UtcDaliSpiralSetAndGetItemSpacing(); +static void UtcDaliSpiralSetAndGetRevolutionDistance(); +static void UtcDaliSpiralSetAndGetSpiralRadiusFunction(); +static void UtcDaliSpiralSetAndGetTopItemAlignment(); +static void UtcDaliSpiralSetAndGetScrollSpeedFactor(); +static void UtcDaliSpiralSetAndGetMaximumSwipeSpeed(); +static void UtcDaliSpiralLayoutSetAndGetItemFlickAnimationDuration(); +static void UtcDaliSpiralLayoutConstraintLeft(); +static void UtcDaliSpiralLayoutConstraintRight(); +static void UtcDaliSpiralLayoutConstraintUp(); +static void UtcDaliSpiralLayoutConstraintDown(); +static void UtcDaliSpiralLayoutGetScrollToPosition(); +static void UtcDaliSpiralLayoutScrollDirection(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSpiralLayoutScrollDirection, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutNew, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetItemSizeFunction, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetItemSpacing, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetRevolutionDistance, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetSpiralRadiusFunction, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetTopItemAlignment, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetScrollSpeedFactor, POSITIVE_TC_IDX }, + { UtcDaliSpiralSetAndGetMaximumSwipeSpeed, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutSetAndGetItemFlickAnimationDuration, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutConstraintLeft, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutConstraintRight, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutConstraintUp, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutConstraintDown, POSITIVE_TC_IDX }, + { UtcDaliSpiralLayoutGetScrollToPosition, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliSpiralLayoutNew() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + DALI_TEST_CHECK(spiralLayout); +} + +static void UtcDaliSpiralSetAndGetItemSizeFunction() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the item size function + spiralLayout->SetItemSizeFunction(SpiralLayoutItemSizeFunction); + + // Check whether we get the correct item size function + DALI_TEST_CHECK(spiralLayout->GetItemSizeFunction() == SpiralLayoutItemSizeFunction); +} + +static void UtcDaliSpiralSetAndGetItemSpacing() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the item spacing + spiralLayout->SetItemSpacing(Radian(0.6f)); + + // Check whether we get the correct item spacing + DALI_TEST_EQUALS(spiralLayout->GetItemSpacing(), 0.6f, TEST_LOCATION ); +} + +static void UtcDaliSpiralSetAndGetRevolutionDistance() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the revolution distance + spiralLayout->SetRevolutionDistance(150.0f); + + // Check whether we get the correct revolution distance + DALI_TEST_EQUALS(spiralLayout->GetRevolutionDistance(), 150.0f, TEST_LOCATION ); +} + +static void UtcDaliSpiralSetAndGetSpiralRadiusFunction() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the spiral radius function + spiralLayout->SetSpiralRadiusFunction(SpiralLayoutSpiralRadiusFunction); + + // Check whether we get the correct spiral radius function + DALI_TEST_CHECK(spiralLayout->GetSpiralRadiusFunction() == SpiralLayoutSpiralRadiusFunction); +} + +static void UtcDaliSpiralSetAndGetTopItemAlignment() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the alignment of the top item + spiralLayout->SetTopItemAlignment(-0.25f); + + // Check whether we get the correct alignment of the top item + DALI_TEST_EQUALS(spiralLayout->GetTopItemAlignment(), -0.25f, TEST_LOCATION ); +} + +static void UtcDaliSpiralSetAndGetScrollSpeedFactor() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the scroll speed factor + spiralLayout->SetScrollSpeedFactor(0.05f); + + // Check whether we get the correct scroll speed factor + DALI_TEST_EQUALS(spiralLayout->GetScrollSpeedFactor(), 0.05f, TEST_LOCATION ); +} + +static void UtcDaliSpiralSetAndGetMaximumSwipeSpeed() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the maximum swipe speed + spiralLayout->SetMaximumSwipeSpeed(50.0f); + + // Check whether we get the correct maximum swipe speed + DALI_TEST_EQUALS(spiralLayout->GetMaximumSwipeSpeed(), 50.0f, TEST_LOCATION ); +} + +static void UtcDaliSpiralLayoutSetAndGetItemFlickAnimationDuration() +{ + ToolkitTestApplication application; + + // Create a spiral layout + SpiralLayoutPtr spiralLayout = SpiralLayout::New(); + + // Set the flick animaiton duration + spiralLayout->SetItemFlickAnimationDuration(0.35f); + + // Check whether we get the correct flick animaiton duration + DALI_TEST_EQUALS( spiralLayout->GetItemFlickAnimationDuration(), 0.35f, TEST_LOCATION ); +} + +static void UtcDaliSpiralLayoutConstraintLeft() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr layout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliSpiralLayoutConstraintRight() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr layout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliSpiralLayoutConstraintUp() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr layout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + + layout->GetClosestOnScreenLayoutPosition(0, 0.0f, vec); + int nextItem = layout->GetNextFocusItemID(0, 10, Dali::Toolkit::Control::Right, false); + DALI_TEST_CHECK(nextItem == 1); + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliSpiralLayoutConstraintDown() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr layout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view and they are positioned some distance from the origin. + int nonZeroCount = 0; + int elementsFound = 0; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + elementsFound++; + Vector3 pos = testActor.GetCurrentPosition(); + + if (pos.LengthSquared() > 0.0f) + { + nonZeroCount++; + } + } + } + + DALI_TEST_CHECK((elementsFound > 0) && (nonZeroCount == elementsFound)); + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliSpiralLayoutScrollDirection() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr navigationLayout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*navigationLayout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + navigationLayout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + ItemLayoutPtr layout = navigationLayout; + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + navigationLayout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + Degree deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == -45.0f); + + navigationLayout->SetOrientation(ControlOrientation::Down); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK((deg == 180.0f - 45.0f)); + + layout->SetOrientation(ControlOrientation::Left); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == 45.0f); + + navigationLayout->SetOrientation(ControlOrientation::Right); + view.ActivateLayout(0, vec, 0.0f); + application.SendNotification(); + application.Render(); + + deg = layout->GetScrollDirection(); + DALI_TEST_CHECK(deg == (270.0f - 45.0f)); + + Stage::GetCurrent().Remove(view); +} + +static void UtcDaliSpiralLayoutGetScrollToPosition() +{ + ToolkitTestApplication application; + + // Create the ItemView actor + TestItemFactory factory; + ItemView view = ItemView::New(factory); + Vector3 vec(480.0f, 800.0f, 0.0f); + SpiralLayoutPtr layout = SpiralLayout::New(); + + view.SetName("view actor"); + view.AddLayout(*layout); + view.SetSize(vec); + + Stage::GetCurrent().Add(view); + layout->SetOrientation(ControlOrientation::Up); + view.ActivateLayout(0, vec, 0.0f); + + application.SendNotification(); + application.Render(0); + + // render 10 frames + for(int i = 0; i < 10; ++i) + { + application.Render(16); // 60hz frames + } + + // Confirm: we have actors in the view. + std::vector indices; + for(unsigned int i = 0; i < 10; i++) + { + Actor testActor = view.GetItem(i); + if (testActor) + { + indices.push_back(i); + } + } + + try + { + if (!indices.empty()) + { + const unsigned int firstTargetIndex = indices[indices.size()-1]; + // scroll to last item + view.ScrollToItem(firstTargetIndex, 0.00f); + application.Render(16); // 60hz frames + + std::size_t moveCount = 0; + for(std::size_t i = 0; i < indices.size(); i++) + { + float layoutPosBefore = view.GetCurrentLayoutPosition(i); + view.ScrollToItem(indices[i], 0.0f); + + application.Render(16); // 60hz frame + + float layoutPosAfter = view.GetCurrentLayoutPosition(i); + + if (fabs(layoutPosBefore-layoutPosAfter) <= FLT_EPSILON) + { + ++moveCount; + } + } + + DALI_TEST_CHECK((moveCount == indices.size())); + } + } + catch(...) + { + tet_result(TET_FAIL); + } + + Stage::GetCurrent().Remove(view); +} diff --git a/automated-tests/dali-test-suite/master-makefile.mk b/automated-tests/dali-test-suite/master-makefile.mk new file mode 100644 index 0000000..fead8d1 --- /dev/null +++ b/automated-tests/dali-test-suite/master-makefile.mk @@ -0,0 +1,53 @@ +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd. +# +# Licensed under the Flora License, Version 1.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://floralicense.org/license/ +# +# 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. +# + +CC = g++ + +TARGETS = +include file.list + +PKGS = dali-core dali dali-toolkit dali-test-suite-utils +include ../../rules.mk +include ../../coverage.mk + +TOOLKIT_TEST_UTILS_DIR=../../dali-toolkit-test-utils/ + +CXXFLAGS += -I$(TOOLKIT_TEST_UTILS_DIR) + +TOOLKIT_TEST_UTILS_SRC_FILES = \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-application.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-adaptor.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-clipboard-event-notifier.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-accessibility-manager.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-orientation.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-physical-keyboard.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-style-monitor.cpp \ + $(TOOLKIT_TEST_UTILS_DIR)/toolkit-timer.cpp + +all: $(TARGETS) + +%: %.cpp $(TOOLKIT_TEST_UTILS_SRC_FILES) + $(CC) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + @rm -f $(notdir $(TARGETS)) + @rm -f tet_captured + @rm -f *~ + @rm -f *.gcda *.gcno + +coverage: + @lcov --directory . -c -o dali.info + @lcov --remove dali.info "*boost*" "/usr/include/*" "*/automated-tests/*" -o dali.info diff --git a/automated-tests/dali-test-suite/navigation-frame/.gitignore b/automated-tests/dali-test-suite/navigation-frame/.gitignore new file mode 100644 index 0000000..00d2413 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/.gitignore @@ -0,0 +1,2 @@ +utc-Dali-NavigationControl +utc-Dali-Page diff --git a/automated-tests/dali-test-suite/navigation-frame/Makefile b/automated-tests/dali-test-suite/navigation-frame/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/navigation-frame/file.list b/automated-tests/dali-test-suite/navigation-frame/file.list new file mode 100644 index 0000000..20ceb42 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/file.list @@ -0,0 +1,3 @@ +TARGETS += \ + utc-Dali-NavigationControl \ + utc-Dali-Page \ diff --git a/automated-tests/dali-test-suite/navigation-frame/tslist b/automated-tests/dali-test-suite/navigation-frame/tslist new file mode 100644 index 0000000..77cb9f4 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/tslist @@ -0,0 +1,2 @@ +/dali-test-suite/navigation-frame/utc-Dali-NavigationControl +/dali-test-suite/navigation-frame/utc-Dali-Page diff --git a/automated-tests/dali-test-suite/navigation-frame/utc-Dali-NavigationControl.cpp b/automated-tests/dali-test-suite/navigation-frame/utc-Dali-NavigationControl.cpp new file mode 100644 index 0000000..bc8ccb3 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/utc-Dali-NavigationControl.cpp @@ -0,0 +1,388 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +} + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliNavigationControlNew(); +static void UtcDaliNavigationControlDownCast(); +static void UtcDaliNavigationControlPushItem(); +static void UtcDaliNavigationControlPopItem(); +static void UtcDaliNavigationControlGetItemCount(); +static void UtcDaliNavigationControlGetItem(); +static void UtcDaliNavigationControlGetCurrentItem(); +static void UtcDaliNavigationControlSetBackground(); +static void UtcDaliNavigationControlCreateNavigationToolBar(); +static void UtcDaliNavigationControlCreateNavigationTitleBar(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliNavigationControlNew, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlDownCast, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlPushItem, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlPopItem, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlGetItemCount, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlGetItem, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlGetCurrentItem, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlSetBackground, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlCreateNavigationToolBar, POSITIVE_TC_IDX }, + { UtcDaliNavigationControlCreateNavigationTitleBar, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliNavigationControlNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliNavigationControlNew"); + + NavigationControl naviControl; + // Check that this handle is uninitialized + DALI_TEST_CHECK( !naviControl ); + + naviControl = NavigationControl::New(); + // Check that the Dali resource is successfully created + DALI_TEST_CHECK( naviControl ); + + NavigationControl naviControl2( naviControl ); + DALI_TEST_CHECK( naviControl2 == naviControl ); + + //Additional check to ensure object is created by checking whether it is registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( TestCallback ); + { + NavigationControl naviControl = NavigationControl::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliNavigationControlDownCast() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlDownCast" ); + + NavigationControl naviControl = NavigationControl::New(); + BaseHandle handle( naviControl ); + + NavigationControl newNaviControl = NavigationControl::DownCast( handle ); + DALI_TEST_CHECK( naviControl ); + DALI_TEST_CHECK( newNaviControl == naviControl ); +} + +static void UtcDaliNavigationControlPushItem() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlPushItem" ); + + // Create a NavigationControl object, and add it to stage + NavigationControl naviControl = NavigationControl::New(); + Stage::GetCurrent().Add(naviControl); + // Check there is no item in the stack + DALI_TEST_CHECK( naviControl.GetItemCount() == 0 ); + + // Create two NavigationItem objects + Page firstItem = Page::New(); + Page secondItem = Page::New(); + + // Push the first item into stack + naviControl.PushItem( firstItem ); + // Check the item count in stack + DALI_TEST_CHECK( naviControl.GetItemCount() == 1 ); + // Check the current item + DALI_TEST_CHECK( naviControl.GetCurrentItem() == firstItem ); + // Check that the newly pushed item is displayed on stage + DALI_TEST_CHECK( firstItem.OnStage() ); + + // Push the second item into stack + naviControl.PushItem( secondItem ); + // Check the item count in stack + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + // Check the current item + DALI_TEST_CHECK( naviControl.GetCurrentItem() == secondItem ); + // Check the bottom item in the stack + DALI_TEST_CHECK( naviControl.GetItem(0) == firstItem ); + // Check that the previous item is off stage + DALI_TEST_CHECK( !firstItem.OnStage() ); + // Check that the newly pushed item is displayed on stage + DALI_TEST_CHECK( secondItem.OnStage() ); + + Page thirdItem; + Page fourthItem(secondItem); + naviControl.PushItem( thirdItem ); + // Check that an uninitialized item cannot be pushed into the stack + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + naviControl.PushItem( fourthItem ); + // Check that an duplicated item with the current item cannot be pushed into the stack + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + // Check that the current item and the item on the stage is still the secondItem + DALI_TEST_CHECK( naviControl.GetCurrentItem() == secondItem ); + DALI_TEST_CHECK( secondItem.OnStage() ); +} + +static void UtcDaliNavigationControlPopItem() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlPopItem" ); + + // Create a NavigationControl object, and add it to stage + NavigationControl naviControl = NavigationControl::New(); + Stage::GetCurrent().Add(naviControl); + // Create three NavigationItem objects + Page firstItem = Page::New(); + Page secondItem = Page::New(); + Page thirdItem = Page::New(); + naviControl.PushItem( firstItem ); + naviControl.PushItem( secondItem ); + naviControl.PushItem( thirdItem ); + + DALI_TEST_CHECK( naviControl.GetItemCount() == 3 ); + + // pop an item out from the stack + Page poppedItem = naviControl.PopItem(); + // check that the item count is decrease by one + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + // check that the item popped out is the thirdItem + DALI_TEST_CHECK( poppedItem == thirdItem ); + // check that the item popped out is disappeared from the stage + DALI_TEST_CHECK( !poppedItem.OnStage() ); + // check that the new top item is displayed on the stage + DALI_TEST_CHECK( secondItem.OnStage() ); + + // repeat the above steps again + poppedItem = naviControl.PopItem(); + DALI_TEST_CHECK( naviControl.GetItemCount() == 1 ); + DALI_TEST_CHECK( poppedItem == secondItem ); + DALI_TEST_CHECK( !poppedItem.OnStage() ); + DALI_TEST_CHECK( firstItem.OnStage() ); + + // check that the bottom-most item can not be popped out from the stack + poppedItem = naviControl.PopItem(); + // when trying to pop the bottom-most item, it returns an uninitialized handle and does nothing else + DALI_TEST_CHECK( !poppedItem ); + DALI_TEST_CHECK( naviControl.GetItemCount() == 1 ); + DALI_TEST_CHECK( firstItem.OnStage() ); +} + +static void UtcDaliNavigationControlGetItemCount() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlGetItemCount" ); + + // Create a NavigationControl object + NavigationControl naviControl = NavigationControl::New(); + // Create three NavigationItem objects + Page firstItem = Page::New(); + Page secondItem = Page::New(); + Page thirdItem = Page::New(); + + DALI_TEST_CHECK( naviControl.GetItemCount() == 0 ); + naviControl.PushItem( firstItem ); + DALI_TEST_CHECK( naviControl.GetItemCount() == 1 ); + naviControl.PushItem( secondItem ); + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + naviControl.PushItem( thirdItem ); + DALI_TEST_CHECK( naviControl.GetItemCount() == 3 ); + naviControl.PopItem(); + DALI_TEST_CHECK( naviControl.GetItemCount() == 2 ); + naviControl.PopItem(); + DALI_TEST_CHECK( naviControl.GetItemCount() == 1 ); +} + +static void UtcDaliNavigationControlGetItem() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlGetItem" ); + + // Create a NavigationControl object + NavigationControl naviControl = NavigationControl::New(); + // Create three NavigationItem objects and push them into stack + Page firstItem = Page::New(); + Page secondItem = Page::New(); + Page thirdItem = Page::New(); + naviControl.PushItem( firstItem ); + naviControl.PushItem( secondItem ); + naviControl.PushItem( thirdItem ); + + // check every item by get it by index + DALI_TEST_CHECK( naviControl.GetItem(0) == firstItem ); + DALI_TEST_CHECK( naviControl.GetItem(1) == secondItem ); + DALI_TEST_CHECK( naviControl.GetItem(2) == thirdItem); +} + +static void UtcDaliNavigationControlGetCurrentItem() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlGetCurrentItem" ); + + // Create a NavigationControl object + NavigationControl naviControl = NavigationControl::New(); + // Create three NavigationItem objects + Page firstItem = Page::New(); + Page secondItem = Page::New(); + Page thirdItem = Page::New(); + + naviControl.PushItem( firstItem ); + DALI_TEST_CHECK( naviControl.GetCurrentItem() == firstItem ); + naviControl.PushItem( secondItem ); + DALI_TEST_CHECK( naviControl.GetCurrentItem() == secondItem ); + naviControl.PushItem( thirdItem ); + DALI_TEST_CHECK( naviControl.GetCurrentItem() == thirdItem ); + naviControl.PopItem(); + DALI_TEST_CHECK( naviControl.GetCurrentItem() == secondItem ); + naviControl.PopItem(); + DALI_TEST_CHECK( naviControl.GetCurrentItem() == firstItem ); +} + +static void UtcDaliNavigationControlSetBackground() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlSetBackground" ); + + try + { + NavigationControl naviControl = NavigationControl::New(); + Stage::GetCurrent().Add( naviControl ); + + ImageActor background = CreateSolidColorActor( Color::RED ); + naviControl.SetBackground( background ); + } + catch (Dali::DaliException& e) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "segmentIndex+1 < mKnots.size() && segmentIndex < mKnots.size()", TEST_LOCATION); + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} + +static void UtcDaliNavigationControlCreateNavigationToolBar() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlCreateNavigationToolBar" ); + + ImageActor background = CreateSolidColorActor( Color::RED ); + Stage stage = Stage::GetCurrent(); + + NavigationControl naviControl = NavigationControl::New(); + stage.Add( naviControl ); + + Toolkit::NaviToolBarStyle toolbarStyle( background, 720, 98, 496, 182, 72, 16, 63, 26); + + naviControl.CreateNavigationToolBar( toolbarStyle, toolbarStyle); + + Page naviItem = Page::New(); + PushButton firstControl = PushButton::New(); + naviItem.AddControlToToolBar(firstControl, Alignment::HorizontalLeft); + PushButton secondControl = PushButton::New(); + naviItem.AddControlToToolBar(secondControl, Alignment::HorizontalCenter); + PushButton thirdControl = PushButton::New(); + naviItem.AddControlToToolBar(thirdControl, Alignment::HorizontalCenter); + PushButton fourthControl = PushButton::New(); + naviItem.AddControlToToolBar(fourthControl, Alignment::HorizontalRight); + PushButton fifthControl = PushButton::New(); + naviItem.AddControlToToolBar(fifthControl, Alignment::HorizontalRight); + + naviControl.PushItem( naviItem ); + + DALI_TEST_CHECK( firstControl.OnStage() ); + // Can add multiple controls to the central group + DALI_TEST_CHECK( secondControl.OnStage() ); + DALI_TEST_CHECK( thirdControl.OnStage() ); + // Can only have one control in the side groups + DALI_TEST_CHECK( !fourthControl.OnStage() ); + DALI_TEST_CHECK( fifthControl.OnStage() ); + +} + +static void UtcDaliNavigationControlCreateNavigationTitleBar() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliNavigationControlCreateNavigationTitleBar" ); + + ImageActor background = CreateSolidColorActor( Color::RED ); + TextStyle textStyle; + Stage stage = Stage::GetCurrent(); + + NavigationControl naviControl = NavigationControl::New(); + stage.Add( naviControl ); + + Toolkit::NaviTitleBarStyle titleBarStyle( background, textStyle, textStyle, 720, 111, 68, 48, 34, 16, 11, 45, 63, 26, 14, 22 ); + naviControl.CreateNavigationTitleBar( titleBarStyle, titleBarStyle ); + + Page naviItem = Page::New(); + + PushButton firstControl = PushButton::New(); + naviItem.AddControlToTitleBar( firstControl ); + PushButton secondControl = PushButton::New(); + naviItem.AddControlToTitleBar( secondControl ); + + Actor titleIcon = Actor::New(); + naviItem.SetTitleIcon( titleIcon ); + + naviControl.PushItem( naviItem ); + + DALI_TEST_CHECK( firstControl.OnStage() ); + DALI_TEST_CHECK( secondControl.OnStage() ); + DALI_TEST_CHECK( titleIcon.OnStage() ); +} diff --git a/automated-tests/dali-test-suite/navigation-frame/utc-Dali-Page.cpp b/automated-tests/dali-test-suite/navigation-frame/utc-Dali-Page.cpp new file mode 100644 index 0000000..ce7d214 --- /dev/null +++ b/automated-tests/dali-test-suite/navigation-frame/utc-Dali-Page.cpp @@ -0,0 +1,262 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +} + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliPageNew(); +static void UtcDaliPageDownCast(); +static void UtcDaliPageSetGetTitle(); +static void UtcDaliPageSetGetSubTitle(); +static void UtcDaliPageSetGetTitleIcon(); +static void UtcDaliPageAddGetToolBarControl(); +static void UtcDaliPageAddGetTitleBarControl(); +static void UtcDaliPageSetGetPopupMenu(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliPageNew, POSITIVE_TC_IDX }, + { UtcDaliPageDownCast, POSITIVE_TC_IDX }, + { UtcDaliPageSetGetTitle, POSITIVE_TC_IDX }, + { UtcDaliPageSetGetSubTitle, POSITIVE_TC_IDX }, + { UtcDaliPageSetGetTitleIcon, POSITIVE_TC_IDX }, + { UtcDaliPageAddGetToolBarControl, POSITIVE_TC_IDX }, + { UtcDaliPageAddGetTitleBarControl, POSITIVE_TC_IDX }, + { UtcDaliPageSetGetPopupMenu, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliPageNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliPageNew"); + + Page naviItem; + // Check that this handle is uninitialized + DALI_TEST_CHECK( !naviItem ); + + naviItem = Page::New(); + // Check that the Dali resource is successfully created + DALI_TEST_CHECK( naviItem ); + + Page naviItem2( naviItem ); + DALI_TEST_CHECK( naviItem2 == naviItem ); + + // Additional check to ensure the object is created by checking whether it is registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( TestCallback ); + { + Page naviItem = Page::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliPageDownCast() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageDownCast" ); + + Page naviItem = Page::New(); + BaseHandle handle( naviItem ); + + Page newNaviItem = Page::DownCast( handle ); + DALI_TEST_CHECK( naviItem ); + DALI_TEST_CHECK( newNaviItem == naviItem ); +} + +static void UtcDaliPageSetGetTitle() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageSetGetTitle" ); + + Page naviItem = Page::New(); + DALI_TEST_CHECK( naviItem.GetTitle().empty() ); + + std::string str( "ItemTitle" ); + naviItem.SetTitle( str ); + DALI_TEST_CHECK( naviItem.GetTitle() == str ); +} + +static void UtcDaliPageSetGetSubTitle() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageSetGetSubTitle" ); + + Page naviItem = Page::New(); + DALI_TEST_CHECK( naviItem.GetSubTitle().empty() ); + + std::string str( "ItemSubTitle" ); + naviItem.SetSubTitle( str ); + DALI_TEST_CHECK( naviItem.GetSubTitle() == str ); +} + +static void UtcDaliPageSetGetTitleIcon() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageSetGetTitleIcon" ); + + Page naviItem = Page::New(); + DALI_TEST_CHECK( !naviItem.GetTitleIcon() ); + + Actor titleIcon = Actor::New(); + naviItem.SetTitleIcon( titleIcon ); + DALI_TEST_CHECK( naviItem.GetTitleIcon() == titleIcon ); +} + +static void UtcDaliPageAddGetToolBarControl() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageAddGetToolBarControl" ); + + Page naviItem = Page::New(); + Page::ControlOnBarContainer container = naviItem.GetControlsOnToolBar(); + // Check that the container is empty in the beginning + DALI_TEST_CHECK( container.empty() ); + + // Add control, check whether adding successfully, also check the container size + PushButton firstControl = PushButton::New(); + DALI_TEST_CHECK( naviItem.AddControlToToolBar(firstControl, Alignment::HorizontalLeft) ); + container = naviItem.GetControlsOnToolBar(); + DALI_TEST_CHECK( container.size() == 1 ); + + // Add control, check whether adding successfully, also check the container size + PushButton secondControl = PushButton::New(); + DALI_TEST_CHECK( naviItem.AddControlToToolBar(secondControl, Alignment::HorizontalCenter) ); + container = naviItem.GetControlsOnToolBar(); + DALI_TEST_CHECK( container.size() == 2 ); + + // The control adding fails, as the alignment is not HorizontalLeft/HorizontalCenter/HorizontalRight + PushButton thirdControl = PushButton::New(); + DALI_TEST_CHECK( !naviItem.AddControlToToolBar(thirdControl, Alignment::VerticalCenter) ); + container = naviItem.GetControlsOnToolBar(); + DALI_TEST_CHECK( container.size() == 2 ); + + // Add control, check whether adding successfully, also check the container size + PushButton fourthControl = PushButton::New(); + DALI_TEST_CHECK( naviItem.AddControlToToolBar(fourthControl, Alignment::HorizontalRight) ); + container = naviItem.GetControlsOnToolBar(); + DALI_TEST_CHECK( container.size() == 3 ); + + // The control adding fails, as the control itself is uninitialized + PushButton fifthControl; + DALI_TEST_CHECK( !naviItem.AddControlToToolBar(fifthControl, Alignment::HorizontalCenter) ); + container = naviItem.GetControlsOnToolBar(); + DALI_TEST_CHECK( container.size() == 3 ); + + // check the content of the three successfully added ControlOnBar objects + DALI_TEST_CHECK( container[0]->control == firstControl ); + DALI_TEST_CHECK( container[0]->alignment == Alignment::HorizontalLeft ); + DALI_TEST_CHECK( container[1]->control == secondControl ); + DALI_TEST_CHECK( container[1]->alignment == Alignment::HorizontalCenter ); + DALI_TEST_CHECK( container[2]->control == fourthControl ); + DALI_TEST_CHECK( container[2]->alignment == Alignment::HorizontalRight ); +} + +static void UtcDaliPageAddGetTitleBarControl() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageAddGetTitleBarControl" ); + + Page naviItem = Page::New(); + ActorContainer container = naviItem.GetControlsOnTitleBar(); + // Check that the container is empty in the beginning + DALI_TEST_CHECK( container.empty() ); + + // Add control, check whether adding successfully, also check the container size + PushButton firstControl = PushButton::New(); + DALI_TEST_CHECK( naviItem.AddControlToTitleBar(firstControl) ); + container = naviItem.GetControlsOnTitleBar(); + DALI_TEST_CHECK( container.size() == 1 ); + + // The control adding fails, as the control itself is uninitialized + PushButton secondControl; + DALI_TEST_CHECK( !naviItem.AddControlToTitleBar(secondControl) ); + container = naviItem.GetControlsOnTitleBar(); + DALI_TEST_CHECK( container.size() == 1 ); + + // Add control, check whether adding successfully, also check the container size + PushButton thirdControl = PushButton::New(); + DALI_TEST_CHECK( naviItem.AddControlToTitleBar(thirdControl) ); + container = naviItem.GetControlsOnTitleBar(); + DALI_TEST_CHECK( container.size() == 2 ); + + // check the content of the three successfully added Controls + DALI_TEST_CHECK( container[0] == firstControl ); + DALI_TEST_CHECK( container[1] == thirdControl ); +} + +static void UtcDaliPageSetGetPopupMenu() +{ + ToolkitTestApplication application; + tet_infoline( "UtcDaliPageSetGetPopupMenu" ); + + Page naviItem = Page::New(); + DALI_TEST_CHECK( !naviItem.GetPopupMenu() ); + + Toolkit::Popup menu = Toolkit::Popup::New(); + naviItem.SetPopupMenu( menu ); + DALI_TEST_CHECK( menu == naviItem.GetPopupMenu() ); +} diff --git a/automated-tests/dali-test-suite/page-turn-view/.gitignore b/automated-tests/dali-test-suite/page-turn-view/.gitignore new file mode 100644 index 0000000..eb12504 --- /dev/null +++ b/automated-tests/dali-test-suite/page-turn-view/.gitignore @@ -0,0 +1 @@ +utc-Dali-PageTurnView diff --git a/automated-tests/dali-test-suite/page-turn-view/Makefile b/automated-tests/dali-test-suite/page-turn-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/page-turn-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/page-turn-view/file.list b/automated-tests/dali-test-suite/page-turn-view/file.list new file mode 100644 index 0000000..dfbf699 --- /dev/null +++ b/automated-tests/dali-test-suite/page-turn-view/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-PageTurnView \ diff --git a/automated-tests/dali-test-suite/page-turn-view/tslist b/automated-tests/dali-test-suite/page-turn-view/tslist new file mode 100644 index 0000000..adceb9d --- /dev/null +++ b/automated-tests/dali-test-suite/page-turn-view/tslist @@ -0,0 +1 @@ +/dali-test-suite/page-turn-view/utc-Dali-PageTurnView diff --git a/automated-tests/dali-test-suite/page-turn-view/utc-Dali-PageTurnView.cpp b/automated-tests/dali-test-suite/page-turn-view/utc-Dali-PageTurnView.cpp new file mode 100644 index 0000000..5e59616 --- /dev/null +++ b/automated-tests/dali-test-suite/page-turn-view/utc-Dali-PageTurnView.cpp @@ -0,0 +1,664 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS) +const unsigned int TOTAL_PAGE_NUMBER = 20; +const Vector2 PAGE_SIZE( 300.f,400.f ); +const unsigned int IMAGE_WIDTH = 30; +const unsigned int IMAGE_HEIGHT = 30; +const Vector2 IMAGE_SIZE( static_cast( IMAGE_WIDTH ), static_cast(IMAGE_HEIGHT) ); +const Vector2 SPINE_SHADOW_PARAMETER( 60.0f, 30.0f ); + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +/* + * Simulate time passed by. + * + * @note this will always process at least 1 frame (1/60 sec) + * + * @param application Test application instance + * @param duration Time to pass in milliseconds. + * @return The actual time passed in milliseconds + */ +int Wait(ToolkitTestApplication& application, int duration = 0) +{ + int time = 0; + + for(int i = 0; i <= ( duration / RENDER_FRAME_INTERVAL); i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + time += RENDER_FRAME_INTERVAL; + } + + return time; +} + +// Generate a PanGestureEvent to send to Core +Integration::PanGestureEvent GeneratePan( + Gesture::State state, + const Vector2& previousPosition, + const Vector2& currentPosition, + unsigned long timeDelta, + unsigned int numberOfTouches = 1) +{ + Integration::PanGestureEvent pan(state); + + pan.previousPosition = previousPosition; + pan.currentPosition = currentPosition; + pan.timeDelta = timeDelta; + pan.numberOfTouches = numberOfTouches; + + return pan; +} + +/** + * Helper to generate PanGestureEvent + * + * @param[in] application Application instance + * @param[in] state The Gesture State + * @param[in] pos The current position of touch. + */ +static void SendPan(ToolkitTestApplication& application, Gesture::State state, const Vector2& pos) +{ + static Vector2 last; + + if( (state == Gesture::Started) || + (state == Gesture::Possible) ) + { + last.x = pos.x; + last.y = pos.y; + } + + application.GetCore().SendEvent(GeneratePan(state, last, pos, RENDER_FRAME_INTERVAL)); + + last.x = pos.x; + last.y = pos.y; +} + +static Vector2 PerformGestureDiagonalSwipe(ToolkitTestApplication& application, Vector2 start, Vector2 direction, int frames, bool toStart = true, bool toFinish = true) +{ + // Now do a pan starting from (start) and heading (direction) + Vector2 pos(start); + + if( toStart ) + { + SendPan(application, Gesture::Possible, pos); + Wait(application); + SendPan(application, Gesture::Started, pos); + Wait(application); + } + + for(int i = 0;i( &(std::ostringstream() << pageId) )->str() ); + + actor.SetParentOrigin( ParentOrigin::CENTER ); + actor.SetAnchorPoint( AnchorPoint::CENTER ); + + SetActorHittability( actor, true ); + + mSourceActors[pageId] = actor; + } + + return mSourceActors[pageId]; + } + + void DeletePage( unsigned int pageId ) + { + mSourceActors.erase( mSourceActors.begin() + pageId ); + mTotalPageNumber--; + } + +private: + ToolkitTestApplication& mApplication; + std::vector mSourceActors; + unsigned int mTotalPageNumber; +}; + + +static void UtcDaliPageTurnPortraitViewNew() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewNew "); + + // Test default constructor + PageTurnView portraitView; + DALI_TEST_CHECK( !portraitView ); + + // Test object creation + TestPageFactory factory(application); + portraitView = PageTurnPortraitView::New( factory, PAGE_SIZE ); + DALI_TEST_CHECK( portraitView ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + TestPageFactory factory(application); + PageTurnView PortraitView = PageTurnPortraitView::New( factory, PAGE_SIZE ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + // Test copy constructor + PageTurnView viewCopy( portraitView ); + DALI_TEST_CHECK( viewCopy ); + + // Test down cast + Handle handleView; + handleView = portraitView; + PageTurnView downCastView = PageTurnView::DownCast( handleView ); + DALI_TEST_CHECK( downCastView ); +} + +static void UtcDaliPageTurnLandscapeViewNew() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewNew "); + + //Test default constructor + PageTurnView landscapeView; + DALI_TEST_CHECK( !landscapeView ); + + // Test object creation + TestPageFactory factory(application); + landscapeView = PageTurnLandscapeView::New( factory, PAGE_SIZE ); + DALI_TEST_CHECK( landscapeView ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + TestPageFactory factory(application); + PageTurnView landscapeView = PageTurnLandscapeView::New( factory, PAGE_SIZE ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + // Test copy constructor + PageTurnView viewCopy( landscapeView ); + DALI_TEST_CHECK( viewCopy ); + + // Test down cast + Handle handleView; + handleView = landscapeView; + PageTurnView downCastView = PageTurnView::DownCast( handleView ); + DALI_TEST_CHECK( downCastView ); + +} + +static void UtcDaliPageTurnViewSetAndGetSpineShadowParameter() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewSetAndGetSpineShadowParameter "); + + TestPageFactory factory(application); + PageTurnView landscapeView = PageTurnLandscapeView::New( factory, PAGE_SIZE ); + DALI_TEST_CHECK( landscapeView.GetSpineShadowParameter() != SPINE_SHADOW_PARAMETER); + landscapeView.SetSpineShadowParameter(SPINE_SHADOW_PARAMETER); + DALI_TEST_CHECK( landscapeView.GetSpineShadowParameter() == SPINE_SHADOW_PARAMETER); +} + +static void UtcDaliPageTurnViewGoToPageAndGetCurrentPage() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewGoToPageAndGetCurrentPage "); + + TestPageFactory factory(application); + PageTurnView portraitView = PageTurnPortraitView::New( factory, PAGE_SIZE ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == 0 ); + + portraitView.GoToPage( 10 ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == 10 ); + + portraitView.GoToPage( 5 ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == 5 ); +} + +static void UtcDaliPageTurnViewEnterLeaveEditMode() +{ + ToolkitTestApplication application; + + tet_infoline( " UtcDaliPageTurnViewEnterLeaveEditMode " ); + + TestPageFactory factory(application); + factory.EnableOffscreenRendering( ); + + PageTurnView pageTurnView = PageTurnLandscapeView::New( factory, PAGE_SIZE ); + pageTurnView.SetPositionInheritanceMode( USE_PARENT_POSITION ); + Stage::GetCurrent().Add( pageTurnView ); + + Vector2 size = Stage::GetCurrent().GetSize(); + + pageTurnView.GoToPage(5); + + // Render and notify + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + + Actor actor = pageTurnView.EnterEditMode(); + // Test that when entering edit mode, current page source actor is returned. + unsigned int pageId; + std::istringstream( actor.GetName() ) >> pageId; + DALI_TEST_CHECK( pageId == 5 ); + + bool signalVerified; + PageTurnView currentView; + unsigned int pageIndex; + bool isTurningForwards; + PageSignalCallback callbackPanStarted( signalVerified, currentView, pageIndex, isTurningForwards ); + pageTurnView.PagePanStartedSignal().Connect( &callbackPanStarted, &PageSignalCallback::PagePanSignalCallback ); + + currentView = pageTurnView; + pageIndex = 5; + DALI_TEST_CHECK( !callbackPanStarted.mSignalVerified ); + + // Test that the control does not receive pan gesture in edit-mode + PerformGestureDiagonalSwipe( application, Vector2(size*0.75f), Vector2(size*0.01f), 10, true, true); + DALI_TEST_CHECK( !callbackPanStarted.mSignalVerified ); + + pageTurnView.LeaveEditMode(); + // Test that the control receives pan gesture after leaving edit-mode + PerformGestureDiagonalSwipe( application, Vector2(size*0.75f), Vector2(size*0.01f), 10, true, true); + DALI_TEST_CHECK( callbackPanStarted.mSignalVerified ); +} + +static void UtcDaliPageTurnViewGetHitActor() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewGetHitActor "); + + TestPageFactory factory(application); + factory.EnableOffscreenRendering( ); + + PageTurnView pageTurnView = PageTurnPortraitView::New( factory, PAGE_SIZE ); + pageTurnView.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pageTurnView.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + Stage::GetCurrent().Add( pageTurnView ); + + // Render and notify + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + + pageTurnView.GoToPage(3); + + Vector2 localCoordinate = Vector2(); + Vector2 screenCoordinate = PAGE_SIZE*0.5f+Vector2(7.f,8.f); + Actor hitActor = pageTurnView.GetHitActor( screenCoordinate, localCoordinate ); + DALI_TEST_CHECK( hitActor ); + unsigned int pageId; + std::istringstream( hitActor.GetName() ) >> pageId; + DALI_TEST_CHECK( pageId == 3 ); + DALI_TEST_EQUALS( localCoordinate, IMAGE_SIZE*0.5f+Vector2(7.f,8.f), 0.1f, TEST_LOCATION ); + + screenCoordinate = PAGE_SIZE*0.5f+IMAGE_SIZE; + hitActor = pageTurnView.GetHitActor( screenCoordinate, localCoordinate ); + DALI_TEST_CHECK( !hitActor ); +} + +static void UtcDaliPageTurnViewRefresh() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewRefresh "); + + TestPageFactory factory(application); + factory.EnableOffscreenRendering( ); + PageTurnView pageTurnView = PageTurnPortraitView::New( factory, PAGE_SIZE ); + pageTurnView.SetParentOrigin( ParentOrigin::TOP_LEFT ); + pageTurnView.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + Stage::GetCurrent().Add( pageTurnView ); + + // Render and notify + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + application.Render(RENDER_FRAME_INTERVAL); + application.SendNotification(); + + factory.DeletePage( 0 ); + + pageTurnView.RefreshCurrentPage(); + // simply calls the certain off screen render task to refresh + Vector2 localCoordinate = Vector2(); + Vector2 screenCoordinate = PAGE_SIZE*0.5f; + Actor hitActor = pageTurnView.GetHitActor( screenCoordinate, localCoordinate ); + unsigned int pageId; + std::istringstream( hitActor.GetName() ) >> pageId; + DALI_TEST_CHECK( pageId == 0 ); + + pageTurnView.RefreshAll(); + // re-parent all the source actors and refresh + hitActor = pageTurnView.GetHitActor( screenCoordinate, localCoordinate ); + std::istringstream( hitActor.GetName() ) >> pageId; + DALI_TEST_CHECK( pageId == 1 ); +} + +static void UtcDaliPageTurnViewSignals() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliPageTurnViewSignals "); + + TestPageFactory factory(application); + Vector2 size = Stage::GetCurrent().GetSize(); + PageTurnView portraitView = PageTurnPortraitView::New( factory, size ); + portraitView.SetPositionInheritanceMode( USE_PARENT_POSITION ); + Stage::GetCurrent().Add( portraitView ); + + // Render and notify + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + + // [0]: testing PageTurnStartedSignal; [1]: testing PageTurnFinishedSignal + // [2]: testing PagePanStartedSignal; [3]: testing PagePanFinishedSignal + bool signalVerified[4]; + PageTurnView currentView; + unsigned int pageIndex; + bool isTurningForwards; + + PageSignalCallback callbackTurnStarted( signalVerified[0], currentView, pageIndex, isTurningForwards ); + portraitView.PageTurnStartedSignal().Connect( &callbackTurnStarted, &PageSignalCallback::PageTurnSignalCallback ); + + PageSignalCallback callbackTurnFinished( signalVerified[1], currentView, pageIndex, isTurningForwards ); + portraitView.PageTurnFinishedSignal().Connect( &callbackTurnFinished, &PageSignalCallback::PageTurnSignalCallback ); + + PageSignalCallback callbackPanStarted( signalVerified[2], currentView, pageIndex, isTurningForwards ); + portraitView.PagePanStartedSignal().Connect( &callbackPanStarted, &PageSignalCallback::PagePanSignalCallback ); + + PageSignalCallback callbackPanFinished( signalVerified[3], currentView, pageIndex, isTurningForwards ); + portraitView.PagePanFinishedSignal().Connect( &callbackPanFinished, &PageSignalCallback::PagePanSignalCallback ); + + DALI_TEST_CHECK( !callbackTurnStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( !callbackPanStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackPanFinished.mSignalVerified ); + + currentView = portraitView; + + //-----Test 1: pan 10 frames from position(size * 0.75f) to position(size * 0.25f), page 0 will be turned forward---- + pageIndex = 0; + isTurningForwards = true; + // Do a pan moving up diagonally. + Vector2 start = size * 0.75f; + Vector2 direction = -size*0.05f; //-size*0.5f/10.f; + + DALI_TEST_CHECK( portraitView.GetCurrentPage() == 0); + PerformGestureDiagonalSwipe( application, start, direction, 5, true, false); + DALI_TEST_CHECK( callbackTurnStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( callbackPanStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackPanFinished.mSignalVerified ); + + PerformGestureDiagonalSwipe( application, start+direction*5.f, direction, 5, false, true); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( callbackPanFinished.mSignalVerified ); + + Wait(application, 1000); + DALI_TEST_CHECK( callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == pageIndex+1); // the page is turn over + + //---Test 2: pan from position( size*0.5f ) to position( size.width, size.height*0.5f ) to position( size * 0.75f ), page 1 will bent then slid back--- + callbackTurnStarted.Reset(); + callbackTurnFinished.Reset(); + callbackPanStarted.Reset(); + callbackPanFinished.Reset(); + portraitView.GoToPage(5); + pageIndex = 5; + isTurningForwards = true; + + //pan 10 frames from position( size.width, size.height*0.5f ) to position( size * 0.75f ) + start = Vector2( size.width, size.height*0.5f ); + direction = Vector2(-size.width*0.025f, size.height*0.025f); + PerformGestureDiagonalSwipe( application, start, direction, 5, true, false); + DALI_TEST_CHECK( callbackPanStarted.mSignalVerified ); + DALI_TEST_CHECK( callbackTurnStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( !callbackPanFinished.mSignalVerified ); + + signalVerified[0] = false; + isTurningForwards = false; + PerformGestureDiagonalSwipe( application, start + direction*2 , direction, 5, false, true); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( callbackPanFinished.mSignalVerified ); + DALI_TEST_CHECK( callbackTurnStarted.mSignalVerified ); // start the sliding back + + Wait(application, 1000); + DALI_TEST_CHECK( callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == pageIndex); // the page is not turned over + + // ----Test 3: pan 10 frames from position( size*0.25f ) to position( size.width*0.75f, size.height*0.25f ), the previous page will be turned backwards--- + callbackTurnStarted.Reset(); + callbackTurnFinished.Reset(); + callbackPanStarted.Reset(); + callbackPanFinished.Reset(); + portraitView.GoToPage(10); + pageIndex = 9; // will turn the previous page back + isTurningForwards = false; + start = size*0.25f; + direction = Vector2(size.x*0.05f, 0.f); + PerformGestureDiagonalSwipe( application, start, direction, 5, true, false); + DALI_TEST_CHECK( callbackPanStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnStarted.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( !callbackPanFinished.mSignalVerified ); + + PerformGestureDiagonalSwipe( application, start+direction*5.f, direction, 5, false, true); + DALI_TEST_CHECK( callbackTurnStarted.mSignalVerified ); + DALI_TEST_CHECK( callbackPanFinished.mSignalVerified ); + DALI_TEST_CHECK( !callbackTurnFinished.mSignalVerified ); + + Wait( application, 1000 ); + + DALI_TEST_CHECK( callbackTurnFinished.mSignalVerified ); + DALI_TEST_CHECK( portraitView.GetCurrentPage() == 9); +} diff --git a/automated-tests/dali-test-suite/popup/.gitignore b/automated-tests/dali-test-suite/popup/.gitignore new file mode 100644 index 0000000..914e48c --- /dev/null +++ b/automated-tests/dali-test-suite/popup/.gitignore @@ -0,0 +1 @@ +utc-Dali-Popup diff --git a/automated-tests/dali-test-suite/popup/Makefile b/automated-tests/dali-test-suite/popup/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/popup/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/popup/file.list b/automated-tests/dali-test-suite/popup/file.list new file mode 100644 index 0000000..3a915c1 --- /dev/null +++ b/automated-tests/dali-test-suite/popup/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-Popup \ diff --git a/automated-tests/dali-test-suite/popup/tslist b/automated-tests/dali-test-suite/popup/tslist new file mode 100644 index 0000000..d1d8cf6 --- /dev/null +++ b/automated-tests/dali-test-suite/popup/tslist @@ -0,0 +1 @@ +/dali-test-suite/popup/utc-Dali-Popup diff --git a/automated-tests/dali-test-suite/popup/utc-Dali-Popup.cpp b/automated-tests/dali-test-suite/popup/utc-Dali-Popup.cpp new file mode 100644 index 0000000..e5fa8af --- /dev/null +++ b/automated-tests/dali-test-suite/popup/utc-Dali-Popup.cpp @@ -0,0 +1,470 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliPopupNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupDestructor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupDownCast, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopoupSetProperty, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupSetBackgroundImage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupSetTitle, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupSetTitleText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupAddButton, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupSetState, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupSetStateSlow, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupShowHide, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupShowHideTail, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPopupOnTouchedOutside, POSITIVE_TC_IDX ); + + +namespace +{ +const int RENDER_FRAME_INTERVAL = 10; ///< Duration of each frame in ms. +const int RENDER_ANIMATION_TEST_DURATION_MS = 1000; ///< 1000ms to test animation +const int RENDER_ANIMATION_TEST_DURATION_FRAMES = RENDER_ANIMATION_TEST_DURATION_MS / RENDER_FRAME_INTERVAL; ///< equivalent frames. +const Vector3 DEFAULT_BUTTON_SIZE(100.0f, 50.0f, 0.0f); +const Dali::TouchPoint pointDownOutside( 0, TouchPoint::Down, 10.0f, 10.0f ); +const Dali::TouchPoint pointUpOutside( 0, TouchPoint::Up, 10.0f, 10.0f ); + +/** + * Counts how many descendents root Actor has, including + * itself. + * + * @param[in] root The root actor to count from. + * @return The number of descendents including root actor itself. + */ +int DescendentCount(const Actor& root) +{ + unsigned int numChildren = root.GetChildCount(); + + int count = 1; + + for(unsigned int i=0; i withoutTailCount ); + + // Hide again + popup.HideTail(); + int withoutTailCount2 = DescendentCount(popup); + + DALI_TEST_CHECK( withTailCount > withoutTailCount2 ); +} + +static bool gTouchedOutside; + +static void OnPopupTouchedOutside() +{ + gTouchedOutside = true; +} + +static void UtcDaliPopupOnTouchedOutside() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + tet_infoline(" UtcDaliPopupOnTouchedOutside"); + + // Create the Popup actor + Popup popup = Popup::New(); + Stage::GetCurrent().Add( popup ); + popup.SetParentOrigin(ParentOrigin::CENTER); + popup.SetAnchorPoint(ParentOrigin::CENTER); + popup.SetState(Popup::POPUP_SHOW, 0.0f); + popup.OutsideTouchedSignal().Connect( &OnPopupTouchedOutside ); + + application.SendNotification(); + application.Render(); + + gTouchedOutside = false; + Dali::Integration::TouchEvent event; + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownOutside ); + application.GetCore().SendEvent( event ); + + application.SendNotification(); + application.Render(); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpOutside ); + application.GetCore().SendEvent( event ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(gTouchedOutside); +} diff --git a/automated-tests/dali-test-suite/scroll-view/.gitignore b/automated-tests/dali-test-suite/scroll-view/.gitignore new file mode 100644 index 0000000..d0282f7 --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/.gitignore @@ -0,0 +1 @@ +utc-Dali-ScrollView diff --git a/automated-tests/dali-test-suite/scroll-view/Makefile b/automated-tests/dali-test-suite/scroll-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/scroll-view/file.list b/automated-tests/dali-test-suite/scroll-view/file.list new file mode 100644 index 0000000..f6ae037 --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/file.list @@ -0,0 +1,3 @@ +TARGETS += \ + utc-Dali-ScrollView \ + utc-Dali-ScrollViewEffect \ diff --git a/automated-tests/dali-test-suite/scroll-view/tslist b/automated-tests/dali-test-suite/scroll-view/tslist new file mode 100644 index 0000000..74541a9 --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/tslist @@ -0,0 +1,2 @@ +/dali-test-suite/scroll-view/utc-Dali-ScrollView +/dali-test-suite/scroll-view/utc-Dali-ScrollViewEffect diff --git a/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollView.cpp b/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollView.cpp new file mode 100644 index 0000000..2ebe46d --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollView.cpp @@ -0,0 +1,1579 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +TEST_FUNCTION( UtcDaliScrollViewNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewDownCast, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewScrollToPosition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewScrollToPage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewScrollToActor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewScrollToSnapPoint, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewRulerScale, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewTransformTo, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewRefreshInterval, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewWrapMode, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewActorAutoSnap, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSignalsStartComplete, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSignalsUpdate, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSignalsClamped, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSignalsSnapStart, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewScrollSensitive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewTouchesRequired, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewAxisAutoLock, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewAxisAutoLockGradient, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewConstraints, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewBind, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewOvershoot, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSnapAlphaFunction, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSnapDuration, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliRulerEnableDisable, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliRulerDomainEnableDisable, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliRulerSnapAndClamp, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliRulerFixedRulerSpacing, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewUIComponent, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewSetMouseWheelScrollDistanceStep, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliScrollViewGetSet, POSITIVE_TC_IDX ); + + +namespace // unnamed namespace +{ + +const int MILLISECONDS_PER_SECOND = 1000; +const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS) +const int RENDER_ANIMATION_TEST_DURATION_MS = 1000; ///< 1000ms to test animation +const int RENDER_DELAY_SCROLL = 1000; ///< duration to wait for any scroll to complete. + +// For Clamp Signal testing... +const float CLAMP_EXCESS_WIDTH = 200.0f; ///< Amount of width that can be panned outside scrollview +const float CLAMP_EXCESS_HEIGHT = 200.0f; ///< Amount of height that can be panned outside scrollview +const int CLAMP_STEP_0_CHECK_NOTCLAMPED = 0; ///< FSM: "First check that scrollview isn't clamped" +const int CLAMP_STEP_1_CHECK_CLAMPED_WEST = 1; ///< FSM: "Next check that scrollview clamps against left side" +const int CLAMP_STEP_2_CHECK_CLAMPED_SOUTH_WEST = 2; ///< FSM: "Then check that scrollview clamps against bottom-left side" +const int CLAMP_STEP_3_SUCCESS = 3; ///< FSM: "Finished (Success)" +const Vector3 CLAMP_START_SCROLL_POSITION(30.0f, 100.0f, 0.0f); ///< Scroll start position for the Clamping tests. +const Vector2 CLAMP_TOUCH_START( 100.0f, 100.0f ); ///< Start point to touch from for the Clamping tests. +const Vector2 CLAMP_TOUCH_MOVEMENT( 5.0f, -5.0f ); ///< Amount to move touch for each frame for the Clamping tests. +const int CLAMP_GESTURE_FRAMES = 100; ///< Number of Frames to synthesize a gesture for the Clamping tests. +const Vector3 TEST_ACTOR_POSITION(100.0f, 100.0f, 0.0f); ///< A Test actor position offset (arbitrary value) +const Vector3 TEST_CONSTRAINT_OFFSET(1.0f, 2.0f, 0.0f); ///< A Test constraint offset (arbitrary value to test effects) +const float TEST_RATIO_TOLERANCE = 0.05; ///< +/-5% tolerance for ratio comparisons. + +const int MAX_FRAMES_TO_TEST_OVERSHOOT = 600; ///< 10 seconds (at 60 frames per second). +const Vector3 OVERSHOOT_START_SCROLL_POSITION(100.0f, 100.0f, 0.0f); ///< Scroll start position for the Overshoot tests. +const float TEST_DEFAULT_SNAP_OVERSHOOT_DURATION = 0.25f; ///< 0.25 seconds should be default snap overshoot duration +const float TEST_CUSTOM1_SNAP_OVERSHOOT_DURATION = 0.05f; ///< a Test duration +const float TEST_CUSTOM2_SNAP_OVERSHOOT_DURATION = 1.5f; ///< another Test duration +const float TEST_CUSTOM3_SNAP_OVERSHOOT_DURATION = TEST_CUSTOM2_SNAP_OVERSHOOT_DURATION * 0.5f; // Same as above, but different alpha function. +const float TIME_TOLERANCE = 0.05f; ///< Allow testing tolerance between a 10th of second (+/- 3 frames) + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +// Generate a PanGestureEvent to send to Core +Integration::PanGestureEvent GeneratePan( + Gesture::State state, + const Vector2& previousPosition, + const Vector2& currentPosition, + unsigned long timeDelta, + unsigned int numberOfTouches = 1) +{ + Integration::PanGestureEvent pan(state); + + pan.previousPosition = previousPosition; + pan.currentPosition = currentPosition; + pan.timeDelta = timeDelta; + pan.numberOfTouches = numberOfTouches; + + return pan; +} + +/** + * Helper to generate PanGestureEvent + * + * @param[in] application Application instance + * @param[in] state The Gesture State + * @param[in] pos The current position of touch. + */ +static void SendPan(ToolkitTestApplication& application, Gesture::State state, const Vector2& pos) +{ + static Vector2 last; + + if( (state == Gesture::Started) || + (state == Gesture::Possible) ) + { + last.x = pos.x; + last.y = pos.y; + } + + Dali::Integration::Core& core = application.GetCore(); + core.SendEvent(GeneratePan(state, last, pos, RENDER_FRAME_INTERVAL)); + + last.x = pos.x; + last.y = pos.y; +} + +/* + * Simulate time passed by. + * + * @note this will always process at least 1 frame (1/60 sec) + * + * @param application Test application instance + * @param duration Time to pass in milliseconds. + * @return The actual time passed in milliseconds + */ +int Wait(ToolkitTestApplication& application, int duration = 0) +{ + int time = 0; + + for(int i = 0; i <= ( duration / RENDER_FRAME_INTERVAL); i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + time += RENDER_FRAME_INTERVAL; + } + + return time; +} + +// Callback probes. + +static bool gOnScrollStartCalled; ///< Whether the OnScrollStart signal was invoked. +static bool gOnScrollUpdateCalled; ///< Whether the OnScrollUpdate signal was invoked. +static bool gOnScrollCompleteCalled; ///< Whether the OnScrollComplete signal was invoked. +static bool gOnScrollClampedCalled; ///< Whether the OnScrollClamped signal was invoked. +static bool gOnSnapStartCalled; ///< Whether the OnSnapStart signal was invoked. +static ClampState3 gLastClampPosition; ///< Clamping information from OnScrollClampedEvent. +static SnapType gLastSnapType; ///< Snaping information from SnapEvent. +static Vector3 gConstraintResult; ///< Result from constraint. + +/** + * Invoked when scrolling starts. + * + * @param[in] position The current scroll position. + */ +static void OnScrollStart( const Vector3& position ) +{ + gOnScrollStartCalled = true; +} + +/** + * Invoked when scrolling updates (via dragging) + * + * @param[in] position The current scroll position. + */ +static void OnScrollUpdate( const Vector3& position ) +{ + gOnScrollUpdateCalled = true; +} + +/** + * Invoked when scrolling finishes + * + * @param[in] position The current scroll position. + */ +static void OnScrollComplete( const Vector3& position ) +{ + gOnScrollCompleteCalled = true; +} + +/** + * Invoked when scrolling clamped. + * + * @param[in] event The position/scale/rotation axes that were clamped. + */ +static void OnScrollClamped( const ScrollView::ClampEvent& event ) +{ + gOnScrollClampedCalled = true; + gLastClampPosition = event.position; +} + +/** + * Invoked when a snap or flick started. + * + * @param[in] event The type of snap and the target position/scale/rotation. + */ +static void OnSnapStart( const ScrollView::SnapEvent& event ) +{ + gOnSnapStartCalled = true; + gLastSnapType = event.type; +} + +/** + * TestSumConstraint + * + * Summation of current value, property, and offset. + * + * current' = current + mOffset + property; + */ +struct TestSumConstraint +{ + /** + * @param[in] offset The offset to be added to current. + */ + TestSumConstraint(const Vector3& offset) + :mOffset(offset) + { + } + + /** + * @param[in] current The current base value + * @param[in] property The property to be added to current. + * @return The new current Vector. + */ + Vector3 operator()(const Vector3& current) + { + gConstraintResult = current + mOffset; + return gConstraintResult; + } + + /** + * @param[in] current The current base value + * @param[in] property The property to be added to current. + * @return The new current Vector. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& property) + { + gConstraintResult = current + property.GetVector3() + mOffset; + return gConstraintResult; + } + + Vector3 mOffset; + +}; + +/** + * @param[in] application The application instance + * @param[in] scrollView The scrollView instance + * @return The time taken for the overshoot to reach origin (zero) + */ +static float TestOvershootSnapDuration(ToolkitTestApplication &application, ScrollView scrollView) +{ + Property::Index overshootPropertyX = scrollView.GetPropertyIndex(ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME); + Property::Index overshootPropertyY = scrollView.GetPropertyIndex(ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME); + + int timeToReachOrigin = -1; + for(int i = 0;i(overshootPropertyX); + float overshootYValue = scrollView.GetProperty(overshootPropertyY); + if(overshootXValue == 0.0f && overshootYValue == 0.0f) + { + break; + } + + timeToReachOrigin += Wait(application); + } + + return static_cast(timeToReachOrigin) * 0.001f; // return in seconds not ms. +} + +/** + * y = 2x alpha function, which is clamped between 0.0f - 1.0f + * + * Animations should appear to finish (reach 100% point) + * at just half the time of a regular Linear AlphaFunction. + * + * @param[in] progress value (ranges from 0.0f - 1.0f) + * @return interpolation value (ranges from 0.0f - 1.0f) + */ +float TestAlphaFunction(float progress) +{ + return std::min( progress * 2.0f, 1.0f ); +} + +} // unnamed namespace + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliScrollViewNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewNew"); + + ScrollView scrollView; + + DALI_TEST_CHECK( !scrollView ); + + scrollView = ScrollView::New(); + + DALI_TEST_CHECK( scrollView ); + + ScrollView scrollView2(scrollView); + + DALI_TEST_CHECK( scrollView2 == scrollView ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + ScrollView scrollView = ScrollView::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliScrollViewDownCast() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewDownCast"); + + ScrollView scrollView = ScrollView::New(); + BaseHandle handle(scrollView); + + ScrollView newScrollView = ScrollView::DownCast( handle ); + DALI_TEST_CHECK( scrollView ); + DALI_TEST_CHECK( newScrollView == scrollView ); +} + +static void UtcDaliScrollViewScrollToPosition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewScrollToPosition"); + + // Create the ScrollView actor + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + + const Vector3 target = Vector3(100.0f, 200.0f, 0.0f); + const Vector3 target2 = Vector3(300.0f, 100.0f, 0.0f); + + scrollView.ScrollTo( target, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), target, TEST_LOCATION ); + scrollView.ScrollTo( target2 ); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), target2, TEST_LOCATION ); + + Wait(application); +} + +static void UtcDaliScrollViewScrollToPage() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewScrollToPage"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + RulerPtr rulerX = new FixedRuler( 100.0f ); + rulerX->SetDomain( RulerDomain(0.0f, 800.0f, true) ); + RulerPtr rulerY = new FixedRuler( 100.0f ); + rulerY->SetDomain( RulerDomain(0.0f, 400.0f, true) ); + + scrollView.SetRulerX( rulerX ); + scrollView.SetRulerY( rulerY ); + + scrollView.ScrollTo( 1, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(100.0f, 0.0f, 0.0f), TEST_LOCATION ); + + scrollView.ScrollTo( 5, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(500.0f, 0.0f, 0.0f), TEST_LOCATION ); + + scrollView.ScrollTo( 10, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(200.0f, 100.0f, 0.0f), TEST_LOCATION ); + + scrollView.ScrollTo( 15, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(700.0f, 100.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 15, TEST_LOCATION ); + + scrollView.ScrollTo( 3 ); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(300.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 3, TEST_LOCATION ); + + scrollView.ScrollTo( 9 ); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(100.0f, 100.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 9, TEST_LOCATION ); + + // Apply DefaultRulers instead and see what happens. + rulerX = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, 800.0f, true) ); + rulerY = new DefaultRuler(); + rulerY->SetDomain( RulerDomain(0.0f, 400.0f, true) ); + + scrollView.SetRulerX( rulerX ); + scrollView.SetRulerY( rulerY ); + + // This time should always scroll to origin (0.0f, 0.0f) + scrollView.ScrollTo( 1, 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 0, TEST_LOCATION ); + + Wait(application); +} + +static void UtcDaliScrollViewScrollToActor() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewScrollToActor"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + + Actor actorA = Actor::New(); + const Vector3 positionA = Vector3(100.0f, 400.0f, 0.0f); + actorA.SetPosition(positionA); + scrollView.Add(actorA); + + Actor actorB = Actor::New(); + const Vector3 positionB = Vector3(500.0f, 200.0f, 0.0f); + actorB.SetPosition(positionB); + scrollView.Add(actorB); + + Wait(application); + + scrollView.ScrollTo(actorA, 0.0f); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), positionA, TEST_LOCATION ); + + Wait(application); + scrollView.ScrollTo(actorB, 0.0f); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), positionB, TEST_LOCATION ); + + scrollView.ScrollTo(actorA); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), positionA, TEST_LOCATION ); + + scrollView.ScrollTo(actorB); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), positionB, TEST_LOCATION ); +} + +static void UtcDaliScrollViewScrollToSnapPoint() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewScrollToSnapPoint"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + RulerPtr rulerX = new FixedRuler( 100.0f ); + rulerX->SetDomain( RulerDomain(0.0f, 800.0f, true) ); + RulerPtr rulerY = new FixedRuler( 100.0f ); + rulerY->SetDomain( RulerDomain(0.0f, 400.0f, true) ); + + scrollView.SetRulerX( rulerX ); + scrollView.SetRulerY( rulerY ); + + scrollView.ScrollTo( Vector3(120.0f, 190.0f, 0.0f), 0.0f ); + Wait(application); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(120.0f, 190.0f, 0.0f), TEST_LOCATION ); + + scrollView.ScrollToSnapPoint(); + + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), Vector3(100.0f, 200.0f, 0.0f), TEST_LOCATION ); +} + +static void UtcDaliScrollViewRulerScale() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewRulerScale"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + + RulerPtr rulerScaleX = new FixedRuler(0.25f); + RulerPtr rulerScaleY = new DefaultRuler(); + rulerScaleX->SetDomain( RulerDomain(0.1f, 0.9f, true) ); + rulerScaleY->SetDomain( RulerDomain(0.1f, 2.0f, true) ); + scrollView.SetRulerScaleX(rulerScaleX); + scrollView.SetRulerScaleY(rulerScaleY); + + scrollView.Add(Actor::New()); + + // Scroll to a position, and then snap. + scrollView.ScaleTo(Vector3(1.95f, 1.4f, 1.0f), 0.0f); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollScale(), Vector3(0.9f, 1.4f, 1.0f), TEST_LOCATION ); + + // Scroll SLOWLY to another position, and then snap. + scrollView.ScaleTo(Vector3(0.45f, -1.0f, 1.0f)); + Wait(application, RENDER_DELAY_SCROLL); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollScale(), Vector3(0.5f, 0.1f, 1.0f), TEST_LOCATION ); + + // Scroll to another position, and then snap. + scrollView.ScaleTo(Vector3(0.71f, 0.71f, 1.0f), 0.0f); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollScale(), Vector3(0.75f, 0.71f, 1.0f), TEST_LOCATION ); +} + +static void UtcDaliScrollViewTransformTo() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewTransformTo"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + Stage::GetCurrent().Add( scrollView ); + + // Position rulers. + RulerPtr rulerX = new FixedRuler(50.0f); + RulerPtr rulerY = new FixedRuler(50.0f); + rulerX->SetDomain( RulerDomain(0.0f, 200.0f, true) ); + rulerY->SetDomain( RulerDomain(0.0f, 200.0f, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + + // Scale rulers. + RulerPtr rulerScaleX = new FixedRuler(0.1f); + RulerPtr rulerScaleY = new FixedRuler(0.1f); + rulerScaleX->SetDomain( RulerDomain(0.0f, 1.0f, true) ); + rulerScaleY->SetDomain( RulerDomain(0.0f, 1.0f, true) ); + scrollView.SetRulerScaleX(rulerScaleX); + scrollView.SetRulerScaleY(rulerScaleY); + + // transform to a random position/scale + Vector3 targetPosition = Vector3(100.0f, 200.0f, 0.0f); + Vector3 targetScale = Vector3(0.44f, 0.58f, 1.0f); + float targetRotation = 0.0f; + scrollView.TransformTo(targetPosition, targetScale, targetRotation, 0.0f); + Wait(application); + + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), targetPosition, TEST_LOCATION ); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollScale(), targetScale, TEST_LOCATION ); + + // transform to another random position/scale (SLOWLY) + targetPosition = Vector3(60.0f, 40.0f, 0.0f); + targetScale = Vector3(0.4f, 0.6f, 1.0f); + targetRotation = 0.0f; + scrollView.TransformTo(targetPosition, targetScale, targetRotation); + Wait(application, RENDER_DELAY_SCROLL); + + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), targetPosition, TEST_LOCATION ); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollScale(), targetScale, TEST_LOCATION ); +} + +static void UtcDaliScrollViewRefreshInterval() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewRefreshInterval"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + DALI_TEST_EQUALS( scrollView.GetRefreshInterval(), 0, TEST_LOCATION); + scrollView.SetRefreshInterval(10); + DALI_TEST_EQUALS( scrollView.GetRefreshInterval(), 10, TEST_LOCATION); + scrollView.SetRefreshInterval(1000); + DALI_TEST_EQUALS( scrollView.GetRefreshInterval(), 1000, TEST_LOCATION); +} + +static void UtcDaliScrollViewWrapMode() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewWrapMode"); + + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + + // Position rulers. 4x4 grid. + RulerPtr rulerX = new FixedRuler(50.0f); + RulerPtr rulerY = new FixedRuler(50.0f); + rulerX->SetDomain( RulerDomain(0.0f, 200.0f, false) ); + rulerY->SetDomain( RulerDomain(0.0f, 200.0f, false) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + + scrollView.SetWrapMode(false); + scrollView.ScrollTo(Vector3(225.0f, 125.0f, 0.0f), 0.0f); // 5th (1st) page across, and 3rd (3rd) page down. (wrapped) + Wait(application); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 17, TEST_LOCATION ); + scrollView.SetWrapMode(true); + DALI_TEST_EQUALS( static_cast(scrollView.GetCurrentPage()), 13, TEST_LOCATION ); +} + +static void UtcDaliScrollViewActorAutoSnap() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewActorAutoSnap"); + + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + rulerY->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + + const Vector3 aPosition = Vector3(200.0f, 50.0f, 0.0f); + Actor a = Actor::New(); + scrollView.Add(a); + a.SetPosition(aPosition); + + const Vector3 bPosition = Vector3(600.0f, 600.0f, 0.0f); + Actor b = Actor::New(); + scrollView.Add(b); + b.SetPosition(bPosition); + + // Goto a random position, and execute snap (should not move) + Vector3 targetScroll = Vector3(500.0f, 500.0f, 0.0f); + scrollView.ScrollTo(targetScroll, 0.0f); + Wait(application); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), targetScroll, TEST_LOCATION ); + + // Enable ActorAutoSnap, and now try snapping. + scrollView.SetActorAutoSnap(true); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), bPosition, TEST_LOCATION ); + + scrollView.ScrollTo(Vector3(0.0f, 0.0f, 0.0f), 0.0f); + Wait(application); + scrollView.ScrollToSnapPoint(); + Wait(application, RENDER_DELAY_SCROLL); + DALI_TEST_EQUALS( scrollView.GetCurrentScrollPosition(), aPosition, TEST_LOCATION ); +} + +static void UtcDaliScrollViewSignalsStartComplete() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSignalsStartComplete"); + + gOnScrollStartCalled = false; + gOnScrollCompleteCalled = false; + + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + rulerY->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + scrollView.ScrollTo( 100.0f, 100.0f ); + Wait(application, RENDER_DELAY_SCROLL); + + DALI_TEST_CHECK(gOnScrollStartCalled); + DALI_TEST_CHECK(gOnScrollCompleteCalled); +} + +static void UtcDaliScrollViewSignalsUpdate() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSignalsUpdate"); + + gOnScrollStartCalled = false; + gOnScrollUpdateCalled = false; + gOnScrollCompleteCalled = false; + + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + rulerY->SetDomain( RulerDomain(0.0f, 1000.0f, false) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + + ImageActor image = CreateSolidColorActor( Color::RED ); + image.SetSize(stageSize); + image.SetParentOrigin(ParentOrigin::TOP_LEFT); + image.SetAnchorPoint(AnchorPoint::TOP_LEFT); + scrollView.Add(image); + + Wait(application); + + // Do a pan starting from 100,100 and moving down diagonally. + Vector2 pos(100.0f, 100.0f); + SendPan(application, Gesture::Possible, pos); + SendPan(application, Gesture::Started, pos); + pos.x += 5.0f; + pos.y += 5.0f; + Wait(application, 100); + + for(int i = 0;i<20;i++) + { + SendPan(application, Gesture::Continuing, pos); + pos.x += 5.0f; + pos.y += 5.0f; + Wait(application); + } + + SendPan(application, Gesture::Finished, pos); + Wait(application, RENDER_DELAY_SCROLL); + + DALI_TEST_CHECK(gOnScrollStartCalled); + DALI_TEST_CHECK(gOnScrollUpdateCalled); + DALI_TEST_CHECK(gOnScrollCompleteCalled); +} + +// Creates a scroll domain slightly bigger than the stage size. +// ScrollView is scrolled to center, slightly to the left. +// Then a pan gesture is carried out causing the scrollview +// to pan South-West direction. Resulting in ClampEvents +// to fire (first Western boundary, then both Western and +// Southern boundary). +static void UtcDaliScrollViewSignalsClamped() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSignalsClamped"); + + gOnScrollUpdateCalled = false; + gOnScrollCompleteCalled = false; + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollClampedSignal().Connect( &OnScrollClamped ); + + scrollView.ScrollTo(CLAMP_START_SCROLL_POSITION, 0.0f); // move in a little. + Wait(application); + + // Now do a pan starting from 100,100 and heading South-West + Vector2 pos(CLAMP_TOUCH_START); + SendPan(application, Gesture::Possible, pos); + SendPan(application, Gesture::Started, pos); + pos += CLAMP_TOUCH_MOVEMENT; // Move South-West a little + Wait(application); + + int step = CLAMP_STEP_0_CHECK_NOTCLAMPED; + // Move 500,500 pixels South-West + // should be initially not clamped (0) + // then it should clamp against West boundary (X Min) (1) + // then it should clamp against South-West boundary (X Min, Y Max) (2) + gLastClampPosition.x = Toolkit::NotClamped; + gLastClampPosition.y = Toolkit::NotClamped; + + for(int i = 0;iSetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + scrollView.SnapStartedSignal().Connect( &OnSnapStart ); + + scrollView.ScrollTo(CLAMP_START_SCROLL_POSITION, 0.0f); // move in a little. + Wait(application); + + // First try insensitive swipe. + scrollView.SetScrollSensitive(false); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, CLAMP_TOUCH_MOVEMENT, CLAMP_GESTURE_FRAMES, true); + + DALI_TEST_CHECK( !gOnScrollStartCalled ); + DALI_TEST_CHECK( !gOnScrollUpdateCalled ); + DALI_TEST_CHECK( !gOnScrollCompleteCalled ); + DALI_TEST_CHECK( !gOnSnapStartCalled ); + + // Second try sensitive swipe. + scrollView.SetScrollSensitive(true); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, CLAMP_TOUCH_MOVEMENT, CLAMP_GESTURE_FRAMES, true); + + DALI_TEST_CHECK( gOnScrollStartCalled ); + DALI_TEST_CHECK( gOnScrollUpdateCalled ); + DALI_TEST_CHECK( gOnScrollCompleteCalled ); + DALI_TEST_CHECK( gOnSnapStartCalled ); +} + +static void UtcDaliScrollViewTouchesRequired() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewTouchesRequired"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + scrollView.SnapStartedSignal().Connect( &OnSnapStart ); + + scrollView.ScrollTo(CLAMP_START_SCROLL_POSITION, 0.0f); // move in a little. + Wait(application); + + // First try touches required being a minimum and maximum of 2. + scrollView.SetTouchesRequiredForPanning(2, 2, true); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, CLAMP_TOUCH_MOVEMENT, CLAMP_GESTURE_FRAMES, true); + + DALI_TEST_CHECK( !gOnScrollStartCalled ); + DALI_TEST_CHECK( !gOnScrollUpdateCalled ); + DALI_TEST_CHECK( !gOnScrollCompleteCalled ); + DALI_TEST_CHECK( !gOnSnapStartCalled ); + + // Second try touches required being a minimum and maximum of 1. + scrollView.SetTouchesRequiredForPanning(1, 1, true); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, CLAMP_TOUCH_MOVEMENT, CLAMP_GESTURE_FRAMES, true); + + DALI_TEST_CHECK( gOnScrollStartCalled ); + DALI_TEST_CHECK( gOnScrollUpdateCalled ); + DALI_TEST_CHECK( gOnScrollCompleteCalled ); + DALI_TEST_CHECK( gOnSnapStartCalled ); +} + +static void UtcDaliScrollViewAxisAutoLock() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewAxisAutoLock"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + + // Normal + scrollView.ScrollTo(Vector3(100.0f, 100.0f, 0.0f), 0.0f); // move in a little. + Wait(application); + Vector3 startPosition = scrollView.GetCurrentScrollPosition(); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, Vector2(5.0f, 1.0f), 50, true); // mostly horizontal + const Vector3 positionAfterNormal = scrollView.GetCurrentScrollPosition(); + + // Autolock + scrollView.SetAxisAutoLock(true); + DALI_TEST_CHECK(scrollView.GetAxisAutoLock()); + + scrollView.ScrollTo(Vector3(100.0f, 100.0f, 0.0f), 0.0f); // move in a little. + Wait(application); + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, Vector2(5.0f, 1.0f), 50, true); // mostly horizontal + const Vector3 positionAfterAutoLock = scrollView.GetCurrentScrollPosition(); + + // compare how much the Y position has deviated for normal and autolock. + const float devianceNormal = fabsf(startPosition.y - positionAfterNormal.y); + const float devianceAutoLock = fabsf(startPosition.y - positionAfterAutoLock.y); + + // in auto-lock it should be a mostly horizontal pan (thus deviance should be much lower) + DALI_TEST_CHECK(devianceAutoLock < devianceNormal); + + scrollView.SetAxisAutoLock(false); + DALI_TEST_CHECK(!scrollView.GetAxisAutoLock()); +} + +static void UtcDaliScrollViewAxisAutoLockGradient() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewAxisAutoLockGradient"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + scrollView.SetAxisAutoLockGradient(0.5f); + DALI_TEST_EQUALS(scrollView.GetAxisAutoLockGradient(), 0.5f, TEST_LOCATION); + scrollView.SetAxisAutoLockGradient(1.0f); + DALI_TEST_EQUALS(scrollView.GetAxisAutoLockGradient(), 1.0f, TEST_LOCATION); +} + +static void UtcDaliScrollViewConstraints() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewConstraints"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + + // Add an Actor to ScrollView, + // Apply TestSumConstraint to ScrollView's children (includes this Actor) + gConstraintResult = Vector3::ZERO; + Actor a = Actor::New(); + scrollView.Add(a); + a.SetPosition( TEST_ACTOR_POSITION ); + Wait(application); + + Property::Index scrollPositionProperty = scrollView.GetPropertyIndex(ScrollView::SCROLL_POSITION_PROPERTY_NAME); + Constraint constraint = Constraint::New( Actor::POSITION, + Source(scrollView, scrollPositionProperty), + TestSumConstraint( TEST_CONSTRAINT_OFFSET ) ); + constraint.SetRemoveAction(Constraint::Discard); + scrollView.ApplyConstraintToChildren(constraint); + Wait(application); + + DALI_TEST_EQUALS( gConstraintResult, TEST_ACTOR_POSITION + TEST_CONSTRAINT_OFFSET, TEST_LOCATION ); + + gConstraintResult = Vector3::ZERO; + scrollView.RemoveConstraintsFromChildren(); + Wait(application); + + DALI_TEST_EQUALS( gConstraintResult, Vector3::ZERO, TEST_LOCATION ); +} + +static void UtcDaliScrollViewBind() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewBind"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + + // Add an Actor to ScrollView, + // Apply TestSumConstraint to ScrollView's children (includes this Actor) + + gConstraintResult = Vector3::ZERO; + Actor a = Actor::New(); + scrollView.Add(a); + a.SetPosition( TEST_ACTOR_POSITION ); + Wait(application); + + Property::Index scrollPositionProperty = scrollView.GetPropertyIndex(ScrollView::SCROLL_POSITION_PROPERTY_NAME); + // apply this constraint to scrollview + Constraint constraint = Constraint::New( Actor::POSITION, + Source(scrollView, scrollPositionProperty), + TestSumConstraint( TEST_CONSTRAINT_OFFSET ) ); + + constraint.SetRemoveAction(Constraint::Discard); + scrollView.ApplyConstraintToChildren(constraint); + + Wait(application); + // Defaulty Bound. + DALI_TEST_EQUALS( gConstraintResult, TEST_ACTOR_POSITION + TEST_CONSTRAINT_OFFSET, TEST_LOCATION ); + + // UnBind + gConstraintResult = Vector3::ZERO; + scrollView.UnbindActor( a ); + Wait(application); + DALI_TEST_EQUALS( gConstraintResult, Vector3::ZERO, TEST_LOCATION ); + + // Bind + gConstraintResult = Vector3::ZERO; + scrollView.BindActor( a ); + Wait(application); + DALI_TEST_EQUALS( gConstraintResult, TEST_ACTOR_POSITION + TEST_CONSTRAINT_OFFSET, TEST_LOCATION ); +} + +static void UtcDaliRulerEnableDisable() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliRulerEnableDisable"); + + RulerPtr ruler = new DefaultRuler(); + + DALI_TEST_CHECK( ruler->IsEnabled() ); + ruler->Disable(); + DALI_TEST_CHECK( !ruler->IsEnabled() ); + ruler->Enable(); + DALI_TEST_CHECK( ruler->IsEnabled() ); +} + +static void UtcDaliRulerDomainEnableDisable() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliRulerDomainEnableDisable"); + + RulerPtr ruler = new DefaultRuler(); + DALI_TEST_EQUALS( ruler->GetDomain().GetSize(), 1.0f, TEST_LOCATION ); + + + ruler->SetDomain( RulerDomain(0.0f, 100.0f, true) ); + DALI_TEST_EQUALS( ruler->GetDomain().GetSize(), 100.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->Clamp(-200.0f), 0.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->Clamp(200.0f), 100.0f, TEST_LOCATION ); + + ruler->DisableDomain(); + DALI_TEST_EQUALS( ruler->GetDomain().GetSize(), 1.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->Clamp(-200.0f), -200.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->Clamp(200.0f), 200.0f, TEST_LOCATION ); +} + +static void UtcDaliRulerSnapAndClamp() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliRulerSnapAndClamp"); + + RulerPtr ruler = new FixedRuler( 50.0f ); + ruler->SetDomain( RulerDomain(0.0f, 400.0f, true) ); + + // default testing. (snap and clamp) + DALI_TEST_EQUALS( ruler->SnapAndClamp(50.0f), 50.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(30.0f), 50.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(10.0f), 0.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(-40.0f), 0.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(390.0f), 400.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(430.0f), 400.0f, TEST_LOCATION); + + // bias testing. + DALI_TEST_EQUALS( ruler->SnapAndClamp(40.0f, 0.0f), 0.0f, TEST_LOCATION); // Flick Left + DALI_TEST_EQUALS( ruler->SnapAndClamp(40.0f, 0.5f), 50.0f, TEST_LOCATION); // No Flick + DALI_TEST_EQUALS( ruler->SnapAndClamp(40.0f, 1.0f), 50.0f, TEST_LOCATION); // Flick Right + + DALI_TEST_EQUALS( ruler->SnapAndClamp(20.0f, 0.0f), 0.0f, TEST_LOCATION); // Flick Left + DALI_TEST_EQUALS( ruler->SnapAndClamp(20.0f, 0.5f), 0.0f, TEST_LOCATION); // No Flick + DALI_TEST_EQUALS( ruler->SnapAndClamp(20.0f, 1.0f), 50.0f, TEST_LOCATION); // Flick Right + + // length testing. + DALI_TEST_EQUALS( ruler->SnapAndClamp(-10.0f, 0.5f, 10.0f), 0.0f, TEST_LOCATION); // 10 units long (over left boundary) + DALI_TEST_EQUALS( ruler->SnapAndClamp(-5.0f, 0.5f, 10.0f), 0.0f, TEST_LOCATION); // 10 units long (slightly ovr left boundary) + DALI_TEST_EQUALS( ruler->SnapAndClamp(300.0f, 0.5f, 10.0f), 300.0f, TEST_LOCATION); // 10 units long (not over a boundary) + DALI_TEST_EQUALS( ruler->SnapAndClamp(395.0f, 0.5f, 10.0f), 390.0f, TEST_LOCATION); // 10 units long (slightly over right boundary) + DALI_TEST_EQUALS( ruler->SnapAndClamp(500.0f, 0.5f, 10.0f), 390.0f, TEST_LOCATION); // 10 units long (over right boundary) + + // scale testing. + DALI_TEST_EQUALS( ruler->SnapAndClamp(-100.0f, 0.5f, 0.0f, 2.0f), 0.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(50.0f, 0.5f, 0.0f, 2.0f), 50.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(700.0f, 0.5f, 0.0f, 2.0f), 700.0f, TEST_LOCATION); + DALI_TEST_EQUALS( ruler->SnapAndClamp(850.0f, 0.5f, 0.0f, 2.0f), 800.0f, TEST_LOCATION); + + // clamp state testing. + ClampState clamped; + DALI_TEST_EQUALS( ruler->SnapAndClamp(50.0f, 0.5f, 0.0f, 1.0f, clamped), 50.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, NotClamped, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->SnapAndClamp(30.0f, 0.5f, 0.0f, 1.0f, clamped), 50.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, NotClamped, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->SnapAndClamp(10.0f, 0.5f, 0.0f, 1.0f, clamped), 0.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, NotClamped, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->SnapAndClamp(-40.0f, 0.5f, 0.0f, 1.0f, clamped), 0.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, ClampedToMin, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->SnapAndClamp(390.0f, 0.5f, 0.0f, 1.0f, clamped), 400.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, NotClamped, TEST_LOCATION ); + DALI_TEST_EQUALS( ruler->SnapAndClamp(430.0f, 0.5f, 0.0f, 1.0f, clamped), 400.0f, TEST_LOCATION); + DALI_TEST_EQUALS( clamped, ClampedToMax, TEST_LOCATION ); +} + +static void UtcDaliRulerFixedRulerSpacing() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliRulerFixedRulerSpacing"); + + RulerPtr rulerZero = new FixedRuler( 0.0f ); + rulerZero->SetDomain( RulerDomain(10.0f, 90.0f, true) ); + + RulerPtr rulerNormal = new FixedRuler( 25.0f ); + rulerNormal->SetDomain( RulerDomain(10.0f, 90.0f, true) ); + + unsigned int volume; + float position; + + position = rulerZero->GetPositionFromPage(1, volume, true); + DALI_TEST_EQUALS( position, 10.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( volume, 1u, TEST_LOCATION ); + + position = rulerNormal->GetPositionFromPage(1, volume, true); + DALI_TEST_EQUALS( position, 35.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( volume, 0u, TEST_LOCATION ); + + position = rulerZero->GetPositionFromPage(2, volume, true); + DALI_TEST_EQUALS( position, 10.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( volume, 2u, TEST_LOCATION ); + + position = rulerNormal->GetPositionFromPage(2, volume, true); + DALI_TEST_EQUALS( position, 60.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( volume, 0u, TEST_LOCATION ); +} + +static void UtcDaliScrollViewOvershoot() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewOvershoot"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + + scrollView.ScrollTo(OVERSHOOT_START_SCROLL_POSITION, 0.0f); // move in a little. + Wait(application); + + // 1. Scroll page in NW (-500,-500 pixels), then inspect overshoot. (don't release touch) + Vector2 currentPos = Vector2(100.0f, 100.0f); + currentPos = PerformGestureDiagonalSwipe(application, currentPos, Vector2(5.0f, 5.0f), 100, false); + Property::Index overshootXProperty = scrollView.GetPropertyIndex(ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME); + Property::Index overshootYProperty = scrollView.GetPropertyIndex(ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME); + Property::Index scrollPositionProperty = scrollView.GetPropertyIndex(ScrollView::SCROLL_POSITION_PROPERTY_NAME); + float overshootXValue = scrollView.GetProperty(overshootXProperty); + float overshootYValue = scrollView.GetProperty(overshootYProperty); + Vector3 positionValue = scrollView.GetProperty(scrollPositionProperty); + DALI_TEST_EQUALS(overshootXValue, -1.0f, TEST_LOCATION); + DALI_TEST_EQUALS(overshootYValue, -1.0f, TEST_LOCATION); + DALI_TEST_EQUALS(positionValue, Vector3::ZERO, TEST_LOCATION); + + float timeToReachOrigin; + + // Now release touch. Overshoot should snap back to zero. + SendPan(application, Gesture::Finished, currentPos); + timeToReachOrigin = TestOvershootSnapDuration(application, scrollView); + + DALI_TEST_CHECK( (timeToReachOrigin > TEST_DEFAULT_SNAP_OVERSHOOT_DURATION - TIME_TOLERANCE) && + (timeToReachOrigin < TEST_DEFAULT_SNAP_OVERSHOOT_DURATION + TIME_TOLERANCE) ); + + // 2. Repeat Scroll, but this time change overshoot snap duration to shorter time + scrollView.SetSnapOvershootDuration(TEST_CUSTOM1_SNAP_OVERSHOOT_DURATION); + + currentPos = PerformGestureDiagonalSwipe(application, Vector2(100.0f, 100.0f), Vector2(5.0f, 5.0f), 100, false); + // Now release touch. Overshoot should snap back to zero. + SendPan(application, Gesture::Finished, currentPos); + timeToReachOrigin = TestOvershootSnapDuration(application, scrollView); + + DALI_TEST_CHECK( (timeToReachOrigin > TEST_CUSTOM1_SNAP_OVERSHOOT_DURATION - TIME_TOLERANCE) && + (timeToReachOrigin < TEST_CUSTOM1_SNAP_OVERSHOOT_DURATION + TIME_TOLERANCE) ); + + // 3. Repeat Scroll, but this time change overshoot snap duration to longer time. + scrollView.SetSnapOvershootDuration(TEST_CUSTOM2_SNAP_OVERSHOOT_DURATION); + + currentPos = PerformGestureDiagonalSwipe(application, Vector2(100.0f, 100.0f), Vector2(5.0f, 5.0f), 100, false); + // Now release touch. Overshoot should snap back to zero. + SendPan(application, Gesture::Finished, currentPos); + timeToReachOrigin = TestOvershootSnapDuration(application, scrollView); + + DALI_TEST_CHECK( (timeToReachOrigin > TEST_CUSTOM2_SNAP_OVERSHOOT_DURATION - TIME_TOLERANCE) && + (timeToReachOrigin < TEST_CUSTOM2_SNAP_OVERSHOOT_DURATION + TIME_TOLERANCE) ); + + // 4. Repeat Scroll, but this time change overshoot function. + scrollView.SetSnapOvershootAlphaFunction(TestAlphaFunction); + + currentPos = PerformGestureDiagonalSwipe(application, Vector2(100.0f, 100.0f), Vector2(5.0f, 5.0f), 100, false); + // Now release touch. Overshoot should snap back to zero. + SendPan(application, Gesture::Finished, currentPos); + timeToReachOrigin = TestOvershootSnapDuration(application, scrollView); + + DALI_TEST_CHECK( (timeToReachOrigin > TEST_CUSTOM3_SNAP_OVERSHOOT_DURATION - TIME_TOLERANCE) && + (timeToReachOrigin < TEST_CUSTOM3_SNAP_OVERSHOOT_DURATION + TIME_TOLERANCE) ); +} + +static void UtcDaliScrollViewSnapAlphaFunction() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSnapAlphaFunction"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + scrollView.SetScrollSnapAlphaFunction( AlphaFunctions::EaseIn ); + DALI_TEST_CHECK( scrollView.GetScrollSnapAlphaFunction() == AlphaFunctions::EaseIn ); + scrollView.SetScrollSnapAlphaFunction( AlphaFunctions::EaseOut ); + DALI_TEST_CHECK( scrollView.GetScrollSnapAlphaFunction() == AlphaFunctions::EaseOut ); + + scrollView.SetScrollFlickAlphaFunction( AlphaFunctions::Bounce ); + DALI_TEST_CHECK( scrollView.GetScrollFlickAlphaFunction() == AlphaFunctions::Bounce ); + scrollView.SetScrollFlickAlphaFunction( AlphaFunctions::BounceBack ); + DALI_TEST_CHECK( scrollView.GetScrollFlickAlphaFunction() == AlphaFunctions::BounceBack ); +} + +static void UtcDaliScrollViewSnapDuration() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSnapDuration"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + scrollView.SetScrollSnapDuration( 1.0f ); + DALI_TEST_EQUALS( scrollView.GetScrollSnapDuration(), 1.0f, TEST_LOCATION ); + scrollView.SetScrollSnapDuration( 0.5f ); + DALI_TEST_EQUALS( scrollView.GetScrollSnapDuration(), 0.5f, TEST_LOCATION ); + + scrollView.SetScrollFlickDuration( 2.0f ); + DALI_TEST_EQUALS( scrollView.GetScrollFlickDuration(), 2.0f, TEST_LOCATION ); + scrollView.SetScrollFlickDuration( 1.5f ); + DALI_TEST_EQUALS( scrollView.GetScrollFlickDuration(), 1.5f, TEST_LOCATION ); +} + +static void UtcDaliScrollViewSignalsSnapStart() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSignalsSnapStart"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + Stage::GetCurrent().Add( scrollView ); + Vector2 stageSize = Stage::GetCurrent().GetSize(); + scrollView.SetSize(stageSize); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetRefreshInterval(0); + scrollView.SetParentOrigin(ParentOrigin::TOP_LEFT); + scrollView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + + // Position rulers. + RulerPtr rulerX = new DefaultRuler(); + RulerPtr rulerY = new DefaultRuler(); + rulerX->SetDomain( RulerDomain(0.0f, stageSize.width + CLAMP_EXCESS_WIDTH, true) ); + rulerY->SetDomain( RulerDomain(0.0f, stageSize.height + CLAMP_EXCESS_HEIGHT, true) ); + scrollView.SetRulerX(rulerX); + scrollView.SetRulerY(rulerY); + scrollView.SnapStartedSignal().Connect( &OnSnapStart ); + + scrollView.ScrollTo(CLAMP_START_SCROLL_POSITION, 0.0f); // move in a little. + Wait(application); + + DALI_TEST_CHECK( !gOnSnapStartCalled ); + + // First try a snap. + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, Vector2(0.5f, 0.0f), 60, true); + + DALI_TEST_CHECK( gOnSnapStartCalled ); + DALI_TEST_CHECK( gLastSnapType == Toolkit::Snap ); + + // Second try a swipe. + PerformGestureDiagonalSwipe(application, CLAMP_TOUCH_START, Vector2(20.0f, 0.0f), 60, true); + + DALI_TEST_CHECK( gOnSnapStartCalled ); + DALI_TEST_CHECK( gLastSnapType == Toolkit::Flick ); +} + +static void UtcDaliScrollViewUIComponent() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewUIComponent"); + + // Set up a scrollView... + ScrollView scrollView = ScrollView::New(); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.EnableScrollComponent( Scrollable::VerticalScrollBar ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.EnableScrollComponent( Scrollable::HorizontalScrollBar ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.EnableScrollComponent( Scrollable::OvershootIndicator ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.DisableScrollComponent( Scrollable::VerticalScrollBar ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.DisableScrollComponent( Scrollable::HorizontalScrollBar ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + scrollView.DisableScrollComponent( Scrollable::OvershootIndicator ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::HorizontalScrollBar) ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::VerticalScrollBar) ); + DALI_TEST_CHECK( !scrollView.IsScrollComponentEnabled(Scrollable::OvershootIndicator) ); + + // Create scroll bar + ScrollBar scrollBar = ScrollBar::New(scrollView, true); + scrollBar.Show(); + scrollBar.Hide(); + + // Check downcast + const ScrollBar scrollBarVertical = ScrollBar(scrollBar); + BaseHandle handle(scrollBarVertical); + + ScrollBar newScrollBar = ScrollBar::DownCast( handle ); + DALI_TEST_CHECK( scrollBarVertical ); + DALI_TEST_CHECK( newScrollBar == scrollBarVertical ); + + ScrollComponent scrollComponent = ScrollComponent(scrollBarVertical); + handle = scrollComponent; + + ScrollComponent newScrollComponent = ScrollComponent::DownCast( handle ); + DALI_TEST_CHECK( scrollComponent ); + DALI_TEST_CHECK( scrollComponent == scrollComponent ); +} + +static void UtcDaliScrollViewSetMouseWheelScrollDistanceStep() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSetMouseWheelScrollDistanceStep"); + + ScrollView scrollView = ScrollView::New(); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetMouseWheelScrollDistanceStep(Vector2(30.0f, 15.0f)); + DALI_TEST_EQUALS( scrollView.GetMouseWheelScrollDistanceStep(), Vector2(30.0f, 15.0f), TEST_LOCATION ); + scrollView.SetMouseWheelScrollDistanceStep(Vector2(60.0f, 30.0f)); + DALI_TEST_EQUALS( scrollView.GetMouseWheelScrollDistanceStep(), Vector2(60.0f, 30.0f), TEST_LOCATION); +} + +static void UtcDaliScrollViewGetSet() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewGetSet"); + ScrollView scrollView = ScrollView::New(); + scrollView.SetMaxOvershoot(50.0f, 50.0f); + scrollView.SetMaxFlickSpeed(0.5f); + DALI_TEST_EQUALS(scrollView.GetMaxFlickSpeed(), 0.5f, Math::MACHINE_EPSILON_0, TEST_LOCATION); + scrollView.SetFrictionCoefficient(0.6f); + DALI_TEST_EQUALS(scrollView.GetFrictionCoefficient(), 0.6f, Math::MACHINE_EPSILON_0, TEST_LOCATION); + scrollView.SetFlickSpeedCoefficient(0.7f); + DALI_TEST_EQUALS(scrollView.GetFlickSpeedCoefficient(), 0.7f, Math::MACHINE_EPSILON_0, TEST_LOCATION); +} diff --git a/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollViewEffect.cpp b/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollViewEffect.cpp new file mode 100644 index 0000000..0d0aa90 --- /dev/null +++ b/automated-tests/dali-test-suite/scroll-view/utc-Dali-ScrollViewEffect.cpp @@ -0,0 +1,934 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliScrollViewCustomEffectSetup(); +static void UtcDaliScrollViewCubeEffectSetup(); +static void UtcDaliScrollViewPageCubeEffectSetup(); +static void UtcDaliScrollViewSpiralEffectSetup(); +static void UtcDaliScrollViewPageCarouselEffectSetup(); +static void UtcDaliScrollViewCarouselEffectSetup(); +static void UtcDaliScrollViewDepthEffectSetup(); +static void UtcDaliScrollViewSlideEffectSetup(); +static void UtcDaliScrollViewTwistEffectSetup(); + +static void UtcDaliScrollViewCubeEffectTest(); +static void UtcDaliScrollViewPageCubeEffectTest(); +static void UtcDaliScrollViewSpiralEffectTest(); +static void UtcDaliScrollViewPageCarouselEffectTest(); +static void UtcDaliScrollViewCarouselEffectTest(); +static void UtcDaliScrollViewDepthEffectTest(); +static void UtcDaliScrollViewSlideEffectTest(); +static void UtcDaliScrollViewTwistEffectTest(); +static void UtcDaliScrollViewCustomEffectTest(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliScrollViewCustomEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewCubeEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewPageCubeEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewSpiralEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewPageCarouselEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewCarouselEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewDepthEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewSlideEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewTwistEffectSetup, POSITIVE_TC_IDX }, + { UtcDaliScrollViewCubeEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewPageCubeEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewSpiralEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewPageCarouselEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewCarouselEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewDepthEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewSlideEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewTwistEffectTest, POSITIVE_TC_IDX }, + { UtcDaliScrollViewCustomEffectTest, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +namespace // unnamed namespace +{ + +const int MILLISECONDS_PER_SECOND = 1000; +const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS) +const int RENDER_ANIMATION_TEST_DURATION_MS = 1000; ///< 1000ms to test animation +const int RENDER_DELAY_SCROLL = 1000; ///< duration to wait for any scroll to complete. + +/* + * Simulate time passed by. + * + * @note this will always process at least 1 frame (1/60 sec) + * + * @param application Test application instance + * @param duration Time to pass in milliseconds. + * @return The actual time passed in milliseconds + */ +int Wait(ToolkitTestApplication& application, int duration = 0) +{ + int time = 0; + + for(int i = 0; i <= ( duration / RENDER_FRAME_INTERVAL); i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + time += RENDER_FRAME_INTERVAL; + } + + return time; +} + +/** + * Creates a Ruler that snaps to a specified grid size. + * If that grid size is 0.0 then this ruler does not + * snap. + * + * @param[in] gridSize (optional) The grid size for the ruler, + * (Default = 0.0 i.e. no snapping) + * @return The ruler is returned. + */ +RulerPtr CreateRuler(float gridSize = 0.0f) +{ + if(gridSize <= Math::MACHINE_EPSILON_0) + { + return new DefaultRuler(); + } + return new FixedRuler(gridSize); +} + +// Callback probes. + +static bool gOnScrollStartCalled; ///< Whether the OnScrollStart signal was invoked. +static bool gOnScrollUpdateCalled; ///< Whether the OnScrollUpdate signal was invoked. +static bool gOnScrollCompleteCalled; ///< Whether the OnScrollComplete signal was invoked. +static bool gOnScrollClampedCalled; ///< Whether the OnScrollClamped signal was invoked. +static bool gOnSnapStartCalled; ///< Whether the OnSnapStart signal was invoked. +static ClampState3 gLastClampPosition; ///< Clamping information from OnScrollClampedEvent. +static SnapType gLastSnapType; ///< Snaping information from SnapEvent. +static Vector3 gConstraintResult; ///< Result from constraint. + +static ActorContainer gPages; ///< Keeps track of all the pages for applying effects. + +static void ResetScrollCallbackResults() +{ + gOnScrollStartCalled = false; + gOnScrollUpdateCalled = false; + gOnScrollCompleteCalled = false; +} + +/** + * Invoked when scrolling starts. + * + * @param[in] position The current scroll position. + */ +static void OnScrollStart( const Vector3& position ) +{ + gOnScrollStartCalled = true; +} + +/** + * Invoked when scrolling updates (via dragging) + * + * @param[in] position The current scroll position. + */ +static void OnScrollUpdate( const Vector3& position ) +{ + gOnScrollUpdateCalled = true; +} + +/** + * Invoked when scrolling finishes + * + * @param[in] position The current scroll position. + */ +static void OnScrollComplete( const Vector3& position ) +{ + gOnScrollCompleteCalled = true; +} + +/** + * Invoked when scrolling clamped. + * + * @param[in] event The position/scale/rotation axes that were clamped. + */ +static void OnScrollClamped( const ScrollView::ClampEvent& event ) +{ + gOnScrollClampedCalled = true; + gLastClampPosition = event.position; +} + +/** + * Invoked when a snap or flick started. + * + * @param[in] event The type of snap and the target position/scale/rotation. + */ +static void OnSnapStart( const ScrollView::SnapEvent& event ) +{ + gOnSnapStartCalled = true; + gLastSnapType = event.type; +} + +ScrollView SetupTestScrollView(int rows, int columns, Vector2 size) +{ + ScrollView scrollView = ScrollView::New(); + scrollView.SetSize(size); + scrollView.SetAnchorPoint(AnchorPoint::CENTER); + scrollView.SetParentOrigin(ParentOrigin::CENTER); + scrollView.ApplyConstraint( Constraint::New( Dali::Actor::SIZE, Dali::ParentSource( Dali::Actor::SIZE ), Dali::EqualToConstraint() ) ); + // Disable Refresh signal (TET environment cannot use adaptor's Timer) + scrollView.SetWrapMode(false); + scrollView.SetRefreshInterval(0); + scrollView.ScrollStartedSignal().Connect( &OnScrollStart ); + scrollView.ScrollUpdatedSignal().Connect( &OnScrollUpdate ); + scrollView.ScrollCompletedSignal().Connect( &OnScrollComplete ); + Stage::GetCurrent().Add( scrollView ); + RulerPtr rulerX = CreateRuler(size.width); + RulerPtr rulerY = CreateRuler(size.height); + if(columns > 1) + { + rulerX->SetDomain(RulerDomain(0.0f, size.width * columns)); + } + else + { + rulerX->Disable(); + } + if(rows > 1) + { + rulerY->SetDomain(RulerDomain(0.0f, size.width * rows)); + } + else + { + rulerY->Disable(); + } + + scrollView.SetRulerX( rulerX ); + scrollView.SetRulerY( rulerY ); + Stage::GetCurrent().Add( scrollView ); + + Actor container = Actor::New(); + container.SetParentOrigin(ParentOrigin::CENTER); + container.SetAnchorPoint(AnchorPoint::CENTER); + container.SetSize( size ); + scrollView.Add( container ); + container.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + + gPages.clear(); + for(int row = 0;row( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + page.SetParentOrigin( ParentOrigin::CENTER ); + page.SetAnchorPoint( AnchorPoint::CENTER ); + page.SetPosition( column * size.x, row * size.y ); + container.Add(page); + + gPages.push_back(page); + } + } + + ResetScrollCallbackResults(); + return scrollView; +} + +void CleanupTest() +{ + gPages.clear(); + ResetScrollCallbackResults(); +} + +Actor AddActorToPage(Actor page, float x, float y, float cols, float rows) +{ + Stage stage = Stage::GetCurrent(); + Vector2 stageSize = stage.GetSize(); + + const float margin = 10.0f; + const Vector2 actorSize((stageSize.x / cols) - margin, (stageSize.y / rows) - margin); + + Actor actor = Actor::New(); + actor.SetParentOrigin( ParentOrigin::CENTER ); + actor.SetAnchorPoint( AnchorPoint::CENTER ); + + Vector3 position( margin * 0.5f + (actorSize.x + margin) * x - stageSize.width * 0.5f, + margin * 0.5f + (actorSize.y + margin) * y - stageSize.height * 0.5f, + 0.0f); + Vector3 positionEnd( margin * 0.5f + (actorSize.x + margin) * (x + cols) - stageSize.width * 0.5f - margin, + margin * 0.5f + (actorSize.y + margin) * (y + rows) - stageSize.height * 0.5f - margin, + 0.0f); + Vector3 size(positionEnd - position); + actor.SetPosition( position + size * 0.5f); + actor.SetSize( positionEnd - position ); + page.Add(actor); + return actor; +} + +} // unnamed namespace + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliScrollViewCustomEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewCustomEffectSetup"); + + ScrollViewCustomEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewCustomEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewCustomEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); + +} + +static void UtcDaliScrollViewCubeEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewCubeEffectSetup"); + + ScrollViewCubeEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewCubeEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewCubeEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewPageCubeEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewPageCubeEffectSetup"); + + ScrollViewPageCubeEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewPageCubeEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewPageCubeEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewSpiralEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewSpiralEffectSetup"); + + ScrollViewPageSpiralEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewPageSpiralEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewPageSpiralEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewPageCarouselEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewCarouselEffectSetup"); + + ScrollViewPageCarouselEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewPageCarouselEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewPageCarouselEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewCarouselEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewCarouselEffectSetup"); + + ScrollViewCarouselEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewCarouselEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewCarouselEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewDepthEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewDepthEffectSetup"); + + ScrollViewDepthEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewDepthEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewDepthEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewSlideEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewSlideEffectSetup"); + + ScrollViewSlideEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewSlideEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewSlideEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewTwistEffectSetup() +{ + tet_infoline(" UtcDaliScrollViewTwistEffectSetup"); + + ScrollViewTwistEffect effect; + + DALI_TEST_CHECK( !effect ); + + BaseHandle handle = ScrollViewTwistEffect::New(); + + DALI_TEST_CHECK( handle ); + + effect = ScrollViewTwistEffect::DownCast(handle); + + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliScrollViewCubeEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewCubeEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor page = gPages[1]; + Wait(application, 500); + + ScrollViewCubeEffect effect = ScrollViewCubeEffect::New(); + scrollView.ApplyEffect(effect); + + Actor actor = AddActorToPage(page, 0.5f, 0.5f, 3, 3); + Wait(application); + Vector3 actorPrePosition = actor.GetCurrentPosition(); + + effect.ApplyToActor(actor, page, Vector3(-105.0f, 30.0f, -240.0f), Vector2(Math::PI * 0.5f, Math::PI * 0.5f), Vector2(0.25f, 0.25f) * size); + + Actor actor2 = AddActorToPage(page, 0.5f, 0.5f, 3, 3); + effect.ApplyToActor(actor2, Vector3(-105.0f, 30.0f, -240.0f), Vector2(Math::PI * 0.5f, Math::PI * 0.5f), Vector2(0.25f, 0.25f) * size); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 actorPostPosition = actor.GetCurrentPosition(); + // just check the actor has moved + DALI_TEST_CHECK((actorPostPosition - actorPrePosition).Length() > Math::MACHINE_EPSILON_1); + CleanupTest(); +} + +static void UtcDaliScrollViewPageCubeEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewPageCubeEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewPageCubeEffect effect = ScrollViewPageCubeEffect::New(); + scrollView.ApplyEffect(effect); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page, Vector2(Math::PI_2, 0.0f)); + } + Wait(application); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + CleanupTest(); +} + +static void UtcDaliScrollViewSpiralEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSpiralEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewPageSpiralEffect effect = ScrollViewPageSpiralEffect::New(); + scrollView.ApplyEffect(effect); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page, Vector2(Math::PI_2, 0.0f)); + } + Wait(application); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + CleanupTest(); +} + +static void UtcDaliScrollViewPageCarouselEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewPageCarouselEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewPageCarouselEffect effect = ScrollViewPageCarouselEffect::New(); + scrollView.ApplyEffect(effect); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page); + } + Wait(application); + + scrollView.ScrollTo(1, 0.5f, DirectionBiasNone); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + CleanupTest(); +} + +static void UtcDaliScrollViewCarouselEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewCarouselEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewCarouselEffect effect = ScrollViewCarouselEffect::New(); + scrollView.ApplyEffect(effect); + + Actor actor = AddActorToPage(testPage, 0.5f, 0.5f, 3, 3); + Wait(application); + Vector3 actorPrePosition = actor.GetCurrentPosition(); + + effect.ApplyToActor( actor, Vector2(1.2f, 1.2f) ); + + scrollView.ScrollTo(Vector3(size.x, 0.0f, 0.0f), 0.5f, DirectionBiasNone, DirectionBiasNone); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 actorPostPosition = actor.GetCurrentPosition(); + // just check the actor has moved + DALI_TEST_CHECK((actorPostPosition - actorPrePosition).Length() > Math::MACHINE_EPSILON_1); + CleanupTest(); +} + +static void UtcDaliScrollViewDepthEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewDepthEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewDepthEffect effect = ScrollViewDepthEffect::New(); + scrollView.ApplyEffect(effect); + + Actor actor = AddActorToPage(testPage, 0.5f, 0.5f, 3, 3); + Wait(application); + Vector3 actorPrePosition = actor.GetCurrentPosition(); + + const Vector2 positionExtent(0.5f, 2.5f); + const Vector2 offsetExtent(1.0f, 1.0f); + const float positionScale(1.5f); + const float scaleExtent(0.5f); + + effect.ApplyToActor( actor, positionExtent, offsetExtent, positionScale, scaleExtent ); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 actorPostPosition = actor.GetCurrentPosition(); + // just check the actor has moved + DALI_TEST_CHECK((actorPostPosition - actorPrePosition).Length() > Math::MACHINE_EPSILON_1); + CleanupTest(); +} + +static void UtcDaliScrollViewSlideEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewSlideEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + Vector3 pageSize(size.x, size.y, 0.0f); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewSlideEffect effect = ScrollViewSlideEffect::New(); + effect.SetDelayReferenceOffset(pageSize * 0.25); + DALI_TEST_EQUALS(effect.GetDelayReferenceOffset(), pageSize * 0.25, Math::MACHINE_EPSILON_0, TEST_LOCATION); + effect.SetMaxDelayDuration(0.5f); + DALI_TEST_EQUALS(effect.GetMaxDelayDuration(), 0.5f, Math::MACHINE_EPSILON_0, TEST_LOCATION); + effect.SetSlideDirection(false); + DALI_TEST_CHECK(!effect.GetSlideDirection()); + + scrollView.ApplyEffect(effect); + + Actor actor = AddActorToPage(testPage, 0.5f, 0.5f, 3, 3); + Wait(application); + Vector3 actorPrePosition = actor.GetCurrentPosition(); + + effect.ApplyToActor(actor, 0.0f, 0.5f); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 actorPostPosition = actor.GetCurrentPosition(); + // just check the actor has moved + DALI_TEST_CHECK((actorPostPosition - actorPrePosition).Length() > Math::MACHINE_EPSILON_1); + CleanupTest(); +} + +static void UtcDaliScrollViewTwistEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewTwistEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + + ScrollViewTwistEffect effect = ScrollViewTwistEffect::New(); + float shrinkDist = 0.2f; + effect.SetMinimumDistanceForShrink(shrinkDist); + DALI_TEST_CHECK((shrinkDist - effect.GetMinimumDistanceForShrink()) < Math::MACHINE_EPSILON_0); + effect.EnableEffect(true); + scrollView.ApplyEffect(effect); + + Actor actor = AddActorToPage(testPage, 0.5f, 0.5f, 3, 3); + Wait(application); + Vector3 actorPrePosition = actor.GetCurrentPosition(); + + effect.ApplyToActor( actor, + true, + Vector2(Math::PI_2, Math::PI_2), + 0.0f); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + // test that the first page has reached centre of screen + Vector3 actorPostPosition = actor.GetCurrentPosition(); + // just check the actor has moved + DALI_TEST_CHECK((actorPostPosition - actorPrePosition).Length() > Math::MACHINE_EPSILON_1); + CleanupTest(); +} + +static void UtcDaliScrollViewCustomEffectTest() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScrollViewCustomEffectTest"); + + Vector2 size = Stage::GetCurrent().GetSize(); + Vector3 pageSize(size.x, size.y, 0.0f); + + ScrollView scrollView = SetupTestScrollView(1, 3, size); + Actor testPage = gPages[1]; + Wait(application, 500); + Vector3 pageStartPos, pagePos; + pageStartPos = pagePos = testPage.GetCurrentPosition(); + //scrollView.RemoveConstraintsFromChildren(); + + ScrollViewCustomEffect effect = ScrollViewCustomEffect::DownCast(scrollView.ApplyEffect(ScrollView::PageEffectCarousel)); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page, pageSize); + } + Wait(application); + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, pageStartPos, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + // test that the first page has reached centre of screen + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + // scroll back to page 0 + scrollView.ScrollTo(0); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, pageStartPos, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + scrollView.RemoveEffect(effect); + + effect = ScrollViewCustomEffect::New(); + effect.SetPageTranslation(Vector3(20.0f, 20.0f, 5.0f)); + effect.SetPageTranslation(Vector3(20.0f, 20.0f, 5.0f), Vector3(20.0f, 20.0f, -5.0f)); + effect.SetPageTranslationIn(Vector3(20.0f, 20.0f, 5.0f)); + effect.SetPageTranslationOut(Vector3(20.0f, 20.0f, -5.0f)); + effect.SetPageTranslation(Vector3(20.0f, 0.0f, 0.0f)); + effect.SetSwingAngle(Math::PI, Vector3::YAXIS); + effect.SetPageSpacing(Vector2(20.0f, 20.0f)); + scrollView.ApplyEffect(effect); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page, pageSize); + } + Wait(application); + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, pageStartPos, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + // test that the first page has reached centre of screen + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + // scroll back to page 0 + scrollView.ScrollTo(0); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, pageStartPos, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + scrollView.RemoveEffect(effect); + effect = ScrollViewCustomEffect::New(); + effect.SetSwingAngle(Math::PI, Vector3::YAXIS); + effect.SetSwingAnchor(AnchorPoint::CENTER_LEFT); + effect.SetPageTranslation(Vector3(size.x, size.y, 0)); + effect.SetOpacityThreshold(0.66f); + scrollView.ApplyEffect(effect); + + for(ActorIter pageIter = gPages.begin(); pageIter != gPages.end(); ++pageIter) + { + Actor page = *pageIter; + page.RemoveConstraints(); + page.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + effect.ApplyToPage(page, pageSize); + } + Wait(application); + + scrollView.ScrollTo(1); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + // test that the first page has reached centre of screen + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, Vector3::ZERO, Math::MACHINE_EPSILON_0, TEST_LOCATION); + + // scroll back to page 0 + scrollView.ScrollTo(0); + while(!gOnScrollCompleteCalled) + { + Wait(application); + } + ResetScrollCallbackResults(); + pagePos = testPage.GetCurrentPosition(); + DALI_TEST_EQUALS(pagePos, pageStartPos, Math::MACHINE_EPSILON_0, TEST_LOCATION); + scrollView.RemoveEffect(effect); + + + effect.SetPageTranslateAlphaFunction(AlphaFunctions::Linear); + effect.SetPageTranslateAlphaFunction(AlphaFunctions::Linear, AlphaFunctions::Linear); + effect.SetPageTranslateAlphaFunctionIn(AlphaFunctions::Linear); + effect.SetPageTranslateAlphaFunctionOut(AlphaFunctions::Linear); + effect.SetGlobalPageRotation(Math::PI, Vector3::YAXIS); + effect.SetAngledOriginPageRotation(Vector3(Math::PI, Math::PI, 0.0f)); + effect.SetGlobalPageRotation(Math::PI, Vector3::YAXIS, Math::PI, Vector3::YAXIS); + effect.SetGlobalPageRotationIn(Math::PI, Vector3::YAXIS); + effect.SetGlobalPageRotationOut(Math::PI, Vector3::YAXIS); + effect.SetGlobalPageRotationOrigin(Vector3::ZERO); + effect.SetGlobalPageRotationOrigin(Vector3::ZERO, Vector3::ZERO); + effect.SetGlobalPageRotationOriginIn(Vector3::ZERO); + effect.SetGlobalPageRotationOriginOut(Vector3::ZERO); + effect.SetSwingAngle(Math::PI, Vector3::YAXIS); + effect.SetSwingAngle(Math::PI, Vector3::YAXIS, Math::PI, Vector3::YAXIS); + effect.SetSwingAngleIn(Math::PI, Vector3::YAXIS); + effect.SetSwingAngleOut(Math::PI, Vector3::YAXIS); + effect.SetSwingAngleAlphaFunction(AlphaFunctions::Linear); + effect.SetSwingAngleAlphaFunction(AlphaFunctions::Linear, AlphaFunctions::Linear); + effect.SetSwingAngleAlphaFunctionIn(AlphaFunctions::Linear); + effect.SetSwingAngleAlphaFunctionOut(AlphaFunctions::Linear); + effect.SetSwingAnchor(AnchorPoint::CENTER, AnchorPoint::CENTER_LEFT); + effect.SetSwingAnchorIn(AnchorPoint::CENTER); + effect.SetSwingAnchorOut(AnchorPoint::CENTER); + effect.SetSwingAnchorAlphaFunction(AlphaFunctions::Linear); + effect.SetSwingAnchorAlphaFunction(AlphaFunctions::Linear, AlphaFunctions::Linear); + effect.SetSwingAnchorAlphaFunctionIn(AlphaFunctions::Linear); + effect.SetSwingAnchorAlphaFunctionOut(AlphaFunctions::Linear); + effect.SetOpacityThreshold(0.5f); + effect.SetOpacityThreshold(0.5f, 0.5f); + effect.SetOpacityThresholdIn(0.5f); + effect.SetOpacityThresholdOut(0.5f); + effect.SetOpacityAlphaFunction(AlphaFunctions::Linear); + effect.SetOpacityAlphaFunction(AlphaFunctions::Linear, AlphaFunctions::Linear); + effect.SetOpacityAlphaFunctionIn(AlphaFunctions::Linear); + effect.SetOpacityAlphaFunctionOut(AlphaFunctions::Linear); + CleanupTest(); +} diff --git a/automated-tests/dali-test-suite/selectors/.gitignore b/automated-tests/dali-test-suite/selectors/.gitignore new file mode 100644 index 0000000..7113688 --- /dev/null +++ b/automated-tests/dali-test-suite/selectors/.gitignore @@ -0,0 +1 @@ +utc-Dali-RotatingSelector diff --git a/automated-tests/dali-test-suite/selectors/Makefile b/automated-tests/dali-test-suite/selectors/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/selectors/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/selectors/file.list b/automated-tests/dali-test-suite/selectors/file.list new file mode 100644 index 0000000..58ab7be --- /dev/null +++ b/automated-tests/dali-test-suite/selectors/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-RotatingSelector \ diff --git a/automated-tests/dali-test-suite/selectors/tslist b/automated-tests/dali-test-suite/selectors/tslist new file mode 100644 index 0000000..6301f5a --- /dev/null +++ b/automated-tests/dali-test-suite/selectors/tslist @@ -0,0 +1 @@ +/dali-test-suite/selectors/utc-Dali-RotatingSelector diff --git a/automated-tests/dali-test-suite/selectors/utc-Dali-RotatingSelector.cpp b/automated-tests/dali-test-suite/selectors/utc-Dali-RotatingSelector.cpp new file mode 100644 index 0000000..02506d2 --- /dev/null +++ b/automated-tests/dali-test-suite/selectors/utc-Dali-RotatingSelector.cpp @@ -0,0 +1,277 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliRotatingSelectorNew(); +static void UtcDaliRotatingSelectorSetSelected(); +static void UtcDaliRotatingSelectorSetSelectedAndUnSelectedActor(); +static void UtcDaliRotatingSelectorSetSelectable(); +static void UtcDaliRotatingSelectorSignalSelected(); + +static bool gSelectedSignalReceived = false; +static bool gSelected = false; + +const Dali::TouchPoint pointDownInside( 0, TouchPoint::Down, 240, 400 ); +const Dali::TouchPoint pointUpInside( 0, TouchPoint::Up, 240, 400 ); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliRotatingSelectorNew, POSITIVE_TC_IDX }, + { UtcDaliRotatingSelectorSetSelected, POSITIVE_TC_IDX }, + { UtcDaliRotatingSelectorSetSelectedAndUnSelectedActor, POSITIVE_TC_IDX }, + { UtcDaliRotatingSelectorSetSelectable, POSITIVE_TC_IDX }, + { UtcDaliRotatingSelectorSignalSelected, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliRotatingSelectorNew() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliRotatingSelectorNew"); + RotatingSelector selector; + + DALI_TEST_CHECK(!selector); + + Actor unSelectedActor = Actor::New(); + Actor selectedActor = Actor::New(); + + selector = RotatingSelector::New(unSelectedActor, selectedActor); + + DALI_TEST_CHECK(selector); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + RotatingSelector selector = RotatingSelector::New(unSelectedActor, selectedActor); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +// Callback test function +void OnSelectedSignal(RotatingSelector actor, bool selected) +{ + gSelectedSignalReceived = true; + gSelected = selected; +} + +static void UtcDaliRotatingSelectorSetSelected() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliRotatingSelectorSetSelected"); + + BitmapImage img = BitmapImage::New( 1,1 ); + ImageActor unSelectedActor = ImageActor::New( img ); + ImageActor selectedActor = ImageActor::New( img ); + + RotatingSelector selector = RotatingSelector::New(unSelectedActor, selectedActor); + + selector.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + selector.SetParentOrigin( ParentOrigin::TOP_LEFT ); + selector.SetPosition( 240, 400 ); + selector.SetSize( 100, 100 ); + + // connect to its selected signal + selector.SelectedSignal().Connect( &OnSelectedSignal ); + + Stage::GetCurrent().Add( selector ); + + gSelectedSignalReceived = false; + gSelected = false; + + selector.SetSelected(true); + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + + DALI_TEST_CHECK( selector.IsSelected() ); + DALI_TEST_CHECK( gSelectedSignalReceived ); + DALI_TEST_CHECK( gSelected ); + + gSelectedSignalReceived = false; + gSelected = false; + + selector.SetSelected(false); + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + + DALI_TEST_CHECK( gSelectedSignalReceived ); + DALI_TEST_CHECK( !gSelected ); + DALI_TEST_CHECK( !selector.IsSelected() ); +} + +static void UtcDaliRotatingSelectorSetSelectedAndUnSelectedActor() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliRotatingSelectorSetSelectedAndUnSelectedActor"); + + BitmapImage img = BitmapImage::New( 1,1 ); + ImageActor actor1 = ImageActor::New( img ); + ImageActor actor2 = ImageActor::New( img ); + + RotatingSelector selector = RotatingSelector::New(actor1, actor2); + Stage::GetCurrent().Add( selector ); + + ImageActor unSelectedActor = ImageActor::New( img ); + ImageActor selectedActor = ImageActor::New( img ); + + selector.SetSelectedActor(selectedActor); + + Actor actor3 = selector.GetSelectedActor(); + DALI_TEST_CHECK( selectedActor == actor3 ); + + selector.SetUnSelectedActor(unSelectedActor); + + Actor actor4 = selector.GetUnSelectedActor(); + DALI_TEST_CHECK( unSelectedActor == actor4 ); + +} + + +static void UtcDaliRotatingSelectorSetSelectable() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliRotatingSelectorSetSelectable"); + + BitmapImage img = BitmapImage::New( 1,1 ); + ImageActor unSelectedActor = ImageActor::New( img ); + ImageActor selectedActor = ImageActor::New( img ); + + RotatingSelector selector = RotatingSelector::New(unSelectedActor, selectedActor); + + selector.SetSelectable(true); + DALI_TEST_CHECK( selector.IsSelectable() ); + + selector.SetSelectable(false); + DALI_TEST_CHECK( !selector.IsSelectable() ); +} + +static void UtcDaliRotatingSelectorSignalSelected() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliRotatingSelectorSignalSelected"); + + BitmapImage img = BitmapImage::New( 1,1 ); + ImageActor unSelectedActor = ImageActor::New( img ); + ImageActor selectedActor = ImageActor::New( img ); + + RotatingSelector selector = RotatingSelector::New(unSelectedActor, selectedActor); + + selector.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + selector.SetParentOrigin( ParentOrigin::TOP_LEFT ); + selector.SetPosition( 240, 400 ); + selector.SetSize( 100, 100 ); + + // connect to its selected signal + selector.SelectedSignal().Connect( &OnSelectedSignal ); + + Stage::GetCurrent().Add( selector ); + + DALI_TEST_CHECK( !selector.IsSelected() ); + + gSelectedSignalReceived = false; + gSelected = false; + + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + + //Test using touch event simulation + Dali::Integration::TouchEvent event; + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointDownInside ); + application.GetCore().SendEvent( event ); + + event = Dali::Integration::TouchEvent(); + event.AddPoint( pointUpInside ); + application.GetCore().SendEvent( event ); + + application.SendNotification(); + application.Render(1000); + application.SendNotification(); + application.Render(1000); + + DALI_TEST_CHECK( selector.IsSelected() ); + DALI_TEST_CHECK( gSelectedSignalReceived ); + DALI_TEST_CHECK( gSelected ); + +} diff --git a/automated-tests/dali-test-suite/shader-effects/.gitignore b/automated-tests/dali-test-suite/shader-effects/.gitignore new file mode 100644 index 0000000..7d83a0c --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/.gitignore @@ -0,0 +1,23 @@ +utc-Dali-BendyEffect +utc-Dali-DissolveEffect +utc-Dali-RippleEffect +utc-Dali-Ripple2DEffect +utc-Dali-SpotEffect +utc-Dali-SwirlEffect +utc-Dali-WaterEffect +utc-Dali-BlindEffect +utc-Dali-IrisEffect +utc-Dali-DissolveEffect +utc-Dali-DistanceFieldEffect +utc-Dali-SquareDissolveEffect +utc-Dali-CarouselEffect +utc-Dali-ShearEffect +utc-Dali-BloomView +utc-Dali-GaussianBlurView +utc-Dali-ShadowView +utc-Dali-SoftButtonEffect +utc-Dali-DisplacementEffect +utc-Dali-OverlayEffect +utc-Dali-MaskEffect +utc-Dali-NinePatchMaskEffect +utc-Dali-PageTurnEffect diff --git a/automated-tests/dali-test-suite/shader-effects/Makefile b/automated-tests/dali-test-suite/shader-effects/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/shader-effects/file.list b/automated-tests/dali-test-suite/shader-effects/file.list new file mode 100644 index 0000000..b7a9acd --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/file.list @@ -0,0 +1,23 @@ +TARGETS += \ + utc-Dali-BendyEffect \ + utc-Dali-DissolveEffect \ + utc-Dali-RippleEffect \ + utc-Dali-Ripple2DEffect \ + utc-Dali-SpotEffect \ + utc-Dali-SwirlEffect \ + utc-Dali-WaterEffect \ + utc-Dali-BlindEffect \ + utc-Dali-IrisEffect \ + utc-Dali-SquareDissolveEffect \ + utc-Dali-CarouselEffect \ + utc-Dali-DistanceFieldEffect \ + utc-Dali-ShearEffect \ + utc-Dali-BloomView \ + utc-Dali-GaussianBlurView \ + utc-Dali-ShadowView \ + utc-Dali-SoftButtonEffect \ + utc-Dali-DisplacementEffect \ + utc-Dali-OverlayEffect \ + utc-Dali-MaskEffect \ + utc-Dali-NinePatchMaskEffect \ + utc-Dali-PageTurnEffect \ diff --git a/automated-tests/dali-test-suite/shader-effects/tslist b/automated-tests/dali-test-suite/shader-effects/tslist new file mode 100644 index 0000000..c9268e2 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/tslist @@ -0,0 +1,22 @@ +/dali-test-suite/shader-effects/utc-Dali-BendyEffect +/dali-test-suite/shader-effects/utc-Dali-DissolveEffect +/dali-test-suite/shader-effects/utc-Dali-RippleEffect +/dali-test-suite/shader-effects/utc-Dali-Ripple2DEffect +/dali-test-suite/shader-effects/utc-Dali-SpotEffect +/dali-test-suite/shader-effects/utc-Dali-SwirlEffect +/dali-test-suite/shader-effects/utc-Dali-WaterEffect +/dali-test-suite/shader-effects/utc-Dali-BlindEffect +/dali-test-suite/shader-effects/utc-Dali-IrisEffect +/dali-test-suite/shader-effects/utc-Dali-SquareDissolveEffect +/dali-test-suite/shader-effects/utc-Dali-CarouselEffect +/dali-test-suite/shader-effects/utc-Dali-ShearEffect +/dali-test-suite/shader-effects/utc-Dali-DistanceFieldEffect +/dali-test-suite/shader-effects/utc-Dali-BloomView +/dali-test-suite/shader-effects/utc-Dali-GaussianBlurView +/dali-test-suite/shader-effects/utc-Dali-ShadowView +/dali-test-suite/shader-effects/utc-Dali-SoftButtonEffect +/dali-test-suite/shader-effects/utc-Dali-DisplacementEffect +/dali-test-suite/shader-effects/utc-Dali-OverlayEffect +/dali-test-suite/shader-effects/utc-Dali-MaskEffect +/dali-test-suite/shader-effects/utc-Dali-NinePatchMaskEffect +/dali-test-suite/shader-effects/utc-Dali-PageTurnEffect diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-BendyEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BendyEffect.cpp new file mode 100644 index 0000000..7e94e93 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BendyEffect.cpp @@ -0,0 +1,193 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliBendyUninitializedEffect, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBendyPropertyNamesEffect, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBendyDefaultValuesEffect, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliBendyCustomValuesEffect, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliBendyUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::BendyEffect effect; + + try + { + // New() must be called to create a BendyEffect or it wont be valid. + effect.SetRadius( 2.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliBendyPropertyNamesEffect() +{ + ToolkitTestApplication application; + + Toolkit::BendyEffect effect = Toolkit::BendyEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetDirectionPropertyName(), "uDirection", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetRadiusPropertyName(), "uRadius", TEST_LOCATION ); +} + +static void UtcDaliBendyDefaultValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::BendyEffect effect = Toolkit::BendyEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + Vector2 leftCorner( Stage::GetCurrent().GetSize() * 0.5f ); + leftCorner.x = -leftCorner.x; + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + leftCorner ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetDirectionPropertyName().c_str(), + Vector2(0.0f, 0.0f) ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 0.0f ) ); +} + +static void UtcDaliBendyCustomValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::BendyEffect effect = Toolkit::BendyEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + Vector2 direction(1.0f, 1.0f); + effect.SetCenter( Vector2(480.0f, 800.0f) ); + effect.SetDirection( direction ); + effect.SetRadius( 2.0f ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2(240.0f, -400.0f) ) ); + + direction.Normalize(); + direction.y *= -1.0f; + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetDirectionPropertyName().c_str(), + direction ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 2.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-BlindEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BlindEffect.cpp new file mode 100644 index 0000000..11089e8 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BlindEffect.cpp @@ -0,0 +1,168 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliBlindEffectUninitialized(); +static void UtcDaliBlindEffectPropertyNames(); +static void UtcDaliBlindEffectDefaultValues(); +static void UtcDaliBlindEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliBlindEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliBlindEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliBlindEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliBlindEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliBlindEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::BlindEffect effect; + + try + { + // New() must be called to create a BlindEffect or it wont be valid. + effect.SetStep( 2.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliBlindEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::BlindEffect effect = Toolkit::BlindEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetStepPropertyName(), "uStep", TEST_LOCATION ); +} + +static void UtcDaliBlindEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::BlindEffect effect = Toolkit::BlindEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetStepPropertyName().c_str(), + 0.0f ) ); +} + +static void UtcDaliBlindEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::BlindEffect effect = Toolkit::BlindEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetStep( 2.0f ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetStepPropertyName().c_str(), + 2.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-BloomView.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BloomView.cpp new file mode 100644 index 0000000..706b1ff --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-BloomView.cpp @@ -0,0 +1,194 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliBloomViewUninitialized(); +static void UtcDaliBloomViewNew(); +static void UtcDaliBloomViewDownCast(); +static void UtcDaliBloomViewPropertyNames(); +static void UtcDaliBloomViewAddRemove(); +static void UtcDaliBloomActivateDeactivate(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliBloomViewUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliBloomViewNew, POSITIVE_TC_IDX }, + { UtcDaliBloomViewDownCast, POSITIVE_TC_IDX }, + { UtcDaliBloomViewPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliBloomViewAddRemove, POSITIVE_TC_IDX }, + { UtcDaliBloomActivateDeactivate, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Negative test case for a method +static void UtcDaliBloomViewUninitialized() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomViewUninitialized"); + + Toolkit::BloomView view; + + try + { + // New() must be called to create a BloomView or it wont be valid. + Actor a = Actor::New(); + view.Add( a ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!view); + } +} + +// Positive test case for a method +static void UtcDaliBloomViewNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomViewNew"); + + Toolkit::BloomView view = Toolkit::BloomView::New(); + DALI_TEST_CHECK( view ); + + Toolkit::BloomView view2 = Toolkit::BloomView::New(10, 1.0f, Pixel::RGB888, 0.5f, 0.5f); + DALI_TEST_CHECK( view2 ); +} + +// Positive test case for a method +static void UtcDaliBloomViewDownCast() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomViewDownCast"); + + Toolkit::BloomView view = Toolkit::BloomView::New(); + BaseHandle handle(view); + + Toolkit::BloomView bloomView = Toolkit::BloomView::DownCast( handle ); + DALI_TEST_CHECK( view ); + DALI_TEST_CHECK( bloomView ); + DALI_TEST_CHECK( bloomView == view ); +} + + +// Positive test case for a method +static void UtcDaliBloomViewPropertyNames() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomViewPropertyNames"); + + Toolkit::BloomView view = Toolkit::BloomView::New(); + DALI_TEST_CHECK( view ); + + // Check the names, this names are used in the shader code, + // if they change in the shader code, then it has to be updated here. + DALI_TEST_EQUALS( view.GetBloomThresholdPropertyIndex(), view.GetPropertyIndex("uBloomThreshold"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetBlurStrengthPropertyIndex(), view.GetPropertyIndex("BlurStrengthProperty"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetBloomIntensityPropertyIndex(), view.GetPropertyIndex("uBloomIntensity"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetBloomSaturationPropertyIndex(), view.GetPropertyIndex("uBloomSaturation"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetImageIntensityPropertyIndex(), view.GetPropertyIndex("uImageIntensity"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetImageSaturationPropertyIndex(), view.GetPropertyIndex("uImageSaturation"), TEST_LOCATION ); +} + +// Positive test case for a method +static void UtcDaliBloomViewAddRemove() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomViewAddRemove"); + + Toolkit::BloomView view = Toolkit::BloomView::New(); + DALI_TEST_CHECK( view ); + + Actor actor = Actor::New(); + DALI_TEST_CHECK( !actor.OnStage() ); + + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(actor); + Stage::GetCurrent().Add(view); + + DALI_TEST_CHECK( actor.OnStage() ); + + view.Remove(actor); + + DALI_TEST_CHECK( !actor.OnStage() ); +} + +// Positive test case for a method +static void UtcDaliBloomActivateDeactivate() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliBloomActivateDeactivate"); + + Toolkit::BloomView view = Toolkit::BloomView::New(); + DALI_TEST_CHECK( view ); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList.GetTaskCount() ); + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(Actor::New()); + Stage::GetCurrent().Add(view); + view.Activate(); + + RenderTaskList taskList2 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u != taskList2.GetTaskCount() ); + + view.Deactivate(); + + RenderTaskList taskList3 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList3.GetTaskCount() ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-CarouselEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-CarouselEffect.cpp new file mode 100644 index 0000000..881ac64 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-CarouselEffect.cpp @@ -0,0 +1,179 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliCarouselEffectUninitialized(); +static void UtcDaliCarouselEffectPropertyNames(); +static void UtcDaliCarouselEffectDefaultValues(); +static void UtcDaliCarouselEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliCarouselEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliCarouselEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliCarouselEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliCarouselEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliCarouselEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::CarouselEffect effect; + + try + { + // New() must be called to create a CarouselEffect or it wont be valid. + effect.SetRadius( 100.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliCarouselEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::CarouselEffect effect = Toolkit::CarouselEffect::New(); + + // Check the names, these names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetRadiusPropertyName(), "uRadius", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetAnglePerUnitPropertyName(), "uAnglePerUnit", TEST_LOCATION ); +} + +static void UtcDaliCarouselEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::CarouselEffect effect = Toolkit::CarouselEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float radiusValue(0.0f); + const Vector2 centerValue(0.0f, 0.0f); + const Vector2 anglePerUnitValue(0.0f, 0.0f); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetRadiusPropertyName().c_str(), radiusValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), centerValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAnglePerUnitPropertyName().c_str(), anglePerUnitValue ) ); +} + +static void UtcDaliCarouselEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::CarouselEffect effect = Toolkit::CarouselEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float radiusValue(100.0f); + const Vector2 centerValue(150.0f, 200.0f); + const Vector2 anglePerUnitValue(0.1f, 0.25f); + + effect.SetRadius( radiusValue ); + effect.SetCenter( centerValue ); + effect.SetAnglePerUnit( anglePerUnitValue ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetRadiusPropertyName().c_str(), radiusValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), centerValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAnglePerUnitPropertyName().c_str(), anglePerUnitValue ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-DisplacementEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DisplacementEffect.cpp new file mode 100644 index 0000000..86100f8 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DisplacementEffect.cpp @@ -0,0 +1,177 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const char* TEST_IMAGE_FILE_NAME = DALI_IMAGE_DIR "gallery_image_01.jpg"; +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliDisplacementEffectUninitialized(); +static void UtcDaliDisplacementEffectNew(); +static void UtcDaliDisplacementEffectPropertyNames(); +static void UtcDaliDisplacementEffectTestSetProperty(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliDisplacementEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliDisplacementEffectNew, POSITIVE_TC_IDX }, + { UtcDaliDisplacementEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliDisplacementEffectTestSetProperty, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Negative test case for a method +static void UtcDaliDisplacementEffectUninitialized() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliDisplacementEffectUninitialized"); + + Toolkit::DisplacementEffect effect; + + try + { + // New() must be called to create a GaussianBlurView or it wont be valid. + effect.SetStateProperty( 1.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +// Positive test case for a method +static void UtcDaliDisplacementEffectNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliDisplacementEffectNew"); + + Toolkit::DisplacementEffect effect = Toolkit::DisplacementEffect::New(Toolkit::DisplacementEffect::DISPLACED); + DALI_TEST_CHECK( effect ); + + Toolkit::DisplacementEffect effect2 = Toolkit::DisplacementEffect::New(Toolkit::DisplacementEffect::FIXED); + DALI_TEST_CHECK( effect2 ); +} + +// Positive test case for a method +static void UtcDaliDisplacementEffectPropertyNames() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliDisplacementEffectPropertyNames"); + + Toolkit::DisplacementEffect effect = Toolkit::DisplacementEffect::New(Toolkit::DisplacementEffect::DISPLACED); + DALI_TEST_CHECK( effect ); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetLightDirectionPropertyName(), "uLightDirection", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetAmbientLightColorPropertyName(), "uAmbientLightColor", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetDiffuseLightColorPropertyName(), "uDiffuseLightColor", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetLightingMultiplierPropertyName(), "uLightMultiplier", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetStatePropertyName(), "uState", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetHeightScalePropertyName(), "uHightScale", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetFixedNormalPropertyName(), "uFixedNormal", TEST_LOCATION ); +} + +// Positive test case for a method +static void UtcDaliDisplacementEffectTestSetProperty() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliDisplacementEffectTestSetProperty"); + + Toolkit::DisplacementEffect effect = Toolkit::DisplacementEffect::New(Toolkit::DisplacementEffect::DISPLACED); + DALI_TEST_CHECK( effect ); + + ImageActor actor = ImageActor::New( Image::New(TEST_IMAGE_FILE_NAME) ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + Toolkit::DisplacementEffect effect2 = Toolkit::DisplacementEffect::New(Toolkit::DisplacementEffect::FIXED); + DALI_TEST_CHECK( effect ); + + ImageActor actor2 = ImageActor::New( Image::New(TEST_IMAGE_FILE_NAME) ); + actor2.SetSize( 100.0f, 100.0f ); + actor2.SetShaderEffect( effect2 ); + Stage::GetCurrent().Add( actor2 ); + + Vector3 testVector3 = Vector3(45.0f, 55.0f, 65.0f); + float testFloat = 0.623f; + effect.SetLightDirection(testVector3); + effect.SetAmbientLightColorProperty(testVector3); + effect.SetDiffuseLightColorProperty(testVector3); + effect.SetStateProperty(testFloat); + effect.SetLightingMultiplierProperty(testFloat); + effect.SetHeightScaleProperty(testFloat); + + effect2.SetFixedNormalProperty(testVector3); + + application.SendNotification(); + application.Render(0); + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetLightDirectionPropertyName() ) ).Get(), testVector3, TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetAmbientLightColorPropertyName() ) ).Get(), testVector3, TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetDiffuseLightColorPropertyName() ) ).Get(), testVector3, TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetStatePropertyName().c_str() ) ).Get(), testFloat, TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetLightingMultiplierPropertyName().c_str() ) ).Get(), testFloat, TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetProperty( effect.GetPropertyIndex( effect.GetHeightScalePropertyName().c_str() ) ).Get(), testFloat, TEST_LOCATION ); + + Vector3 normalizedVector3(testVector3); + normalizedVector3.Normalize(); + DALI_TEST_EQUALS( effect2.GetProperty( effect2.GetPropertyIndex( effect2.GetFixedNormalPropertyName() ) ).Get(), normalizedVector3, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-DissolveEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DissolveEffect.cpp new file mode 100644 index 0000000..13a6391 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DissolveEffect.cpp @@ -0,0 +1,197 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliDissolveUninitializedEffect(); +static void UtcDaliDissolvePropertyNamesEffect(); +static void UtcDaliDissolveDefaultValuesEffect(); +static void UtcDaliDissolveCustomValuesEffect(); +static void UtcDaliSetEffectImageEffect(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliDissolveUninitializedEffect, NEGATIVE_TC_IDX }, + { UtcDaliDissolvePropertyNamesEffect, POSITIVE_TC_IDX }, + { UtcDaliDissolveDefaultValuesEffect, POSITIVE_TC_IDX }, + { UtcDaliDissolveCustomValuesEffect, POSITIVE_TC_IDX }, + { UtcDaliSetEffectImageEffect, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliDissolveUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::DissolveEffect effect; + + try + { + // New() must be called to create a DissolveEffect or it wont be valid. + effect.SetDistortion( 2.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliDissolvePropertyNamesEffect() +{ + ToolkitTestApplication application; + + Toolkit::DissolveEffect effectHighPrecision = Toolkit::DissolveEffect::New(); + Toolkit::DissolveEffect effectMediumPrecision = Toolkit::DissolveEffect::New( false ); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effectHighPrecision.GetDistortionPropertyName(), "uPercentage", TEST_LOCATION ); + DALI_TEST_EQUALS( effectMediumPrecision.GetDistortionPropertyName(), "uPercentage", TEST_LOCATION ); +} + +static void UtcDaliDissolveDefaultValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::DissolveEffect effect = Toolkit::DissolveEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + effect.SetCentralLine( Vector2(0.0,0.5), Vector2(1.0, -0.1) ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + Property::Index index = effect.GetPropertyIndex( effect.GetDistortionPropertyName()); + float value; + (effect.GetProperty(index)).Get( value ); + DALI_TEST_EQUALS(value, 0.f, TEST_LOCATION ); +} + +static void UtcDaliDissolveCustomValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::DissolveEffect effect = Toolkit::DissolveEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetDistortion( 0.5f ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + Property::Index index = effect.GetPropertyIndex( effect.GetDistortionPropertyName()); + float value; + (effect.GetProperty(index)).Get( value ); + DALI_TEST_EQUALS(value, 0.5f, TEST_LOCATION ); +} + +static void UtcDaliSetEffectImageEffect() +{ + ToolkitTestApplication application; + + Toolkit::DissolveEffect effect = Toolkit::DissolveEffect::New(); + DALI_TEST_CHECK( effect ); + + Image effectImage = CreateBitmapImage(); + effect.SetEffectImage(effectImage); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + Property::Index index = effect.GetPropertyIndex( effect.GetDistortionPropertyName()); + float value; + (effect.GetProperty(index)).Get( value ); + DALI_TEST_EQUALS(value, 0.f, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-DistanceFieldEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DistanceFieldEffect.cpp new file mode 100644 index 0000000..6cbd1d1 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-DistanceFieldEffect.cpp @@ -0,0 +1,202 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliDistanceFieldEffectUninitialized(); +static void UtcDaliDistanceFieldEffectPropertyNames(); +static void UtcDaliDistanceFieldEffectDefaultValues(); +static void UtcDaliDistanceFieldEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliDistanceFieldEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliDistanceFieldEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliDistanceFieldEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliDistanceFieldEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateDistanceField() +{ + BitmapImage image = BitmapImage::New(256, 256, Pixel::RGBA8888); + BitmapImage distanceFieldImage = BitmapImage::New(256, 256, Pixel::L8); + + PixelBuffer* pixbuf = image.GetBuffer(); + + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + // GenerateDistanceFieldMap(distanceFieldImage.GetBuffer(), Size(256, 256), pixbuf, Size(256, 256), 8, 4); + + return distanceFieldImage; +} + +static void UtcDaliDistanceFieldEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::DistanceFieldEffect effect; + + try + { + // New() must be called to create a DistanceField effect or it wont be valid. + effect.SetShadow( true ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliDistanceFieldEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::DistanceFieldEffect effect = Toolkit::DistanceFieldEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetColorPropertyName(), "uColor", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetSmoothingPropertyName(), "uSmoothing", TEST_LOCATION ); + + // control flags + DALI_TEST_EQUALS( effect.GetOutlineEnablePropertyName(), "uDoOutline", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetGlowEnablePropertyName(), "uDoGlow", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetShadowEnablePropertyName(), "uDoShadow", TEST_LOCATION ); + + DALI_TEST_EQUALS( effect.GetGlowBoundaryPropertyName(), "uGlowBoundary", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetGlowColorPropertyName(), "uGlowColor", TEST_LOCATION ); + + DALI_TEST_EQUALS( effect.GetOutlineColorPropertyName(), "uOutlineColor", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetOutlineSizePropertyName(), "uOutlineParams", TEST_LOCATION ); + + DALI_TEST_EQUALS( effect.GetShadowColorPropertyName(), "uShadowColor", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetShadowOffsetPropertyName(), "uShadowOffset", TEST_LOCATION ); + +} + +static void UtcDaliDistanceFieldEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::DistanceFieldEffect effect = Toolkit::DistanceFieldEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateDistanceField(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetOutlineEnablePropertyName().c_str(), + 0.0f )); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetGlowEnablePropertyName().c_str(), + 0.0f )); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetShadowEnablePropertyName().c_str(), + 0.0f )); +} + +static void UtcDaliDistanceFieldEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::DistanceFieldEffect effect = Toolkit::DistanceFieldEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateDistanceField(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetShadowColor(Color::YELLOW); + effect.SetGlowColor(Color::BLUE); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetShadowColorPropertyName().c_str(), + Color::YELLOW ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetGlowColorPropertyName().c_str(), + Color::BLUE ) ); +} + diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-GaussianBlurView.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-GaussianBlurView.cpp new file mode 100644 index 0000000..96aace7 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-GaussianBlurView.cpp @@ -0,0 +1,232 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const char* TEST_IMAGE_FILE_NAME = DALI_IMAGE_DIR "gallery_image_01.jpg"; +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliGaussianBlurViewUninitialized(); +static void UtcDaliGaussianBlurViewNew(); +static void UtcDaliGaussianBlurViewDownCast(); +static void UtcDaliGaussianBlurViewPropertyNames(); +static void UtcDaliGaussianBlurViewAddRemove(); +static void UtcDaliGaussianBlurActivateDeactivate(); +static void UtcDaliGaussianBlurViewSetGetBackgroundColor(); +static void UtcDaliGaussianBlurViewSetGetRenderTarget(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliGaussianBlurViewUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliGaussianBlurViewNew, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurViewDownCast, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurViewPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurViewAddRemove, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurActivateDeactivate, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurViewSetGetBackgroundColor, POSITIVE_TC_IDX }, + { UtcDaliGaussianBlurViewSetGetRenderTarget, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Negative test case for a method +static void UtcDaliGaussianBlurViewUninitialized() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewUninitialized"); + + Toolkit::GaussianBlurView view; + + try + { + // New() must be called to create a GaussianBlurView or it wont be valid. + Actor a = Actor::New(); + view.Add( a ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!view); + } +} + +// Positive test case for a method +static void UtcDaliGaussianBlurViewNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewNew"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + DALI_TEST_CHECK( view ); + + Toolkit::GaussianBlurView view2 = Toolkit::GaussianBlurView::New(5, 1.5f, Pixel::RGB888, 0.5f, 0.5f, false); + DALI_TEST_CHECK( view2 ); +} + +// Positive test case for a method +static void UtcDaliGaussianBlurViewDownCast() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewDownCast"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + BaseHandle handle(view); + + Toolkit::GaussianBlurView gaussianBlurView = Toolkit::GaussianBlurView::DownCast( handle ); + DALI_TEST_CHECK( view ); + DALI_TEST_CHECK( gaussianBlurView ); + DALI_TEST_CHECK( gaussianBlurView == view ); +} + + +// Positive test case for a method +static void UtcDaliGaussianBlurViewPropertyNames() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewPropertyNames"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + DALI_TEST_CHECK( view ); + + // Check the names, this names are used in the shader code, + // if they change in the shader code, then it has to be updated here. + DALI_TEST_EQUALS( view.GetBlurStrengthPropertyIndex(), view.GetPropertyIndex("GaussianBlurStrengthPropertyName"), TEST_LOCATION ); +} + +// Positive test case for a method +static void UtcDaliGaussianBlurViewAddRemove() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewAddRemove"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + DALI_TEST_CHECK( view ); + + Actor actor = Actor::New(); + DALI_TEST_CHECK( !actor.OnStage() ); + + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(actor); + Stage::GetCurrent().Add(view); + + DALI_TEST_CHECK( actor.OnStage() ); + + view.Remove(actor); + + DALI_TEST_CHECK( !actor.OnStage() ); +} + +// Positive test case for a method +static void UtcDaliGaussianBlurActivateDeactivate() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurActivateDeactivate"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + DALI_TEST_CHECK( view ); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList.GetTaskCount() ); + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(Actor::New()); + Stage::GetCurrent().Add(view); + view.Activate(); + + RenderTaskList taskList2 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u != taskList2.GetTaskCount() ); + + view.Deactivate(); + + RenderTaskList taskList3 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList3.GetTaskCount() ); +} + +// Positive test case for a method +static void UtcDaliGaussianBlurViewSetGetBackgroundColor() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewSetGetBackgroundColor"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(); + DALI_TEST_CHECK( view ); + + view.SetBackgroundColor(Dali::Color::RED); + Vector4 color = view.GetBackgroundColor(); + DALI_TEST_CHECK( color == Dali::Color::RED ); +} + +// Positive test case for a method +static void UtcDaliGaussianBlurViewSetGetRenderTarget() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewSetGetRenderTarget"); + + Toolkit::GaussianBlurView view = Toolkit::GaussianBlurView::New(5, 1.5f, Pixel::RGB888, 0.5f, 0.5f, true); + DALI_TEST_CHECK( view ); + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(Actor::New()); + Stage::GetCurrent().Add(view); + view.Activate(); + + FrameBufferImage renderTarget = FrameBufferImage::New( 480.0f, 800.0f, Pixel::RGB888 ); + view.SetUserImageAndOutputRenderTarget(Image::New(TEST_IMAGE_FILE_NAME), renderTarget); + DALI_TEST_CHECK( view.GetBlurredRenderTarget() == renderTarget ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-IrisEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-IrisEffect.cpp new file mode 100644 index 0000000..f458d72 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-IrisEffect.cpp @@ -0,0 +1,179 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliIrisEffectUninitialized(); +static void UtcDaliIrisEffectPropertyNames(); +static void UtcDaliIrisEffectDefaultValues(); +static void UtcDaliIrisEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliIrisEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliIrisEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliIrisEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliIrisEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliIrisEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::IrisEffect effect; + + try + { + // New() must be called to create a IrisEffect or it wont be valid. + effect.SetRadius( 2.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliIrisEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::IrisEffect effect = Toolkit::IrisEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetRadiusPropertyName(), "uRadius", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetBlendFactorPropertyName(), "uBlendFactor", TEST_LOCATION ); +} + +static void UtcDaliIrisEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::IrisEffect effect = Toolkit::IrisEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float radiusValue(0.0f); + const Vector2 centerValue(0.5f, 0.5f); + const float blendFactorValue(100.0f); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetRadiusPropertyName().c_str(), radiusValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), centerValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetBlendFactorPropertyName().c_str(), blendFactorValue ) ); +} + +static void UtcDaliIrisEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::IrisEffect effect = Toolkit::IrisEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float radiusValue(23.0f); + const Vector2 centerValue(0.2f, 0.7f); + const float blendFactorValue(10.0f); + + effect.SetRadius( radiusValue ); + effect.SetCenter( centerValue ); + effect.SetBlendFactor( blendFactorValue ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetRadiusPropertyName().c_str(), radiusValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), centerValue ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetBlendFactorPropertyName().c_str(), blendFactorValue ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-MaskEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-MaskEffect.cpp new file mode 100644 index 0000000..3e7d279 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-MaskEffect.cpp @@ -0,0 +1,101 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliMaskEffectCreateEffect, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliMaskEffectDestructor, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +static BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliMaskEffectCreateEffect() +{ + ToolkitTestApplication application; + + BitmapImage image = CreateBitmapImage(); + + ShaderEffect effect = Toolkit::MaskEffect::New( image ); + DALI_TEST_CHECK( effect ); +} + +static void UtcDaliMaskEffectDestructor() +{ + ToolkitTestApplication application; + + Toolkit::MaskEffect* effect = new Toolkit::MaskEffect(); + delete effect; + + DALI_TEST_CHECK( true ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-NinePatchMaskEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-NinePatchMaskEffect.cpp new file mode 100644 index 0000000..09e8251 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-NinePatchMaskEffect.cpp @@ -0,0 +1,109 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliNinePatchMaskEffectApply, POSITIVE_TC_IDX ); + + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +static BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliNinePatchMaskEffectApply() +{ + ToolkitTestApplication application; + + BitmapImage image = CreateBitmapImage(); + ImageActor actor0 = ImageActor::New( image ); + Toolkit::NinePatchMaskEffect::Apply( actor0, "" ); + + Stage::GetCurrent().Add( actor0 ); + + application.SendNotification(); // Force usage of constraint + application.Render(); + + DALI_TEST_CHECK( actor0.GetStyle() == ImageActor::STYLE_NINE_PATCH ); + + ImageActor actor1 = ImageActor::New( image ); + Vector4 border( 0, 0, 0, 0 ); + Toolkit::NinePatchMaskEffect::Apply( actor1, "", border ); + + Stage::GetCurrent().Add( actor1 ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( actor1.GetStyle() == ImageActor::STYLE_NINE_PATCH ); +} + diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-OverlayEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-OverlayEffect.cpp new file mode 100644 index 0000000..5b84680 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-OverlayEffect.cpp @@ -0,0 +1,124 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliOverlayConstructor(); +static void UtcDaliOverlayUninitializedEffect(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliOverlayConstructor, POSITIVE_TC_IDX }, + { UtcDaliOverlayUninitializedEffect, NEGATIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliOverlayConstructor() +{ + ToolkitTestApplication application; + + BitmapImage image = CreateBitmapImage(); + + Toolkit::OverlayEffect effect = Toolkit::OverlayEffect::New( image ); + DALI_TEST_CHECK( effect ); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + +} + +static void UtcDaliOverlayUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::OverlayEffect effect; + + try + { + BitmapImage image = CreateBitmapImage(); + + // New() must be called to create a OverlayEffect or it wont be valid. + effect.SetEffectImage( image ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-PageTurnEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-PageTurnEffect.cpp new file mode 100644 index 0000000..94c0e9a --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-PageTurnEffect.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliPageTurnEffectApply, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliPageTurnEffectConstruct, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +static BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliPageTurnEffectApply() +{ + ToolkitTestApplication application; + + BitmapImage image = CreateBitmapImage(); + + Toolkit::PageTurnEffect pageTurnEffect = Toolkit::PageTurnEffect::New(); + Toolkit::PageTurnEffect pageTurnEffect2 = Toolkit::PageTurnEffect::New(false); + + ImageActor pageActor = ImageActor::New( image ); + ImageActor backPageActor = ImageActor::New( image ); + pageActor.Add( backPageActor ); + + pageTurnEffect.SetIsTurningBack( true ); + pageTurnEffect.SetShadowWidth( 0.0f ); + pageTurnEffect.SetSpineShadowParameter( Vector2( 0.0f, 0.0f ) ); + + pageActor.SetShaderEffect( pageTurnEffect ); + Stage::GetCurrent().Add( pageActor ); + + application.SendNotification(); + application.Render(); + + const Vector2 pageSize( 0.0f, 0.0f ); + pageTurnEffect.SetPageSize( pageSize ); + + const Vector2 originalCenter( 0.0f, 0.0f ); + pageTurnEffect.SetOriginalCenter( originalCenter ); + + const Vector2 currentCenter( 0.0f, 0.0f ); + pageTurnEffect.SetCurrentCenter( currentCenter ); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( pageTurnEffect.GetPageSizePropertyName().c_str(), pageSize ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( pageTurnEffect.GetOriginalCenterPropertyName().c_str(), originalCenter ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( pageTurnEffect.GetCurrentCenterPropertyName().c_str(), currentCenter ) ); +} + +static void UtcDaliPageTurnEffectConstruct() +{ + ToolkitTestApplication application; + + Toolkit::PageTurnEffect* effect = new Toolkit::PageTurnEffect(); + delete effect; + + DALI_TEST_CHECK( true ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-Ripple2DEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-Ripple2DEffect.cpp new file mode 100644 index 0000000..191a1aa --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-Ripple2DEffect.cpp @@ -0,0 +1,176 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliRipple2DEffectUninitialized(); +static void UtcDaliRipple2DEffectPropertyNames(); +static void UtcDaliRipple2DEffectDefaultValues(); +static void UtcDaliRipple2DEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliRipple2DEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliRipple2DEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliRipple2DEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliRipple2DEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliRipple2DEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::Ripple2DEffect effect; + + try + { + // New() must be called to create a Ripple2DEffect or it wont be valid. + effect.SetAmplitude( 0.5f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliRipple2DEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::Ripple2DEffect effect = Toolkit::Ripple2DEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetAmplitudePropertyName(), "uAmplitude", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetTimePropertyName(), "uTime", TEST_LOCATION ); +} + +static void UtcDaliRipple2DEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::Ripple2DEffect effect = Toolkit::Ripple2DEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName().c_str(), + 0.0f ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTimePropertyName().c_str(), + 0.0f ) ); +} + +static void UtcDaliRipple2DEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::Ripple2DEffect effect = Toolkit::Ripple2DEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + + effect.SetAmplitude( 5.0f ); + effect.SetTime( 2.0f ); + + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName().c_str(), + 5.0f ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTimePropertyName().c_str(), + 2.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-RippleEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-RippleEffect.cpp new file mode 100644 index 0000000..e71b820 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-RippleEffect.cpp @@ -0,0 +1,187 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliRippleUninitializedEffect(); +static void UtcDaliRipplePropertyNamesEffect(); +static void UtcDaliRippleDefaultValuesEffect(); +static void UtcDaliRippleCustomValuesEffect(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliRippleUninitializedEffect, NEGATIVE_TC_IDX }, + { UtcDaliRipplePropertyNamesEffect, POSITIVE_TC_IDX }, + { UtcDaliRippleDefaultValuesEffect, POSITIVE_TC_IDX }, + { UtcDaliRippleCustomValuesEffect, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliRippleUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::RippleEffect effect; + + try + { + // New() must be called to create a RippleEffect or it wont be valid. + effect.SetAmplitude( 0.5f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliRipplePropertyNamesEffect() +{ + ToolkitTestApplication application; + + Toolkit::RippleEffect effect = Toolkit::RippleEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetAmplitudePropertyName(), "uAmplitude", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetTimePropertyName(), "uTime", TEST_LOCATION ); + +} + +static void UtcDaliRippleDefaultValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::RippleEffect effect = Toolkit::RippleEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName().c_str(), + 0.0f ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2( 0.0f, 0.0f ) ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTimePropertyName().c_str(), + 0.0f ) ); +} + +static void UtcDaliRippleCustomValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::RippleEffect effect = Toolkit::RippleEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetAmplitude( 0.5f ); + effect.SetCenter( Vector2( 10.0f, 10.0f ) ); + effect.SetTime( 2.0f ); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName().c_str(), + 0.5f ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2( 10.0f, 10.0f ) ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTimePropertyName().c_str(), + 2.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShadowView.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShadowView.cpp new file mode 100644 index 0000000..385a569 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShadowView.cpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliShadowViewUninitialized(); +static void UtcDaliShadowViewNew(); +static void UtcDaliShadowViewDownCast(); +static void UtcDaliShadowViewPropertyNames(); +static void UtcDaliShadowViewAddRemove(); +static void UtcDaliShadowViewActivateDeactivate(); + + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliShadowViewUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliShadowViewNew, POSITIVE_TC_IDX }, + { UtcDaliShadowViewDownCast, POSITIVE_TC_IDX }, + { UtcDaliShadowViewPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliShadowViewAddRemove, POSITIVE_TC_IDX }, + { UtcDaliShadowViewActivateDeactivate, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Negative test case for a method +static void UtcDaliShadowViewUninitialized() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewUninitialized"); + + Toolkit::ShadowView view; + try + { + // New() must be called to create a GaussianBlurView or it wont be valid. + Actor a = Actor::New(); + view.Add( a ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!view); + } +} + +// Positive test case for a method +static void UtcDaliShadowViewNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewNew"); + + Toolkit::ShadowView view = Toolkit::ShadowView::New(); + DALI_TEST_CHECK( view ); + + Toolkit::ShadowView view2 = Toolkit::ShadowView::New(1.0f, 1.0f); + DALI_TEST_CHECK( view2 ); +} + +// Positive test case for a method +static void UtcDaliShadowViewDownCast() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewDownCast"); + + Toolkit::ShadowView view = Toolkit::ShadowView::New(); + BaseHandle handle(view); + + Toolkit::ShadowView shadowView = Toolkit::ShadowView::DownCast( handle ); + DALI_TEST_CHECK( view ); + DALI_TEST_CHECK( shadowView ); + DALI_TEST_CHECK( shadowView == view ); +} + +// Positive test case for a method +static void UtcDaliShadowViewPropertyNames() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewPropertyNames"); + + Toolkit::ShadowView view = Toolkit::ShadowView::New(); + DALI_TEST_CHECK( view ); + + // Check the names, this names are used in the shader code, + // if they change in the shader code, then it has to be updated here. + DALI_TEST_EQUALS( view.GetBlurStrengthPropertyIndex(), view.GetPropertyIndex("BlurStrengthProperty"), TEST_LOCATION ); + DALI_TEST_EQUALS( view.GetShadowColorPropertyIndex(), view.GetPropertyIndex("ShadowColorProperty"), TEST_LOCATION ); +} + +// Positive test case for a method +static void UtcDaliShadowViewAddRemove() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewAddRemove"); + + Toolkit::ShadowView view = Toolkit::ShadowView::New(); + DALI_TEST_CHECK( view ); + + Actor actor = Actor::New(); + DALI_TEST_CHECK( !actor.OnStage() ); + + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(actor); + Stage::GetCurrent().Add(view); + + DALI_TEST_CHECK( actor.OnStage() ); + + view.Remove(actor); + + DALI_TEST_CHECK( !actor.OnStage() ); +} + +// Positive test case for a method +static void UtcDaliShadowViewActivateDeactivate() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliShadowViewActivateDeactivate"); + + Toolkit::ShadowView view = Toolkit::ShadowView::New(); + DALI_TEST_CHECK( view ); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList.GetTaskCount() ); + + view.SetParentOrigin(ParentOrigin::CENTER); + view.SetSize(Stage::GetCurrent().GetSize()); + view.Add(Actor::New()); + Stage::GetCurrent().Add(view); + view.Activate(); + + RenderTaskList taskList2 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u != taskList2.GetTaskCount() ); + + view.Deactivate(); + + RenderTaskList taskList3 = Stage::GetCurrent().GetRenderTaskList(); + DALI_TEST_CHECK( 1u == taskList3.GetTaskCount() ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShearEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShearEffect.cpp new file mode 100644 index 0000000..9d5966f --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-ShearEffect.cpp @@ -0,0 +1,196 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliShearEffectUninitialized(); +static void UtcDaliShearEffectPropertyNames(); +static void UtcDaliShearEffectDefaultValues(); +static void UtcDaliShearEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliShearEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliShearEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliShearEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliShearEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliShearEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::ShearEffect effect; + + try + { + // New() must be called to create a ShearEffect or it wont be valid. + effect.SetAngleXAxis( 45.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliShearEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::ShearEffect effect = Toolkit::ShearEffect::New(); + + // Check the names, these names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetAngleXAxisPropertyName(), "uAngleXAxis", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetAngleYAxisPropertyName(), "uAngleYAxis", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); +} + +/** + * Converts value to screen position in the same way that + * the core does under COORDINATE_TYPE_SCREEN_POSITION + * + * @param[in] value the input position value. + * @return The translated position value ready for gl. + */ +Vector2 ToScreenPosition(Vector2 value) +{ + Vector2 stageSize = Dali::Stage::GetCurrent().GetSize(); + value.x = value.x - stageSize.x * 0.5f; + value.y = value.y - stageSize.y * 0.5f; + value.y *= -1.0f; + + return value; +} + +static void UtcDaliShearEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::ShearEffect effect = Toolkit::ShearEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float angleXAxis(0.0f); + const float angleYAxis(0.0f); + const Vector2 centerValue(0.0f, 0.0f); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAngleXAxisPropertyName().c_str(), angleXAxis ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAngleYAxisPropertyName().c_str(), angleYAxis ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), ToScreenPosition(centerValue) ) ); +} + +static void UtcDaliShearEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::ShearEffect effect = Toolkit::ShearEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + const float angleXAxis(10.0f); + const float angleYAxis(22.5f); + const Vector2 centerValue(50.0f, 100.0f); + + effect.SetAngleXAxis( angleXAxis ); + effect.SetAngleYAxis( angleYAxis ); + effect.SetCenter( centerValue ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAngleXAxisPropertyName().c_str(), angleXAxis ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetAngleYAxisPropertyName().c_str(), angleYAxis ) ); + DALI_TEST_CHECK( gl.CheckUniformValue( effect.GetCenterPropertyName().c_str(), ToScreenPosition(centerValue) ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-SoftButtonEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SoftButtonEffect.cpp new file mode 100644 index 0000000..3f26c51 --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SoftButtonEffect.cpp @@ -0,0 +1,110 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSoftButtonEffectUninitialized(); +static void UtcDaliSoftButtonEffectNew(); +static void UtcDaliSoftButtonEffectPropertyNames(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSoftButtonEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliSoftButtonEffectNew, POSITIVE_TC_IDX }, + { UtcDaliSoftButtonEffectPropertyNames, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Negative test case for a method +static void UtcDaliSoftButtonEffectUninitialized() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliSoftButtonEffectUninitialized"); + + Toolkit::SoftButtonEffect effect; + + // New() must be called to create a SoftButtonEffect or it wont be valid. + + DALI_TEST_CHECK(!effect); +} + +// Positive test case for a method +static void UtcDaliSoftButtonEffectNew() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliGaussianBlurViewNew"); + + Toolkit::SoftButtonEffect effect = Toolkit::SoftButtonEffect::New(Toolkit::SoftButtonEffect::ELLIPTICAL); + DALI_TEST_CHECK( effect ); +} + +// Positive test case for a method +static void UtcDaliSoftButtonEffectPropertyNames() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliSoftButtonEffectPropertyNames"); + + Toolkit::SoftButtonEffect effect = Toolkit::SoftButtonEffect::New(Toolkit::SoftButtonEffect::ELLIPTICAL); + DALI_TEST_CHECK( effect ); + + // Check the names, this names are used in the shader code, + // if they change in the shader code, then it has to be updated here. + DALI_TEST_EQUALS( effect.GetLightingIndentationAmountPropertyName(), "uLightingIndentationAmount", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetTextureDistortionAmountPropertyName(), "uTextureDistortAmount", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetAmbientLightAmountPropertyName(), "uAmbientLight", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetDiffuseLightPropertyName(), "uDiffuseLight", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetLightingMultiplierPropertyName(), "uLightMultiplier", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetInsideShapeSizeScalePropertyName(), "uInsideCircleSizeScale", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetOutsideShapeDepthPropertyName(), "uOutsideCircleDepth", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetEffectPixelAreaPropertyName(), "uEffectRegion", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetRectangleSizeScalePropertyName(), "uRectangleSizeScale", TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-SpotEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SpotEffect.cpp new file mode 100644 index 0000000..dd60bbb --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SpotEffect.cpp @@ -0,0 +1,180 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSpotUninitializedEffect(); +static void UtcDaliSpotPropertyNamesEffect(); +static void UtcDaliSpotDefaultValuesEffect(); +static void UtcDaliSpotCustomValuesEffect(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSpotUninitializedEffect, NEGATIVE_TC_IDX }, + { UtcDaliSpotPropertyNamesEffect, POSITIVE_TC_IDX }, + { UtcDaliSpotDefaultValuesEffect, POSITIVE_TC_IDX }, + { UtcDaliSpotCustomValuesEffect, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliSpotUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::SpotEffect effect; + + try + { + // New() must be called to create a SpotEffect or it wont be valid. + effect.SetRadius( 0.5f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliSpotPropertyNamesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SpotEffect effect = Toolkit::SpotEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetRadiusPropertyName(), "uRadius", TEST_LOCATION ); +} + +static void UtcDaliSpotDefaultValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SpotEffect effect = Toolkit::SpotEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2(0.0f, 0.0f) ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 0.0f ) ); +} + +static void UtcDaliSpotCustomValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SpotEffect effect = Toolkit::SpotEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetCenter( Vector2(480.0f, 800.0f) ); + effect.SetRadius( 5.0f ); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2(480.0f, 800.0f) ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 5.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-SquareDissolveEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SquareDissolveEffect.cpp new file mode 100644 index 0000000..57fb6ab --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SquareDissolveEffect.cpp @@ -0,0 +1,204 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSquareDissolveEffectUninitialized(); +static void UtcDaliSquareDissolveEffectPropertyNames(); +static void UtcDaliSquareDissolveEffectDefaultValues(); +static void UtcDaliSquareDissolveEffectCustomValues(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSquareDissolveEffectUninitialized, NEGATIVE_TC_IDX }, + { UtcDaliSquareDissolveEffectPropertyNames, POSITIVE_TC_IDX }, + { UtcDaliSquareDissolveEffectDefaultValues, POSITIVE_TC_IDX }, + { UtcDaliSquareDissolveEffectCustomValues, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliSquareDissolveEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::SquareDissolveEffect effect; + + try + { + // New() must be called to create a SquareDissolveEffect or it wont be valid. + effect.SetStep( 2.0f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliSquareDissolveEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::SquareDissolveEffect effect = Toolkit::SquareDissolveEffect::New(); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetStepPropertyName(), "uStep", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetRowsPropertyName(), "uRows", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetColumnsPropertyName(), "uColumns", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetTexSizePropertyName(), "texSize", TEST_LOCATION ); +} + +static void UtcDaliSquareDissolveEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::SquareDissolveEffect effect = Toolkit::SquareDissolveEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetStepPropertyName().c_str(), + 0.1f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRowsPropertyName().c_str(), + 25.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetColumnsPropertyName().c_str(), + 25.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTexSizePropertyName().c_str(), + Vector2(1.0f, 1.0f) ) ); +} + +static void UtcDaliSquareDissolveEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::SquareDissolveEffect effect = Toolkit::SquareDissolveEffect::New(); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetStep( 2.0f ); + effect.SetRows( 3.0f ); + effect.SetColumns( 4.0f ); + effect.SetTextureSize( Vector2(12.0f, 13.0f) ); + + actor.SetShaderEffect(effect); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetStepPropertyName().c_str(), + 2.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRowsPropertyName().c_str(), + 3.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetColumnsPropertyName().c_str(), + 4.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetTexSizePropertyName().c_str(), + Vector2(12.0f, 13.0f) ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-SwirlEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SwirlEffect.cpp new file mode 100644 index 0000000..ab2ec1e --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-SwirlEffect.cpp @@ -0,0 +1,192 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSwirlUninitializedEffect(); +static void UtcDaliSwirlPropertyNamesEffect(); +static void UtcDaliSwirlDefaultValuesEffect(); +static void UtcDaliSwirlCustomValuesEffect(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSwirlUninitializedEffect, NEGATIVE_TC_IDX }, + { UtcDaliSwirlPropertyNamesEffect, POSITIVE_TC_IDX }, + { UtcDaliSwirlDefaultValuesEffect, POSITIVE_TC_IDX }, + { UtcDaliSwirlCustomValuesEffect, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliSwirlUninitializedEffect() +{ + ToolkitTestApplication application; + + Toolkit::SwirlEffect effect; + + try + { + // New() must be called to create a SwirlEffect or it wont be valid. + effect.SetRadius( 0.5f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliSwirlPropertyNamesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SwirlEffect effect = Toolkit::SwirlEffect::New(false); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetAnglePropertyName(), "uAngle", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName(), "uCenter", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetRadiusPropertyName(), "uRadius", TEST_LOCATION ); +} + +static void UtcDaliSwirlDefaultValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SwirlEffect effect = Toolkit::SwirlEffect::New(true); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAnglePropertyName().c_str(), + 0.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2(0.5f, 0.5f) ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 1.0f ) ); +} + +static void UtcDaliSwirlCustomValuesEffect() +{ + ToolkitTestApplication application; + + Toolkit::SwirlEffect effect = Toolkit::SwirlEffect::New(false); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + + effect.SetAngle( 1.0f ); + effect.SetCenter( Vector2(0.3f, 0.7f) ); + effect.SetRadius( 2.0f ); + + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + // Gets converted to opengl viewport coordinates + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAnglePropertyName().c_str(), + 1.0f ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName().c_str(), + Vector2(0.3f, 0.7f) ) ); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetRadiusPropertyName().c_str(), + 2.0f ) ); +} diff --git a/automated-tests/dali-test-suite/shader-effects/utc-Dali-WaterEffect.cpp b/automated-tests/dali-test-suite/shader-effects/utc-Dali-WaterEffect.cpp new file mode 100644 index 0000000..f0e731b --- /dev/null +++ b/automated-tests/dali-test-suite/shader-effects/utc-Dali-WaterEffect.cpp @@ -0,0 +1,378 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliWaterEffectUninitialized, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectPropertyNames, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectOutOfBounds, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectDefaultValues, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectCustomValues, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetAmplitudePositive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetAmplitudeNegative, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetCenterPositive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetCenterNegative, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetPropagationPositive, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliWaterEffectGetPropagationNegative, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +// Create bitmap image +BitmapImage CreateBitmapImage() +{ + BitmapImage image = BitmapImage::New(4,4,Pixel::RGBA8888); + + PixelBuffer* pixbuf = image.GetBuffer(); + + // Using a 4x4 image gives a better blend with the GL implementation + // than a 3x3 image + for(size_t i=0; i<16; i++) + { + pixbuf[i*4+0] = 0xFF; + pixbuf[i*4+1] = 0xFF; + pixbuf[i*4+2] = 0xFF; + pixbuf[i*4+3] = 0xFF; + } + + return image; +} + +static void UtcDaliWaterEffectUninitialized() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect; + + try + { + // New() must be called to create a RippleEffect or it wont be valid. + effect.SetAmplitude( 0, 0.5f ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK(!effect); + } +} + +static void UtcDaliWaterEffectPropertyNames() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + // Check the names, this names are used in the shaders code, + // if they change the shader code has to be updated + DALI_TEST_EQUALS( effect.GetAmplitudePropertyName( 0 ), "uDrops[0].amplitude", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetCenterPropertyName( 0 ), "uDrops[0].center", TEST_LOCATION ); + DALI_TEST_EQUALS( effect.GetPropagationPropertyName( 0 ), "uDrops[0].radius", TEST_LOCATION ); +} + +static void UtcDaliWaterEffectOutOfBounds() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + try + { + // the highest index acceptable is (GetNumberOfWaves() - 1) + effect.SetAmplitude( effect.GetNumberOfWaves(), 0 ); + DALI_TEST_CHECK( false ); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_CHECK( true ); + } +} + +static void UtcDaliWaterEffectDefaultValues() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + // Check that the effect has the number of waves it was requested + DALI_TEST_CHECK( effect.GetNumberOfWaves() == 4 ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + application.SendNotification(); + application.Render(); + + Vector2 leftCorner( Stage::GetCurrent().GetSize() * 0.5f ); + leftCorner.x = -leftCorner.x; + + for ( unsigned int i = 0; i < effect.GetNumberOfWaves(); ++i ) + { + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName(i).c_str(), + 0.0f ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName(i).c_str(), + leftCorner ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetPropagationPropertyName(i).c_str(), + 0.0f ) ); + } +} + +static void UtcDaliWaterEffectCustomValues() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + effect.SetAmplitude( 0, 0.5f ); + effect.SetCenter( 0, Vector2 ( 10.0f, 10.0f ) ); + effect.SetPropagation( 0, 2.0f ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetAmplitudePropertyName(0).c_str(), + 0.5f ) ); + + Vector2 leftCorner( Stage::GetCurrent().GetSize() * 0.5f ); + leftCorner.x = -leftCorner.x; + + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetCenterPropertyName(0).c_str(), + Vector2( leftCorner.x + 10.0f, leftCorner.y - 10.0f ) ) ); + DALI_TEST_CHECK( + application.GetGlAbstraction().CheckUniformValue( + effect.GetPropagationPropertyName(0).c_str(), + 2.0f ) ); +} + +static void UtcDaliWaterEffectGetAmplitudePositive() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + float amplitude(0.5f); + DALI_TEST_CHECK(effect.GetAmplitude(0) != amplitude); + effect.SetAmplitude( 0, amplitude ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(amplitude, effect.GetAmplitude(0), TEST_LOCATION); +} + +static void UtcDaliWaterEffectGetAmplitudeNegative() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + try + { + effect.GetAmplitude(9999); + tet_result(TET_FAIL); + } + catch(DaliException& exception) + { + if (exception.mCondition == "index < mNumberOfWaves") + { + tet_result(TET_PASS); + } + } +} + +static void UtcDaliWaterEffectGetCenterPositive() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + Vector2 center(10.0f, 20.0f); + DALI_TEST_CHECK(effect.GetCenter(0) != center); + effect.SetCenter( 0, center ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(center, effect.GetCenter(0), TEST_LOCATION); +} + +static void UtcDaliWaterEffectGetCenterNegative() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + try + { + effect.GetCenter(9999); + tet_result(TET_FAIL); + } + catch(DaliException& exception) + { + if (exception.mCondition == "index < mNumberOfWaves") + { + tet_result(TET_PASS); + } + } +} + +static void UtcDaliWaterEffectGetPropagationPositive() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + float propagation(0.5f); + DALI_TEST_CHECK(effect.GetPropagation(0) != propagation); + effect.SetPropagation( 0, propagation ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(propagation, effect.GetPropagation(0), TEST_LOCATION); +} + +static void UtcDaliWaterEffectGetPropagationNegative() +{ + ToolkitTestApplication application; + + Toolkit::WaterEffect effect = Toolkit::WaterEffect::New(4); + DALI_TEST_CHECK( effect ); + + BitmapImage image = CreateBitmapImage(); + + ImageActor actor = ImageActor::New( image ); + actor.SetSize( 100.0f, 100.0f ); + actor.SetShaderEffect( effect ); + Stage::GetCurrent().Add( actor ); + + try + { + effect.GetPropagation(9999); + tet_result(TET_FAIL); + } + catch(DaliException& exception) + { + if (exception.mCondition == "index < mNumberOfWaves") + { + tet_result(TET_PASS); + } + } +} diff --git a/automated-tests/dali-test-suite/slider/.gitignore b/automated-tests/dali-test-suite/slider/.gitignore new file mode 100644 index 0000000..1403d3c --- /dev/null +++ b/automated-tests/dali-test-suite/slider/.gitignore @@ -0,0 +1 @@ +utc-Dali-Slider diff --git a/automated-tests/dali-test-suite/slider/Makefile b/automated-tests/dali-test-suite/slider/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/slider/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/slider/file.list b/automated-tests/dali-test-suite/slider/file.list new file mode 100644 index 0000000..a7db699 --- /dev/null +++ b/automated-tests/dali-test-suite/slider/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-Slider \ diff --git a/automated-tests/dali-test-suite/slider/tslist b/automated-tests/dali-test-suite/slider/tslist new file mode 100644 index 0000000..976e985 --- /dev/null +++ b/automated-tests/dali-test-suite/slider/tslist @@ -0,0 +1 @@ +/dali-test-suite/slider/utc-Dali-Slider diff --git a/automated-tests/dali-test-suite/slider/utc-Dali-Slider.cpp b/automated-tests/dali-test-suite/slider/utc-Dali-Slider.cpp new file mode 100644 index 0000000..1aa0c32 --- /dev/null +++ b/automated-tests/dali-test-suite/slider/utc-Dali-Slider.cpp @@ -0,0 +1,199 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSliderNew(); +static void UtcDaliSliderDestructor(); +static void UtcDaliSliderDownCast(); +static void UtcDaliSliderSignals(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSliderNew, POSITIVE_TC_IDX }, + { UtcDaliSliderDestructor, POSITIVE_TC_IDX }, + { UtcDaliSliderDownCast, POSITIVE_TC_IDX }, + { UtcDaliSliderSignals, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +static void UtcDaliSliderNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliSliderNew"); + + // Create the Slider actor + Slider slider; + + DALI_TEST_CHECK( !slider ); + + slider = Slider::New(); + + DALI_TEST_CHECK( slider ); + + Slider slider2(slider); + + DALI_TEST_CHECK( slider2 == slider ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + Slider slider = Slider::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliSliderDestructor() +{ + ToolkitTestApplication application; + + Slider* slider = new Slider(); + delete slider; + + DALI_TEST_CHECK( true ); +} + +static void UtcDaliSliderDownCast() +{ + ToolkitTestApplication application; + + Handle handle = Slider::New(); + + Slider slider = Slider::DownCast( handle ); + + DALI_TEST_CHECK( slider == handle ); +} + +static bool gSliderValueChangedCallBackCalled; +static bool OnSliderValueChanged( Slider slider, float value ) +{ + gSliderValueChangedCallBackCalled = true; + return true; +} + +static bool gSliderMarkCallBackCalled; +static bool OnSliderMark( Slider slider, int value ) +{ + gSliderMarkCallBackCalled = true; + return true; +} + +static void UtcDaliSliderSignals() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + tet_infoline(" UtcDaliSliderSignals"); + + // Create the Popup actor + Slider slider = Slider::New(); + Stage::GetCurrent().Add( slider ); + slider.SetParentOrigin(ParentOrigin::TOP_LEFT); + slider.SetAnchorPoint(ParentOrigin::TOP_LEFT); + slider.SetSize( Stage::GetCurrent().GetSize().x, 20.0f ); + slider.SetPosition( 0.0f, 0.0f ); + + const float MIN_BOUND = 0.0f; + const float MAX_BOUND = 1.0f; + const int NUM_MARKS = 5; + Property::Array marks; + for( int i = 0; i < NUM_MARKS; ++i ) + { + marks.push_back( MIN_BOUND + ( static_cast(i) / ( NUM_MARKS - 1) ) * ( MAX_BOUND - MIN_BOUND ) ); + } + slider.SetProperty( slider.GetPropertyIndex( Slider::MARKS_PROPERTY_NAME ), marks ); + slider.SetProperty( slider.GetPropertyIndex( Slider::MARK_TOLERANCE_PROPERTY_NAME ), 0.1f ); + + slider.ValueChangedSignal().Connect( &OnSliderValueChanged ); + slider.MarkSignal().Connect( &OnSliderMark ); + + application.SendNotification(); + application.Render(); + + gSliderValueChangedCallBackCalled = false; + gSliderMarkCallBackCalled = false; + + Dali::Integration::TouchEvent event; + + event = Dali::Integration::TouchEvent(); + + const Dali::TouchPoint pointDown( 0, TouchPoint::Down, 10.0f, 10.0f ); + event.AddPoint( pointDown ); + + for( int i = 0; i < 5; ++i ) + { + const Dali::TouchPoint pointDown( 0, TouchPoint::Motion, 10.0f + i * 10.0f, 10.0f ); + event.AddPoint( pointDown ); + } + + const Dali::TouchPoint pointUp( 0, TouchPoint::Up, 50.0f, 10.0f ); + event.AddPoint( pointUp ); + + application.GetCore().SendEvent( event ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(gSliderValueChangedCallBackCalled); + DALI_TEST_CHECK(gSliderMarkCallBackCalled); +} diff --git a/automated-tests/dali-test-suite/super-blur-view/.gitignore b/automated-tests/dali-test-suite/super-blur-view/.gitignore new file mode 100644 index 0000000..7e95e96 --- /dev/null +++ b/automated-tests/dali-test-suite/super-blur-view/.gitignore @@ -0,0 +1 @@ +utc-Dali-SuperBlurView diff --git a/automated-tests/dali-test-suite/super-blur-view/Makefile b/automated-tests/dali-test-suite/super-blur-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/super-blur-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/super-blur-view/file.list b/automated-tests/dali-test-suite/super-blur-view/file.list new file mode 100644 index 0000000..7e2bf9e --- /dev/null +++ b/automated-tests/dali-test-suite/super-blur-view/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-SuperBlurView \ diff --git a/automated-tests/dali-test-suite/super-blur-view/tslist b/automated-tests/dali-test-suite/super-blur-view/tslist new file mode 100644 index 0000000..7095b08 --- /dev/null +++ b/automated-tests/dali-test-suite/super-blur-view/tslist @@ -0,0 +1 @@ +/dali-test-suite/super-blur-view/utc-Dali-SuperBlurView diff --git a/automated-tests/dali-test-suite/super-blur-view/utc-Dali-SuperBlurView.cpp b/automated-tests/dali-test-suite/super-blur-view/utc-Dali-SuperBlurView.cpp new file mode 100644 index 0000000..99d0781 --- /dev/null +++ b/automated-tests/dali-test-suite/super-blur-view/utc-Dali-SuperBlurView.cpp @@ -0,0 +1,240 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +const int BLUR_LEVELS = 3; +const int RENDER_FRAME_INTERVAL = 16; + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +/* + * Simulate time passed by. + * + * @note this will always process at least 1 frame (1/60 sec) + * + * @param application Test application instance + * @param duration Time to pass in milliseconds. + * @return The actual time passed in milliseconds + */ +int Wait(ToolkitTestApplication& application, int duration = 0) +{ + int time = 0; + + for(int i = 0; i <= ( duration / RENDER_FRAME_INTERVAL); i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + time += RENDER_FRAME_INTERVAL; + } + + return time; +} + +Image CreateSolidColorImage( ToolkitTestApplication& application, const Vector4& color, unsigned int width, unsigned int height ) +{ + BitmapImage imageData = BitmapImage::New( width, height, Pixel::RGBA8888 ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + unsigned int size = width * height; + + for( size_t i = 0; i < size; i++ ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + imageData.Update(); + + application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE ); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + application.Render(RENDER_FRAME_INTERVAL); + application.SendNotification(); + + return imageData; +} +}//namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliSuperBlurViewNew(); +static void UtcDaliSuperBlurViewSetImage(); +static void UtcDaliSuperBlurViewSetGetBlurStrength(); +static void UtcDaliSuperBlurViewGetBlurStrengthPropertyIndex(); +static void UtcDaliSuperBlurViewGetBlurredImage(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliSuperBlurViewNew, POSITIVE_TC_IDX }, + { UtcDaliSuperBlurViewSetImage, POSITIVE_TC_IDX }, + { UtcDaliSuperBlurViewSetGetBlurStrength, POSITIVE_TC_IDX }, + { UtcDaliSuperBlurViewGetBlurStrengthPropertyIndex, POSITIVE_TC_IDX }, + { UtcDaliSuperBlurViewGetBlurredImage, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliSuperBlurViewNew() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliSuperBlurViewNew "); + + // Test default constructor. + SuperBlurView blurView; + DALI_TEST_CHECK( !blurView ); + + // Test object creation + blurView = SuperBlurView::New( BLUR_LEVELS ); + DALI_TEST_CHECK( blurView ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + SuperBlurView blurView = SuperBlurView::New( BLUR_LEVELS ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + // Test copy constructor + SuperBlurView blurViewCopy2( blurView ); + DALI_TEST_CHECK( blurViewCopy2 ); + + // Test down cast + Actor actorView; + actorView = blurView; + SuperBlurView downCastView = SuperBlurView::DownCast( actorView ); + DALI_TEST_CHECK( downCastView ); +} + +static void UtcDaliSuperBlurViewSetImage() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliSuperBlurViewSetImage "); + + SuperBlurView blurView = SuperBlurView::New( BLUR_LEVELS ); + // create image actors for the original image and each blurred image + DALI_TEST_CHECK( blurView.GetChildCount() == BLUR_LEVELS+1 ); + + Image inputImage = CreateSolidColorImage( application, Color::GREEN, 50, 50 ); + blurView.SetImage( inputImage ); + // start multiple guassian blur call, each guassian blur creates two render tasks + DALI_TEST_CHECK( Stage::GetCurrent().GetRenderTaskList().GetTaskCount() == BLUR_LEVELS*2 + 1); +} + +static void UtcDaliSuperBlurViewSetGetBlurStrength() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliSuperBlurViewSetGetBlurStrength "); + + SuperBlurView blurView = SuperBlurView::New( BLUR_LEVELS ); + DALI_TEST_EQUALS(blurView.GetCurrentBlurStrength(), 0.f, TEST_LOCATION ); + + blurView.SetBlurStrength( 0.65f ); + Wait(application); + DALI_TEST_EQUALS(blurView.GetCurrentBlurStrength(), 0.65f, TEST_LOCATION ); +} + +static void UtcDaliSuperBlurViewGetBlurStrengthPropertyIndex() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliSuperBlurViewGetBlurStrengthPropertyIndex "); + + SuperBlurView blurView = SuperBlurView::New( BLUR_LEVELS ); + Property::Index blurPropertyIdx = blurView.GetBlurStrengthPropertyIndex(); + + float blurStrength; + (blurView.GetProperty( blurPropertyIdx )).Get(blurStrength); + DALI_TEST_EQUALS(blurStrength, 0.f, TEST_LOCATION ); + + blurView.SetBlurStrength( 0.65f ); + Wait(application); + (blurView.GetProperty( blurPropertyIdx )).Get(blurStrength); + DALI_TEST_EQUALS(blurStrength, 0.65f, TEST_LOCATION ); +} + +static void UtcDaliSuperBlurViewGetBlurredImage() +{ + ToolkitTestApplication application; + + tet_infoline( "UtcDaliSuperBlurViewGetBlurredImage" ); + + SuperBlurView blurView = SuperBlurView::New( BLUR_LEVELS ); + blurView.SetSize( 100.f,100.f ); + Image inputImage = CreateSolidColorImage( application, Color::GREEN, 100, 100 ); + blurView.SetImage( inputImage ); + + Wait(application, 200); // Make sure all the gaussian blur finished + + Image image1 = blurView.GetBlurredImage( 1 ); + DALI_TEST_CHECK( image1 ); + + Image image2 = blurView.GetBlurredImage( 2 ); + DALI_TEST_CHECK( image2.GetWidth() == 25 ); + DALI_TEST_CHECK( image2.GetHeight() == 25 ); + + Image image3 = blurView.GetBlurredImage( 3 ); + DALI_TEST_CHECK( FrameBufferImage::DownCast( image2 ) ); + +} diff --git a/automated-tests/dali-test-suite/table-view/.gitignore b/automated-tests/dali-test-suite/table-view/.gitignore new file mode 100644 index 0000000..98a4fb6 --- /dev/null +++ b/automated-tests/dali-test-suite/table-view/.gitignore @@ -0,0 +1 @@ +utc-Dali-TableView diff --git a/automated-tests/dali-test-suite/table-view/Makefile b/automated-tests/dali-test-suite/table-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/table-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/table-view/file.list b/automated-tests/dali-test-suite/table-view/file.list new file mode 100644 index 0000000..8ad8d0f --- /dev/null +++ b/automated-tests/dali-test-suite/table-view/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-TableView \ diff --git a/automated-tests/dali-test-suite/table-view/tslist b/automated-tests/dali-test-suite/table-view/tslist new file mode 100644 index 0000000..9543070 --- /dev/null +++ b/automated-tests/dali-test-suite/table-view/tslist @@ -0,0 +1 @@ +/dali-test-suite/table-view/utc-Dali-TableView diff --git a/automated-tests/dali-test-suite/table-view/utc-Dali-TableView.cpp b/automated-tests/dali-test-suite/table-view/utc-Dali-TableView.cpp new file mode 100644 index 0000000..21cad20 --- /dev/null +++ b/automated-tests/dali-test-suite/table-view/utc-Dali-TableView.cpp @@ -0,0 +1,575 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void UtcDaliTableViewNew(); +static void UtcDaliTableViewMetricsPadding(); +static void UtcDaliTableViewMetricsFixed(); +static void UtcDaliTableViewMetricsRelative(); +static void UtcDaliTableViewAnimation(); +static void UtcDaliTableViewChild(); +static void UtcDaliTableViewAdd(); +static void UtcDaliTableViewCells(); +static void UtcDaliTableViewChildAssert(); +static void UtcDaliTableViewMetricsAssert(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { UtcDaliTableViewNew, POSITIVE_TC_IDX }, + { UtcDaliTableViewMetricsPadding, POSITIVE_TC_IDX }, + { UtcDaliTableViewMetricsFixed, POSITIVE_TC_IDX }, + { UtcDaliTableViewMetricsRelative, POSITIVE_TC_IDX }, + { UtcDaliTableViewAnimation, POSITIVE_TC_IDX }, + { UtcDaliTableViewChild, POSITIVE_TC_IDX }, + { UtcDaliTableViewAdd, POSITIVE_TC_IDX }, + { UtcDaliTableViewCells, POSITIVE_TC_IDX }, + { UtcDaliTableViewChildAssert, POSITIVE_TC_IDX }, + { UtcDaliTableViewMetricsAssert, POSITIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +struct Constraint100 +{ + Constraint100( ) + { + } + + /** + * function operator to apply the parent size + */ + Dali::Vector3 operator()(const Dali::Vector3& current) + { + return Dali::Vector3( 100.0f, 100.0f, 100.0f ); + } +}; + +// Convenience function to quickly set up a 10x10 table with each cell being 10x10 pixels in size by default. +static void SetupTableViewAndActors(TableView& tableView, Actor& actor1, Actor& actor2, Actor& actor3) +{ + tableView = TableView::New(10,10); // 10 by 10 grid. + DALI_TEST_CHECK(tableView); + + Stage::GetCurrent().Add( tableView ); + tableView.ApplyConstraint( Constraint::New( Actor::SIZE, Constraint100() ) ); + tableView.SetLayoutAnimationDuration(0.0f); + + actor1 = Actor::New(); + actor2 = Actor::New(); + actor3 = Actor::New(); + + actor1.SetSize(10,10); + actor2.SetSize(10,10); + actor3.SetSize(10,10); + + tableView.AddChild(actor1, TableView::CellPosition(0,0)); + tableView.AddChild(actor2, TableView::CellPosition(0,1)); + tableView.AddChild(actor3, TableView::CellPosition(1,0)); +} + +static void UtcDaliTableViewNew() +{ + ToolkitTestApplication application; + + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK(tableView); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + TableView tableView = TableView::New(10,10); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +// Test adjusting the metric values for the cell. +static void UtcDaliTableViewMetricsPadding() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewMetricsPadding"); + + TableView tableView; + Actor actor1; + Actor actor2; + Actor actor3; + + SetupTableViewAndActors(tableView, actor1, actor2, actor3); + + // 1. check that padding works. no padding: + tableView.SetCellPadding(Size(0.0f, 0.0f)); + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( tableView.GetCellPadding(), Size(0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor2.GetCurrentPosition(), Vector3(10.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor3.GetCurrentPosition(), Vector3(0.0f, 10.0f, 0.0f), TEST_LOCATION ); + + // 1. check that padding works. some padding: + tableView.SetCellPadding(Size(5.0f, 10.0f)); + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( tableView.GetCellPadding(), Size(5.0f, 10.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(5.0f, 10.0f, 0.0f), TEST_LOCATION ); +} + +// Test adjusting the metric values for the cell. +static void UtcDaliTableViewMetricsFixed() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewMetricsFixed"); + + TableView tableView; + Actor actor1; + Actor actor2; + Actor actor3; + + SetupTableViewAndActors(tableView, actor1, actor2, actor3); + application.SendNotification(); + application.Render(); + + // 1. check that with no fixed width/heights, actors are in default position. + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor2.GetCurrentPosition(), Vector3(10.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor3.GetCurrentPosition(), Vector3(0.0f, 10.0f, 0.0f), TEST_LOCATION ); + + // 2. check that with a fixed width & height, actors to the right and below are offsetted. + tableView.SetFixedWidth(0, 20.0f); + tableView.SetFixedHeight(0, 50.0f); + DALI_TEST_EQUALS( tableView.GetFixedWidth(0), 20.0f, TEST_LOCATION ); + DALI_TEST_EQUALS( tableView.GetFixedHeight(0), 50.0f, TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor2.GetCurrentPosition(), Vector3(20.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor3.GetCurrentPosition(), Vector3(0.0f, 50.0f, 0.0f), TEST_LOCATION ); +} + +// Test adjusting the metric values for the cell. +static void UtcDaliTableViewMetricsRelative() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewMetricsRelative"); + + TableView tableView; + Actor actor1; + Actor actor2; + Actor actor3; + + SetupTableViewAndActors(tableView, actor1, actor2, actor3); + application.SendNotification(); + application.Render(); + + // 1. check that with no relative width/heights, actors are in default position. + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor2.GetCurrentPosition(), Vector3(10.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor3.GetCurrentPosition(), Vector3(0.0f, 10.0f, 0.0f), TEST_LOCATION ); + + // 2. check that with a relative width & height, actors to the right and below are offsetted. + tableView.SetRelativeWidth(0, 0.3f); // cell 0,0 occupies 30%x50% of the grid (i.e. 30x50 pixels) + tableView.SetRelativeHeight(0, 0.5f); + DALI_TEST_EQUALS( tableView.GetRelativeWidth(0), 0.3f, TEST_LOCATION ); + DALI_TEST_EQUALS( tableView.GetRelativeHeight(0), 0.5f, TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( actor1.GetCurrentPosition(), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor2.GetCurrentPosition(), Vector3(30.0f, 0.0f, 0.0f), TEST_LOCATION ); + DALI_TEST_EQUALS( actor3.GetCurrentPosition(), Vector3(0.0f, 50.0f, 0.0f), TEST_LOCATION ); +} + + +// Test animation duration setting. +static void UtcDaliTableViewAnimation() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewAnimation"); + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK(tableView); + + tableView.SetLayoutAnimationDuration(5.0f); + DALI_TEST_EQUALS(tableView.GetLayoutAnimationDuration(), 5.0f, TEST_LOCATION); + + tableView.SetLayoutAnimationDuration(2.5f); + DALI_TEST_EQUALS(tableView.GetLayoutAnimationDuration(), 2.5f, TEST_LOCATION); +} + +// Test Adding/Removing/Finding Children. +static void UtcDaliTableViewChild() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewChild"); + + // Create a 10x10 table-view + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK( tableView ); + + // Check if actor doesn't exist. + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(0,0)) ); + + // Add an actor to it at 0,0 + Actor actor = Actor::New(); + tableView.AddChild(actor, TableView::CellPosition()); + + // Check if exists. + DALI_TEST_CHECK( tableView.GetChildAt(TableView::CellPosition(0,0)) ); + + // Remove this actor + tableView.RemoveChildAt(TableView::CellPosition()); + + // Check if actor no longer exists. + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(0,0)) ); + + // Add actor to it again, but at 2,5 + tableView.AddChild(actor, TableView::CellPosition(2,5)); + + // Add another actor somewhere else 7,8 + Actor actor2 = Actor::New(); + tableView.AddChild(actor2, TableView::CellPosition(7,8)); + + Actor searchActor; + + // Check that no actor exists in a few random places. + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(0,0)) ); + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(2,1)) ); + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(6,3)) ); + DALI_TEST_CHECK( !tableView.GetChildAt(TableView::CellPosition(9,5)) ); + + // Check for actors at actual positions. + searchActor = tableView.GetChildAt(TableView::CellPosition(2,5)); + DALI_TEST_CHECK( searchActor == actor); + + searchActor = tableView.GetChildAt(TableView::CellPosition(7,8)); + DALI_TEST_CHECK( searchActor == actor2); + + // Create a second table, and add already added Child to new one. + TableView tableView2 = TableView::New(5,5); + tableView2.AddChild(actor, TableView::CellPosition(2,2)); + DALI_TEST_CHECK( tableView2.GetChildAt(TableView::CellPosition(2,2)) ); +} + +// Test calling Add on it's own (to invoke the OnChildAdd) +static void UtcDaliTableViewAdd() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTableViewAdd"); + + // Create a 4x1 table-view, and just keep adding. + TableView tableView = TableView::New(1,4); + DALI_TEST_CHECK( tableView ); + + for(unsigned int i = 0;i<16;i++) + { + Actor currentActor = Actor::New(); + TableView::CellPosition position = TableView::CellPosition(); + tableView.Add( currentActor ); + tableView.FindChildPosition(currentActor, position); + tet_printf("%dx%d (%d,%d)\n", tableView.GetColumns(), tableView.GetRows(), position.columnIndex, position.rowIndex); + + DALI_TEST_EQUALS((position.rowIndex * 4 + position.columnIndex), i, TEST_LOCATION); + } +} + +// Test cell modification. +static void UtcDaliTableViewCells() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTableViewCells"); + + // Create a 10x10 table-view + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK( tableView ); + + // Add a few actors to the table. + Actor actor1 = Actor::New(); + Actor actor2 = Actor::New(); + Actor actor3 = Actor::New(); + actor1.SetName("Actor1"); + actor2.SetName("Actor2"); + actor3.SetName("Actor3"); + + // note: positions are specified in reversed cartesian coords - row,col (i.e. y,x) + tableView.AddChild(actor1, TableView::CellPosition(0,0)); + tableView.AddChild(actor2, TableView::CellPosition(5,5)); + tableView.AddChild(actor3, TableView::CellPosition(7,2)); + + DALI_TEST_CHECK( tableView.GetRows() == 10 && tableView.GetColumns() == 10 ); + + // Add a row between actor1 and actor2 | insert column on actor1 and see what happens... + tableView.InsertRow(3); + tableView.InsertColumn(0); + DALI_TEST_CHECK( tableView.GetRows() == 11 && tableView.GetColumns() == 11 ); + + TableView::CellPosition cellPosition; + bool result; + + result = tableView.FindChildPosition(actor1, cellPosition); + DALI_TEST_CHECK( result && cellPosition.rowIndex == 0 && cellPosition.columnIndex == 1); + result = tableView.FindChildPosition(actor2, cellPosition); + DALI_TEST_CHECK( result && cellPosition.rowIndex == 6 && cellPosition.columnIndex == 6); + result = tableView.FindChildPosition(actor3, cellPosition); + DALI_TEST_CHECK( result && cellPosition.rowIndex == 8 && cellPosition.columnIndex == 3); + + // Delete a row between actor2 and actor3 | delete column on actor2 and see what happens... + tableView.DeleteRow(7); + tableView.DeleteColumn(6); + DALI_TEST_CHECK( tableView.GetRows() == 10 && tableView.GetColumns() == 10 ); + + result = tableView.FindChildPosition(actor1, cellPosition); + DALI_TEST_CHECK( result && cellPosition.rowIndex == 0 && cellPosition.columnIndex == 1); + result = tableView.FindChildPosition(actor2, cellPosition); + DALI_TEST_CHECK( !result ); + result = tableView.FindChildPosition(actor3, cellPosition); + DALI_TEST_CHECK( result && cellPosition.rowIndex == 7 && cellPosition.columnIndex == 3); + + // Delete the other two remaining actors by a row delete and a column delete. + std::vector actorsRemoved; + tableView.DeleteRow(0, actorsRemoved); + tet_printf("Row Delete >> Actors Removed: %d {", actorsRemoved.size()); + for(size_t i = 0;i %s, ", i, actorsRemoved[i].GetName().c_str()); + tet_printf("}\n"); + DALI_TEST_EQUALS( static_cast(actorsRemoved.size()), 1, TEST_LOCATION ); + DALI_TEST_CHECK( actorsRemoved[0] == actor1 ); + + actorsRemoved.clear(); + tableView.DeleteColumn(3, actorsRemoved); + tet_printf("Column Delete >> Actors Removed: %d {", actorsRemoved.size()); + for(size_t i = 0;i %s, ", i, actorsRemoved[i].GetName().c_str()); + tet_printf("}\n"); + DALI_TEST_EQUALS( static_cast(actorsRemoved.size()), 1, TEST_LOCATION ); + DALI_TEST_CHECK( actorsRemoved[0] == actor3 ); + + DALI_TEST_CHECK( tableView.GetRows() == 9 && tableView.GetColumns() == 9 ); + + tableView.AddChild(actor1, TableView::CellPosition(5,8)); + tableView.Resize(100,100); + DALI_TEST_CHECK( tableView.GetRows() == 100 && tableView.GetColumns() == 100 ); + + tableView.AddChild(actor2, TableView::CellPosition(69,57)); + DALI_TEST_CHECK( tableView.FindChildPosition(actor1, cellPosition) && tableView.FindChildPosition(actor2, cellPosition) ); + + tableView.Resize(20,20); + DALI_TEST_CHECK( tableView.FindChildPosition(actor1, cellPosition) && !tableView.FindChildPosition(actor2, cellPosition) ); + + actorsRemoved.clear(); + tableView.Resize(1,1, actorsRemoved); + DALI_TEST_CHECK( !tableView.FindChildPosition(actor1, cellPosition) && !tableView.FindChildPosition(actor2, cellPosition) ); + DALI_TEST_EQUALS( static_cast(actorsRemoved.size()), 1, TEST_LOCATION ); + DALI_TEST_CHECK( actorsRemoved[0] == actor1 ); + + // Add child outside table size, forcing a resize. + tableView.AddChild(actor1, TableView::CellPosition(100, 100, 1, 1)); + DALI_TEST_CHECK( tableView.GetRows() == 101 && tableView.GetColumns() == 101 ); + + // Add child outside table size, forcing a resize. + tableView.AddChild(actor1, TableView::CellPosition(110, 110, 5, 5)); + DALI_TEST_CHECK( tableView.GetRows() == 115 && tableView.GetColumns() == 115 ); + + DALI_TEST_CHECK( true ); +} + +static void UtcDaliTableViewChildAssert() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTableViewChildAssert"); + + // Create a 10x10 table-view + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK( tableView ); + Actor childActor; + + try + { + tableView.AddChild( childActor, TableView::CellPosition(0,0,5,5) ); + // should assert + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e ) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "child", TEST_LOCATION); + } +} + +static void UtcDaliTableViewMetricsAssert() +{ + ToolkitTestApplication application; + tet_infoline("UtcDaliTableViewChildAssert"); + + // Create a 10x10 table-view + TableView tableView = TableView::New(10,10); + DALI_TEST_CHECK( tableView ); + + // fixeds... + + try + { + tableView.SetFixedHeight( 10, 1.0f ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("1. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "rowIndex < mFixedHeights.size()", TEST_LOCATION); + } + + try + { + tableView.GetFixedHeight( 10 ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("2. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "rowIndex < mFixedHeights.size()", TEST_LOCATION); + } + + try + { + tableView.SetFixedWidth( 10, 1.0f ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("3. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "columnIndex < mFixedWidths.size()", TEST_LOCATION); + } + + try + { + tableView.GetFixedWidth( 10 ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("4. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "columnIndex < mFixedWidths.size()", TEST_LOCATION); + } + + // relatives... + + try + { + tableView.SetRelativeHeight( 10, 0.1f ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("5. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "rowIndex < mRelativeHeights.size()", TEST_LOCATION); + } + + try + { + tableView.GetRelativeHeight( 10 ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("6. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "rowIndex < mRelativeHeights.size()", TEST_LOCATION); + } + + try + { + tableView.SetRelativeWidth( 10, 0.1f ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("7. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "columnIndex < mRelativeWidths.size()", TEST_LOCATION); + } + + try + { + tableView.GetRelativeWidth( 10 ); + + tet_result(TET_FAIL); + } + catch( Dali::DaliException &e) + { + tet_printf("8. Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "columnIndex < mRelativeWidths.size()", TEST_LOCATION); + } +} diff --git a/automated-tests/dali-test-suite/tc-gen.sh b/automated-tests/dali-test-suite/tc-gen.sh new file mode 100755 index 0000000..178f24d --- /dev/null +++ b/automated-tests/dali-test-suite/tc-gen.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +TMPSTR=$0 +SCRIPT=${TMPSTR##*/} + +if [ $# -ne 1 ]; then + echo "Usage) $SCRIPT ClassName" + exit 1 +fi + +MODULE="Dali" +SECTION=${PWD##*/} +CLASS=$1 + +TESTSUITEPATH=`dirname $PWD` +TESTSUITENAME=${TESTSUITEPATH##*/} + +TEMPLATE=../utc-MODULE-CLASS.cpp.in +TESTCASE=utc-${MODULE}-${CLASS} + +# Create .cpp file +if [ ! -e "$TESTCASE.cpp" ]; then + sed -e ' + s^@CLASS@^'"$CLASS"'^g + s^@MODULE@^'"$MODULE"'^g + ' $TEMPLATE > $TESTCASE.cpp +fi + +if [ ! -e "$TESTCASE.cpp" ]; then + echo "Failed" + exit 1 +fi + + +# file.list +if ! [ -f file.list ]; then + touch file.list + echo "TARGETS += \\" >> file.list +fi +echo " $TESTCASE \\" >> file.list + +# tslist +if ! [ -f tslist ]; then + touch tslist +fi +echo "/$TESTSUITENAME/$SECTION/$TESTCASE" >> tslist + +# Makefile +if ! [ -f Makefile ]; then + ln -s ../master-makefile.mk Makefile +fi + +echo "$TESTCASE" >> .gitignore + +echo "Testcase file is $TESTCASE.cpp" +echo "$TESTCASE is added to tslist" +echo "$TESTCASE is added to file.list" +echo "Done" diff --git a/automated-tests/dali-test-suite/text-input/.gitignore b/automated-tests/dali-test-suite/text-input/.gitignore new file mode 100644 index 0000000..0f77dca --- /dev/null +++ b/automated-tests/dali-test-suite/text-input/.gitignore @@ -0,0 +1 @@ +utc-Dali-TextInput diff --git a/automated-tests/dali-test-suite/text-input/Makefile b/automated-tests/dali-test-suite/text-input/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/text-input/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/text-input/file.list b/automated-tests/dali-test-suite/text-input/file.list new file mode 100644 index 0000000..3f0dba0 --- /dev/null +++ b/automated-tests/dali-test-suite/text-input/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-TextInput \ diff --git a/automated-tests/dali-test-suite/text-input/tslist b/automated-tests/dali-test-suite/text-input/tslist new file mode 100644 index 0000000..eace898 --- /dev/null +++ b/automated-tests/dali-test-suite/text-input/tslist @@ -0,0 +1 @@ +/dali-test-suite/text-input/utc-Dali-TextInput diff --git a/automated-tests/dali-test-suite/text-input/utc-Dali-TextInput.cpp b/automated-tests/dali-test-suite/text-input/utc-Dali-TextInput.cpp new file mode 100644 index 0000000..e8f0b51 --- /dev/null +++ b/automated-tests/dali-test-suite/text-input/utc-Dali-TextInput.cpp @@ -0,0 +1,531 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include + +#include + +#include + + +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + Actor actor = Actor::DownCast(handle); + + if(actor) + { + TextInput handle = TextInput::DownCast(actor); + if (handle) + { + gObjectCreatedCallBackCalled = true; + } + } +} + +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} +namespace +{ +static bool gHasEndSignalBeenReceived; +static bool gHasStartSignalBeenReceived; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextInputConstruction, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputDownCast, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputGetText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetMaxCharacterLength, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetInitialText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputAddChars, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputRemoveChars, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputEndSignalEmit, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputStartSignalEmit, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputExceedMaxCharactersInitial, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputExceedMaxCharacters, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetNumberOfLines, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetAndGetFadeBoundary, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetAndGetWidthExceedPolicy, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputSetAndGetHeightExceedPolicy, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextInputScroll, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void UtcDaliTextInputConstruction() +{ + ToolkitTestApplication application; + + tet_infoline("Testing New constructor"); + + TextInput textInput = TextInput::New(); + DALI_TEST_CHECK(textInput); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + TextInput textInput = TextInput::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + + +static bool downCastToTextInput(Dali::Actor actor) +{ + TextInput handle = TextInput::DownCast(actor); + if (handle) + { + tet_infoline("Downcasted to TextInput"); + return true; + } + else + { + tet_infoline("Did not downcast to TextInput"); + return false; + } +} + +// Positive test case for a method +static void UtcDaliTextInputDownCast() +{ + ToolkitTestApplication application; + + TextInput textInput = TextInput::New(); + + tet_infoline("Testing Downcasting with a TextInput"); + DALI_TEST_EQUALS(true,downCastToTextInput(textInput), TEST_LOCATION); // downcast a TextInput + + Dali::TextActor badHandle = Dali::TextActor::New("test"); + + tet_infoline("Testing Downcasting with the wrong actor"); + DALI_TEST_EQUALS(false, downCastToTextInput(badHandle), TEST_LOCATION); // downcast a TextActor to TextInput +} + +// Positive test case for a method +static void UtcDaliTextInputGetText() +{ + ToolkitTestApplication application; + + tet_infoline("Testing GetText"); + + const std::string teststring = "test"; + + TextInput textInput = TextInput::New(); // create empty TextInput + + DALI_TEST_EQUALS("",textInput.GetText(), TEST_LOCATION); // Get text which should be empty + + textInput.SetInitialText(teststring); + + DALI_TEST_EQUALS(teststring,textInput.GetText(), TEST_LOCATION); // Get text which should be test string + +} + +// Positive test case for a method +static void UtcDaliTextInputSetMaxCharacterLength() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Setting of max characters"); + + const int maxChars = 4; + const char* testChar = "v"; + + Dali::Integration::Core& core = application.GetCore(); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.SetKeyInputFocus(); + textInput.SetMaxCharacterLength(maxChars); + + Integration::KeyEvent event(testChar, testChar, 0, 0, 0, Integration::KeyEvent::Down ); + + std::string testString = ""; + // Send max number of characters + for (int i=0; i < maxChars; i++) + { + core.SendEvent(event); + testString.append(testChar); + } + + DALI_TEST_EQUALS(testString,textInput.GetText(), TEST_LOCATION); // Get text which should be empty + + core.SendEvent(event); // try to append additional character + + DALI_TEST_EQUALS(testString,textInput.GetText(), TEST_LOCATION); // Get text which should be empty + + textInput.SetMaxCharacterLength(maxChars+1); // increment max characters by 1 + + core.SendEvent(event); // append additional character + testString.append(testChar); + + DALI_TEST_EQUALS(testString,textInput.GetText(), TEST_LOCATION); // Get text which should be empty +} + +// Positive test case for a method +static void UtcDaliTextInputSetInitialText() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Setting of Initial Text"); + + const std::string teststring = "test"; + + TextInput textInput = TextInput::New(); // create empty TextInput + + textInput.SetInitialText(teststring); + + DALI_TEST_EQUALS(teststring,textInput.GetText(), TEST_LOCATION); // Get text which should be empty +} + +static void UtcDaliTextInputAddChars() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Adding characters"); + + Dali::Integration::Core& core = application.GetCore(); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.SetKeyInputFocus(); + + Integration::KeyEvent event("a", "a", 0, 0, 0, Integration::KeyEvent::Down); + core.SendEvent(event); + + DALI_TEST_EQUALS("a",textInput.GetText(), TEST_LOCATION); // Get text which should be "a" + + Integration::KeyEvent event2("v", "v", 0, 0, 0, Integration::KeyEvent::Down); + core.SendEvent(event2); + + DALI_TEST_EQUALS("av",textInput.GetText(), TEST_LOCATION); // Get text which should be "av" +} + +static void UtcDaliTextInputRemoveChars() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Removal of end characters"); + + Dali::Integration::Core& core = application.GetCore(); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.SetKeyInputFocus(); + + Integration::KeyEvent event("a", "a", 0, 0, 0, Integration::KeyEvent::Down); + core.SendEvent(event); + + DALI_TEST_EQUALS("a",textInput.GetText(), TEST_LOCATION); // Get text which should be "a" + + Integration::KeyEvent event2("BackSpace", "", 0, 0, 0, Integration::KeyEvent::Down); + core.SendEvent(event2); + + DALI_TEST_EQUALS("",textInput.GetText(), TEST_LOCATION); // Get text which should be "" + + core.SendEvent(event); + core.SendEvent(event); + + DALI_TEST_EQUALS("aa",textInput.GetText(), TEST_LOCATION); // Get text which should be "aa" + + core.SendEvent(event2); + + DALI_TEST_EQUALS("a",textInput.GetText(), TEST_LOCATION); // Get text which should be "a" +} + +// Callback test function +void OnEndInput(TextInput textInput) +{ + gHasEndSignalBeenReceived = true; +} + +static void UtcDaliTextInputEndSignalEmit() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Set editable false emits end signal"); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.InputFinishedSignal().Connect( &OnEndInput ); + + textInput.SetEditable(true) ; + + gHasEndSignalBeenReceived = false; + + textInput.SetEditable(false) ; + + DALI_TEST_EQUALS(true, gHasEndSignalBeenReceived, TEST_LOCATION); +} + + +// Callback test function +void OnStartInput(TextInput textInput) +{ + gHasStartSignalBeenReceived = true; +} + +static void UtcDaliTextInputStartSignalEmit() +{ + ToolkitTestApplication application; + + tet_infoline("Testing SetEditable emits start signal"); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.InputStartedSignal().Connect( &OnStartInput ); + + gHasStartSignalBeenReceived = false; + + textInput.SetEditable(true); // Set editable first time + + DALI_TEST_EQUALS(true, gHasStartSignalBeenReceived, TEST_LOCATION); + + gHasStartSignalBeenReceived = false; + + textInput.SetEditable(true); // Set editable second time, signal should not be sent again. + + DALI_TEST_EQUALS(false, gHasStartSignalBeenReceived, TEST_LOCATION); + + textInput.SetEditable(false); + + gHasStartSignalBeenReceived = false; + + textInput.SetEditable(true,Vector2(3.f,2.f)); // Set editable again + + DALI_TEST_EQUALS(true, gHasStartSignalBeenReceived, TEST_LOCATION); +} + +static void UtcDaliTextInputExceedMaxCharactersInitial() +{ + ToolkitTestApplication application; + + tet_infoline("Testing Setting Initial Text obeys Max Character Limit"); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.SetMaxCharacterLength(4); + + textInput.SetInitialText("TooBig"); + + tet_printf( "Get text result : %s\n", textInput.GetText().c_str()); + + DALI_TEST_EQUALS("TooB",textInput.GetText(), TEST_LOCATION); // Get text which should be only 4 characters +} + + +static void UtcDaliTextInputExceedMaxCharacters() +{ + ToolkitTestApplication application; + + Dali::Integration::Core& core = application.GetCore(); + + tet_infoline("Testing Max characters is obeyed when inputting key events "); + + TextInput textInput = TextInput::New(); // create empty TextInput + + Stage::GetCurrent().Add(textInput); + + textInput.SetMaxCharacterLength(4); + + textInput.SetInitialText(""); + + textInput.SetEditable(true); + + Integration::KeyEvent eventA("a", "a", 0, 0, 0, Integration::KeyEvent::Down ); + Integration::KeyEvent eventB("b", "b", 0, 0, 0, Integration::KeyEvent::Down ); + + core.SendEvent(eventA); + core.SendEvent(eventB); + core.SendEvent(eventA); + core.SendEvent(eventB); + + core.SendEvent(eventA); + core.SendEvent(eventB); + + tet_printf( "Get text result : %s\n", textInput.GetText().c_str()); + + DALI_TEST_EQUALS("abab",textInput.GetText(), TEST_LOCATION); // Get text which should be only 4 characters +} + +static void UtcDaliTextInputSetNumberOfLines() +{ + ToolkitTestApplication application; + + tet_infoline("Ensuring API for setting and getting max number of lines is correct"); + + TextInput textInput = TextInput::New(); // create empty TextInput + + unsigned int numberOfLines = 1; + + textInput.SetNumberOfLinesLimit( numberOfLines ); + + DALI_TEST_EQUALS(numberOfLines ,textInput.GetNumberOfLinesLimit(), TEST_LOCATION); +} + +static void UtcDaliTextInputSetAndGetFadeBoundary() +{ + tet_infoline("UtcDaliTextViewSetAndGetFadeBoundary: "); + + ToolkitTestApplication application; + + TextView::FadeBoundary fadeBoundary( PixelSize( 0 ), PixelSize( 20 ), PixelSize( 0 ), PixelSize( 10 ) ); + + TextInput textInput = TextInput::New(); + textInput.SetInitialText( "Hello world!" ); + + textInput.SetFadeBoundary( fadeBoundary ); + + TextView::FadeBoundary fadeBoundary2 = textInput.GetFadeBoundary(); + + DALI_TEST_EQUALS( fadeBoundary.mLeft, fadeBoundary2.mLeft, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mRight, fadeBoundary2.mRight, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mTop, fadeBoundary2.mTop, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mBottom, fadeBoundary2.mBottom, TEST_LOCATION ); +} + +static void UtcDaliTextInputSetAndGetWidthExceedPolicy() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextInputSetAndGetWidthExceedPolicy: "); + + const TextView::ExceedPolicy EXCEED_POLICIES[] = { TextView::Original, TextView::Fade, TextView::Split, TextView::ShrinkToFit }; + const unsigned int NUM_EXCEED_POLICIES = sizeof( EXCEED_POLICIES ) / sizeof( unsigned int ); + + TextInput textInput = TextInput::New(); + textInput.SetInitialText( "Hello world!" ); + + for( unsigned int epIndex = 0; epIndex < NUM_EXCEED_POLICIES; ++epIndex ) + { + textInput.SetWidthExceedPolicy( EXCEED_POLICIES[epIndex] ); + + DALI_TEST_EQUALS( textInput.GetWidthExceedPolicy(), EXCEED_POLICIES[epIndex], TEST_LOCATION ); + } +} + +static void UtcDaliTextInputSetAndGetHeightExceedPolicy() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliTextInputSetAndGetHeightExceedPolicy: "); + + const TextView::ExceedPolicy EXCEED_POLICIES[] = { TextView::Original, TextView::Fade, TextView::ShrinkToFit }; + const unsigned int NUM_EXCEED_POLICIES = sizeof( EXCEED_POLICIES ) / sizeof( unsigned int ); + + TextInput textInput = TextInput::New(); + textInput.SetInitialText( "Hello world!" ); + + for( unsigned int epIndex = 0; epIndex < NUM_EXCEED_POLICIES; ++epIndex ) + { + textInput.SetHeightExceedPolicy( EXCEED_POLICIES[epIndex] ); + + DALI_TEST_EQUALS( textInput.GetHeightExceedPolicy(), EXCEED_POLICIES[epIndex], TEST_LOCATION ); + } +} + +static void UtcDaliTextInputScroll() +{ + tet_infoline("UtcDaliTextInputScroll: "); + ToolkitTestApplication application; + + // Avoids the frame buffer texture to throw an exception. + application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE ); + + TextInput view = TextInput::New(); + view.SetMultilinePolicy( TextView::SplitByNewLineChar ); + view.SetWidthExceedPolicy( TextView::Original ); + view.SetHeightExceedPolicy( TextView::Original ); + view.SetTextAlignment( static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ) ); + view.SetInitialText( "Hello world! This is a scroll test." ); + view.SetSize( 100.f, 100.f ); + view.SetSnapshotModeEnabled( false ); + + Stage::GetCurrent().Add( view ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( !view.IsScrollEnabled() ); // Scroll should be disabled by default. + + view.SetScrollEnabled( true ); + + DALI_TEST_CHECK( view.IsScrollEnabled() ); + DALI_TEST_CHECK( view.IsSnapshotModeEnabled() ); // Scroll should enable snapshot mode. + + view.SetScrollPosition( Vector2( 400.f, 400.f ) ); + + application.SendNotification(); + application.Render(); + + const Vector2& scrollPosition = view.GetScrollPosition(); + DALI_TEST_EQUALS( scrollPosition, Vector2( 149.153656f, 0.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} diff --git a/automated-tests/dali-test-suite/text-view/.gitignore b/automated-tests/dali-test-suite/text-view/.gitignore new file mode 100644 index 0000000..bd332d7 --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/.gitignore @@ -0,0 +1,2 @@ +utc-Dali-TextView +utc-Dali-MarkupProcessor diff --git a/automated-tests/dali-test-suite/text-view/Makefile b/automated-tests/dali-test-suite/text-view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/text-view/file.list b/automated-tests/dali-test-suite/text-view/file.list new file mode 100644 index 0000000..006e1f5 --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/file.list @@ -0,0 +1,3 @@ +TARGETS += \ + utc-Dali-TextView \ + utc-Dali-MarkupProcessor \ diff --git a/automated-tests/dali-test-suite/text-view/tslist b/automated-tests/dali-test-suite/text-view/tslist new file mode 100644 index 0000000..b37b02d --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/tslist @@ -0,0 +1,2 @@ +/dali-test-suite/text-view/utc-Dali-TextView +/dali-test-suite/text-view/utc-Dali-MarkupProcessor diff --git a/automated-tests/dali-test-suite/text-view/utc-Dali-MarkupProcessor.cpp b/automated-tests/dali-test-suite/text-view/utc-Dali-MarkupProcessor.cpp new file mode 100644 index 0000000..7e4e7e9 --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/utc-Dali-MarkupProcessor.cpp @@ -0,0 +1,418 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; + +namespace +{ + +struct MarkupStringTest +{ + std::string input; + std::string expectedResult; +}; + +bool TestMarkupString( const std::string& input, const std::string& expectedResult, std::string& result ) +{ + Toolkit::MarkupProcessor::StyledTextArray styledTextArray; + + GetStyledTextArray( input, styledTextArray ); + GetMarkupString( styledTextArray, result ); + + return expectedResult == result; +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliMarkupProcessor, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliMarkupProcessorSetTextStyle01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliMarkupProcessorSetTextStyle02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliMarkupProcessorTestColors, POSITIVE_TC_IDX ); + + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void UtcDaliMarkupProcessor() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliMarkupProcessor "); + + const std::string text1( "Text" ); + const std::string text2( "< font face ='FreeSerif' color= 'green' >t< / font >" ); + const std::string text3( "< font face = 'FreeSerif' size= '16' style = 'Bold' color='red'>< i>Styled< / u> Text< /i >< / font >< br / >" ); + const std::string text4( "Styled Text< br/>" ); + const std::string text5( "< shadow color = 'blue' paramx = '1' paramy = '0.75' >Shadow< / shadow>
" ); + const std::string text6( "< glow color = 'red' param = '0.1' >Glow< br />" ); + const std::string text7( "< outline color = 'red' paramx = '0.7' paramy = '0.7' >Outline< / outline >< /font >< br / >" ); + const std::string text8( "Smooth< br / >" ); + const std::string text9( "\\<" ); + const std::string text10( "\\>" ); + + char crlf[2]; + crlf[0] = 0x0D; + crlf[1] = 0x0A; + const std::string text11( crlf, 2 ); + + const std::string result1( text1 ); + const std::string result2( "t" ); + const std::string result3( "Styled Text
" ); + const std::string result4( "Styled Text
" ); + const std::string result5( "Shadow
" ); + const std::string result6( "Glow
" ); + const std::string result7( "Outline
" ); + const std::string result8( "Smooth
" ); + const std::string result9( text9 ); + const std::string result10( text10 ); + const std::string result11( "
" ); + + std::string markupString; + Toolkit::MarkupProcessor::StyledTextArray styledTextArray; + + GetStyledTextArray( text1, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result1, markupString, TEST_LOCATION ); + + GetStyledTextArray( text2, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result2, markupString, TEST_LOCATION ); + + GetStyledTextArray( text3, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result3, markupString, TEST_LOCATION ); + + GetStyledTextArray( text4, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result4, markupString, TEST_LOCATION ); + + GetStyledTextArray( text5, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result5, markupString, TEST_LOCATION ); + + GetStyledTextArray( text6, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result6, markupString, TEST_LOCATION ); + + GetStyledTextArray( text7, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result7, markupString, TEST_LOCATION ); + + GetStyledTextArray( text8, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result8, markupString, TEST_LOCATION ); + + GetStyledTextArray( text9, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + DALI_TEST_EQUALS( result9, markupString, TEST_LOCATION ); + + GetStyledTextArray( text10, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result10, markupString, TEST_LOCATION ); + + GetStyledTextArray( text11, styledTextArray ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result11, markupString, TEST_LOCATION ); +} + +static void UtcDaliMarkupProcessorSetTextStyle01() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliMarkupProcessorSetTextStyle01 "); + + const std::string text1( "Text with no defined style" ); + const std::string result1( "Text with no defined style" ); + const std::string result2( "Text with no defined style" ); + + std::string markupString; + Toolkit::MarkupProcessor::StyledTextArray styledTextArray; + + GetStyledTextArray( text1, styledTextArray ); + + TextStyle style; + style.SetItalics( true ); + style.SetTextColor( Color::GREEN ); + + SetTextStyle( styledTextArray, style ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result1, markupString, TEST_LOCATION ); + + styledTextArray.clear(); + SetTextStyle( text1, styledTextArray, style ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result1, markupString, TEST_LOCATION ); + + GetStyledTextArray( text1, styledTextArray ); + SetTextStyleToRange( styledTextArray, style, TextStyle::ALL, 0, text1.size() - 1 ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result1, markupString, TEST_LOCATION ); + + GetStyledTextArray( text1, styledTextArray ); + SetTextStyleToRange( styledTextArray, style, TextStyle::ALL, 10, 19 ); + GetMarkupString( styledTextArray, markupString ); + + DALI_TEST_EQUALS( result2, markupString, TEST_LOCATION ); + + std::string plainString; + GetPlainString( styledTextArray, plainString ); + + DALI_TEST_EQUALS( text1, plainString, TEST_LOCATION ); +} + +static void UtcDaliMarkupProcessorSetTextStyle02() +{ + ToolkitTestApplication application; + + tet_infoline(" UtcDaliMarkupProcessorSetTextStyle02 "); + + Toolkit::MarkupProcessor::StyledTextArray styledTextArray; + + // Test style applied to and empty string doesn't crash + + TextStyle style; + style.SetItalics( true ); + style.SetTextColor( Color::GREEN ); + + bool fails = false; + try + { + SetTextStyle( styledTextArray, style ); + } + catch( ... ) + { + fails = true; + } + + DALI_TEST_CHECK( !fails ); +} + +static void UtcDaliMarkupProcessorTestColors() +{ + ToolkitTestApplication application; + + tet_infoline("UtcDaliMarkupProcessorTestColors "); + + struct MarkupStringTest colorTests[] = + { + { + std::string( "black" ), + std::string( "black" ) + }, + { + std::string( "white" ), + std::string( "white" ) + }, + { + std::string( "red" ), + std::string( "red" ) + }, + { + std::string( "green" ), + std::string( "green" ) + }, + { + std::string( "blue" ), + std::string( "blue" ) + }, + { + std::string( "yellow" ), + std::string( "yellow" ) + }, + { + std::string( "magenta" ), + std::string( "magenta" ) + }, + { + std::string( "cyan" ), + std::string( "cyan" ) + }, + { + std::string( "transparent" ), + std::string( "transparent" ) + }, + { + std::string( "black" ), + std::string( "black" ) + }, + { + std::string( "white" ), + std::string( "white" ) + }, + { + std::string( "red" ), + std::string( "red" ) + }, + { + std::string( "green" ), + std::string( "green" ) + }, + { + std::string( "blue" ), + std::string( "blue" ) + }, + { + std::string( "yellow" ), + std::string( "yellow" ) + }, + { + std::string( "magenta" ), + std::string( "magenta" ) + }, + { + std::string( "cyan" ), + std::string( "cyan" ) + }, + { + std::string( "black" ), + std::string( "black" ) + }, + { + std::string( "white" ), + std::string( "white" ) + }, + { + std::string( "red" ), + std::string( "red" ) + }, + { + std::string( "green" ), + std::string( "green" ) + }, + { + std::string( "blue" ), + std::string( "blue" ) + }, + { + std::string( "yellow" ), + std::string( "yellow" ) + }, + { + std::string( "magenta" ), + std::string( "magenta" ) + }, + { + std::string( "cyan" ), + std::string( "cyan" ) + }, + { + std::string( "black" ), + std::string( "black" ) + }, + { + std::string( "black" ), + std::string( "black" ) + }, + { + std::string( "white" ), + std::string( "white" ) + }, + { + std::string( "red" ), + std::string( "red" ) + }, + { + std::string( "green" ), + std::string( "green" ) + }, + { + std::string( "blue" ), + std::string( "blue" ) + }, + { + std::string( "yellow" ), + std::string( "yellow" ) + }, + { + std::string( "magenta" ), + std::string( "magenta" ) + }, + { + std::string( "cyan" ), + std::string( "cyan" ) + }, + { + std::string( "transparent" ), + std::string( "transparent" ) + }, + { + std::string( "outline" ), + std::string( "outline" ) + }, + }; + + const std::size_t numberOfTests( 36 ); + + bool fails = false; + for( std::size_t index = 0; index < numberOfTests; ++index ) + { + const MarkupStringTest& test = colorTests[index]; + + std::string result; + if( !TestMarkupString( test.input, test.expectedResult, result ) ) + { + TestMarkupString( test.input, test.expectedResult, result ); + tet_printf( "%s\n input : %s\nexpected result : %s\n result : %s\n", TEST_LOCATION, test.input.c_str(), test.expectedResult.c_str(), result.c_str() ); + + fails = true; + } + } + + DALI_TEST_CHECK( !fails ); +} diff --git a/automated-tests/dali-test-suite/text-view/utc-Dali-TextView.cpp b/automated-tests/dali-test-suite/text-view/utc-Dali-TextView.cpp new file mode 100644 index 0000000..f0fd63d --- /dev/null +++ b/automated-tests/dali-test-suite/text-view/utc-Dali-TextView.cpp @@ -0,0 +1,839 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include + +#include + +#include +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ + +bool TestEqual( float x, float y ) +{ + return !( fabsf( x - y ) > Math::MACHINE_EPSILON_1000 ); +} + +static bool gObjectCreatedCallBackCalled; +static unsigned int gNumberObjectCreated; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; + ++gNumberObjectCreated; +} + +static bool gTextScrolled; +static Vector2 gScrollDelta; +static void TestTextScrolled( TextView textView, Vector2 scrollDelta ) +{ + gTextScrolled = true; + gScrollDelta = scrollDelta; +} + +} // namespace + +static void Startup(); +static void Cleanup(); + +extern "C" +{ +void (*tet_startup)() = Startup; +void (*tet_cleanup)() = Cleanup; +} + +enum +{ + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliTextViewNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetStyleToCurrentText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetLineHeight, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetFadeBoundary, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetEllipsizeText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetWidthExceedPolicy, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetAndGetHeightExceedPolicy, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewTestLayoutOptions01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewTestLayoutOptions02, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewInsertRemoveText, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSnapshotEnable, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewScroll, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetProperty, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewSetSortModifier, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliTextViewUnderlineText, POSITIVE_TC_IDX ); + +// Called only once before first test is run. + +static void Startup() +{ +} + +// Called only once after last test is run + +static void Cleanup() +{ +} + +static void UtcDaliTextViewNew() +{ + tet_infoline("UtcDaliTextViewNew: "); + ToolkitTestApplication application; + + // Test default constructor. + TextView view; + + DALI_TEST_CHECK( !view ); + + // Test default initialization. + view = TextView::New(); + + DALI_TEST_CHECK( view ); + + // Test copy constructor and asignment operator. + TextView viewCopy1; + + viewCopy1 = view; + + DALI_TEST_CHECK( viewCopy1 ); + + TextView viewCopy2( view ); + + DALI_TEST_CHECK( viewCopy2 ); + + // Test down cast. + Actor actorView; + + actorView = view; + + TextView downCastView = TextView::DownCast( actorView ); + + DALI_TEST_CHECK( downCastView ); + + // Test constructor with a given text. + + const std::string text( "Hello world!" ); + + const float DESCENDER = 8.0f; + + TextView view1 = TextView::New( text ); + + DALI_TEST_EQUALS( view1.GetText(), text, TEST_LOCATION ); + + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + TextView view2 = TextView::New( styledText ); + + DALI_TEST_EQUALS( view2.GetText(), text, TEST_LOCATION ); + + // Check the default Toolkit::TextView::CharacterLayoutInfo::CharacterLayoutInfo() to increase coverage. + TextView::CharacterLayoutInfo characterLayoutInfo; + + DALI_TEST_EQUALS( characterLayoutInfo.mSize, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mPosition, Vector3::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsNewLineChar, false, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsRightToLeftCharacter, false, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsVisible, true, TEST_LOCATION ); + + TextView::CharacterLayoutInfo characterLayoutInfo2( Size( 2.f, 2.f ), + Vector3( 3.f, 4.f, 5.f ), + true, + true, + false, + DESCENDER ); + + characterLayoutInfo = characterLayoutInfo2; + + DALI_TEST_EQUALS( characterLayoutInfo.mSize, Size( 2.f, 2.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mPosition, Vector3( 3.f, 4.f, 5.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsNewLineChar, true, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsRightToLeftCharacter, true, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mIsVisible, false, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo.mDescender, DESCENDER , TEST_LOCATION ); + + + TextView::CharacterLayoutInfo characterLayoutInfo3( characterLayoutInfo ); + + DALI_TEST_EQUALS( characterLayoutInfo3.mSize, Size( 2.f, 2.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo3.mPosition, Vector3( 3.f, 4.f, 5.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo3.mIsNewLineChar, true, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo3.mIsRightToLeftCharacter, true, TEST_LOCATION ); + DALI_TEST_EQUALS( characterLayoutInfo3.mIsVisible, false, TEST_LOCATION ); + + // Check the default Toolkit::TextView::TextLayoutInfo::TextLayoutInfo() to increase coverage. + + TextView::TextLayoutInfo textLayoutInfo; + DALI_TEST_EQUALS( textLayoutInfo.mCharacterLayoutInfoTable.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mCharacterLogicalToVisualMap.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mCharacterVisualToLogicalMap.size(), 0u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mTextSize, Size::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo.mScrollOffset, Vector2::ZERO, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + textLayoutInfo.mCharacterLayoutInfoTable.push_back( characterLayoutInfo ); + textLayoutInfo.mCharacterLogicalToVisualMap.push_back( 1 ); + textLayoutInfo.mCharacterVisualToLogicalMap.push_back( 1 ); + textLayoutInfo.mTextSize = Size( 10.f, 10.f ); + textLayoutInfo.mScrollOffset = Vector2( 5.f, 5.f ); + + TextView::TextLayoutInfo textLayoutInfo2( textLayoutInfo ); + + DALI_TEST_EQUALS( textLayoutInfo2.mCharacterLayoutInfoTable.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mCharacterLogicalToVisualMap.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mCharacterVisualToLogicalMap.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mTextSize, Size( 10.f, 10.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo2.mScrollOffset, Vector2( 5.f, 5.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + TextView::TextLayoutInfo textLayoutInfo3; + + textLayoutInfo3 = textLayoutInfo2; + + DALI_TEST_EQUALS( textLayoutInfo3.mCharacterLayoutInfoTable.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo3.mCharacterLogicalToVisualMap.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo3.mCharacterVisualToLogicalMap.size(), 1u, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo3.mTextSize, Size( 10.f, 10.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( textLayoutInfo3.mScrollOffset, Vector2( 5.f, 5.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + TextView view = TextView::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliTextViewSetAndGetText() +{ + tet_infoline("UtcDaliTextViewSetAndGetText: "); + ToolkitTestApplication application; + + TextView view = TextView::New(); + view.SetSnapshotModeEnabled( false ); // Disables offscreen rendering. + + std::string str( "Text with differing aCeNdEr and dEcEnDeR" ); + + view.SetText( str ); + DALI_TEST_EQUALS( view.GetText(), str, TEST_LOCATION ); + + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( str, styledText ); + + view.SetText( styledText ); + DALI_TEST_EQUALS( view.GetText(), str, TEST_LOCATION ); + + // Test the number of text actor created. + + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gNumberObjectCreated = 0; + registry.ObjectCreatedSignal().Connect(&TestCallback); + + // Following string should create three text-actors ([Hel], [lo wo] and [rld]). + std::string text( "Hello world!\n" + "\n" ); + + Stage::GetCurrent().Add( view ); + view.SetText( text ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( 3u, gNumberObjectCreated, TEST_LOCATION ); +} + +static void UtcDaliTextViewSetStyleToCurrentText() +{ + tet_infoline("UtcDaliTextViewSetStyleToCurrentText: "); + ToolkitTestApplication application; + + TextStyle style; + style.SetItalics( true ); + + const std::string text( "앞서 농식품부 주이석 검역검사본부\n" + "동물방역부장을 단장으로 하는\n" + "민관합동조사단은 지난달 30일부터\n" + "12일간의 현지 조사활동을 마치고\n" + "11일 새벽 귀국했습니다." ); + TextView view = TextView::New( text ); + + bool fail = false; + try + { + view.SetStyleToCurrentText( style ); + } + catch( ... ) + { + tet_printf( "Tet case fails\n" ); + fail = true; + tet_result(TET_FAIL); + } + + DALI_TEST_CHECK( !fail ); +} + +static void UtcDaliTextViewSetAndGetLineHeight() +{ + tet_infoline("UtcDaliTextViewSetAndGetLineHeight: "); + + ToolkitTestApplication application; + + const float lineHeightOffset( 9.f ); + + TextView textView = TextView::New(); + + textView.SetLineHeightOffset( PointSize( lineHeightOffset ) ); + + DALI_TEST_EQUALS( float(textView.GetLineHeightOffset()), lineHeightOffset, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliTextViewSetAndGetFadeBoundary() +{ + tet_infoline("UtcDaliTextViewSetAndGetFadeBoundary: "); + + ToolkitTestApplication application; + + TextView::FadeBoundary fadeBoundary( PixelSize( 0 ), PixelSize( 20 ), PixelSize( 0 ), PixelSize( 10 ) ); + + TextView textView = TextView::New( "Hello world!" ); + + textView.SetFadeBoundary( fadeBoundary ); + + TextView::FadeBoundary fadeBoundary2 = textView.GetFadeBoundary(); + + DALI_TEST_EQUALS( fadeBoundary.mLeft, fadeBoundary2.mLeft, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mRight, fadeBoundary2.mRight, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mTop, fadeBoundary2.mTop, TEST_LOCATION ); + DALI_TEST_EQUALS( fadeBoundary.mBottom, fadeBoundary2.mBottom, TEST_LOCATION ); +} + +static void UtcDaliTextViewSetAndGetEllipsizeText() +{ + tet_infoline("UtcDaliTextViewSetAndGetEllipsizeText: "); + + ToolkitTestApplication application; + + TextView textView = TextView::New( "Hello world!" ); + + textView.SetEllipsizeText( std::string( "..." ) ); + + DALI_TEST_EQUALS( std::string( "..." ), textView.GetEllipsizeText(), TEST_LOCATION ); + + Toolkit::MarkupProcessor::StyledTextArray styledTextArray; + + GetStyledTextArray( std::string( "..." ), styledTextArray ); + + textView.SetEllipsizeText( styledTextArray ); + + DALI_TEST_EQUALS( std::string( "..." ), textView.GetEllipsizeText(), TEST_LOCATION ); + +} + +static void UtcDaliTextViewSetAndGetWidthExceedPolicy() +{ + tet_infoline("UtcDaliTextViewSetAndGetWidthExceedPolicy: "); + + ToolkitTestApplication application; + + const TextView::ExceedPolicy EXCEED_POLICIES[] = { TextView::Original, TextView::Fade, TextView::Split, TextView::ShrinkToFit }; + const unsigned int NUM_EXCEED_POLICIES = sizeof( EXCEED_POLICIES ) / sizeof( unsigned int ); + + TextView textView = TextView::New( "Hello world!" ); + + for( unsigned int epIndex = 0; epIndex < NUM_EXCEED_POLICIES; ++epIndex ) + { + textView.SetWidthExceedPolicy( EXCEED_POLICIES[epIndex] ); + + DALI_TEST_EQUALS( textView.GetWidthExceedPolicy(), EXCEED_POLICIES[epIndex], TEST_LOCATION ); + } +} + +static void UtcDaliTextViewSetAndGetHeightExceedPolicy() +{ + tet_infoline("UtcDaliTextViewSetAndGetHeightExceedPolicy: "); + + ToolkitTestApplication application; + + const TextView::ExceedPolicy EXCEED_POLICIES[] = { TextView::Original, TextView::Fade, TextView::ShrinkToFit }; + const unsigned int NUM_EXCEED_POLICIES = sizeof( EXCEED_POLICIES ) / sizeof( unsigned int ); + + TextView textView = TextView::New( "Hello world!" ); + + for( unsigned int epIndex = 0; epIndex < NUM_EXCEED_POLICIES; ++epIndex ) + { + textView.SetHeightExceedPolicy( EXCEED_POLICIES[epIndex] ); + + DALI_TEST_EQUALS( textView.GetHeightExceedPolicy(), EXCEED_POLICIES[epIndex], TEST_LOCATION ); + } +} + +static void UtcDaliTextViewTestLayoutOptions01() +{ + tet_infoline("UtcDaliTextViewTestLayoutOptions01: "); + + ToolkitTestApplication application; + + const std::string text( "앞서 농식품부 주이석 검역검사본부\n" + "동물방역부장을 단장으로 하는\n" + "민관합동조사단은 지난달 30일부터\n" + "12일간의 현지 조사활동을 마치고\n" + "11일 새벽 귀국했습니다." ); + + const TextView::MultilinePolicy MULTILINE_POLICIES[] = { TextView::SplitByNewLineChar, TextView::SplitByWord, TextView::SplitByChar }; + const TextView::ExceedPolicy EXCEED_WIDTH_POLICIES[] = { TextView::Original, TextView::Fade, TextView::Split, TextView::ShrinkToFit, TextView::EllipsizeEnd }; + const TextView::ExceedPolicy EXCEED_HEIGHT_POLICIES[] = { TextView::Original, TextView::Fade, TextView::ShrinkToFit }; + const Alignment::Type TEXT_ALIGNMENT[] = { static_cast( Alignment::HorizontalLeft | Alignment::VerticalTop ), + static_cast( Alignment::HorizontalLeft | Alignment::VerticalCenter ), + static_cast( Alignment::HorizontalLeft | Alignment::VerticalBottom ), + static_cast( Alignment::HorizontalCenter | Alignment::VerticalTop ), + static_cast( Alignment::HorizontalCenter | Alignment::VerticalCenter ), + static_cast( Alignment::HorizontalCenter | Alignment::VerticalBottom ), + static_cast( Alignment::HorizontalRight | Alignment::VerticalTop ), + static_cast( Alignment::HorizontalRight | Alignment::VerticalCenter ), + static_cast( Alignment::HorizontalRight | Alignment::VerticalBottom ) }; + const TextView::LineJustification LINE_JUSTIFICATION[] = { TextView::Left, TextView::Center, TextView::Right, TextView::Justified }; + + const unsigned int NUM_MULTILINE_POLICIES = sizeof( MULTILINE_POLICIES ) / sizeof( unsigned int ); + const unsigned int NUM_WIDTH_EXCEED_POLICIES = sizeof( EXCEED_WIDTH_POLICIES ) / sizeof( unsigned int ); + const unsigned int NUM_HEIGHT_EXCEED_POLICIES = sizeof( EXCEED_HEIGHT_POLICIES ) / sizeof( unsigned int ); + const unsigned int NUM_TEXT_ALIGNMENT = sizeof( TEXT_ALIGNMENT ) / sizeof( unsigned int ); + const unsigned int NUM_LINE_JUSTIFICATION = sizeof( LINE_JUSTIFICATION ) / sizeof( unsigned int ); + + TextView textView = TextView::New( text ); + textView.SetSnapshotModeEnabled( false ); // Disables offscreen rendering. + + Stage::GetCurrent().Add( textView ); + + TextView::TextLayoutInfo textLayoutInfo; + + for( unsigned int mlpIndex = 0; mlpIndex < NUM_MULTILINE_POLICIES; ++mlpIndex ) + { + textView.SetMultilinePolicy( MULTILINE_POLICIES[mlpIndex] ); + for( unsigned int ewpIndex = 0; ewpIndex < NUM_WIDTH_EXCEED_POLICIES; ++ewpIndex ) + { + textView.SetWidthExceedPolicy( EXCEED_WIDTH_POLICIES[ewpIndex] ); + for( unsigned int ehpIndex = 0; ehpIndex < NUM_HEIGHT_EXCEED_POLICIES; ++ehpIndex ) + { + textView.SetHeightExceedPolicy( EXCEED_HEIGHT_POLICIES[ehpIndex] ); + for( unsigned int taIndex = 0; taIndex < NUM_TEXT_ALIGNMENT; ++taIndex ) + { + textView.SetTextAlignment( TEXT_ALIGNMENT[taIndex] ); + for( unsigned int ljIndex = 0; ljIndex < NUM_LINE_JUSTIFICATION; ++ljIndex ) + { + textView.SetLineJustification( LINE_JUSTIFICATION[ljIndex] ); + + try + { + textView.GetTextLayoutInfo( textLayoutInfo ); + + application.SendNotification(); + application.Render(); + } + catch( Dali::DaliException& e ) + { + DALI_TEST_EQUALS( e.mCondition, "!\"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination\"", TEST_LOCATION ); + } + catch( ... ) + { + tet_printf( "Tet case fails\n" ); + tet_printf( " MultilinePolicy : %d\n", MULTILINE_POLICIES[mlpIndex] ); + tet_printf( " Width ExceedPolicy : %d\n", EXCEED_WIDTH_POLICIES[ewpIndex] ); + tet_printf( " Height ExceedPolicy : %d\n", EXCEED_HEIGHT_POLICIES[ehpIndex] ); + tet_printf( " TextAlignment : %d\n", TEXT_ALIGNMENT[taIndex] ); + tet_printf( " LineJustification : %d\n", LINE_JUSTIFICATION[ljIndex] ); + tet_result(TET_FAIL); + } + + DALI_TEST_CHECK( LINE_JUSTIFICATION[ljIndex] == textView.GetLineJustification() ); + } + DALI_TEST_CHECK( TEXT_ALIGNMENT[taIndex] == textView.GetTextAlignment() ); + } + DALI_TEST_CHECK( EXCEED_HEIGHT_POLICIES[ehpIndex] == textView.GetHeightExceedPolicy() ); + } + DALI_TEST_CHECK( EXCEED_WIDTH_POLICIES[ewpIndex] == textView.GetWidthExceedPolicy() ); + } + DALI_TEST_CHECK( MULTILINE_POLICIES[mlpIndex] == textView.GetMultilinePolicy() ); + } +} + +static void UtcDaliTextViewTestLayoutOptions02() +{ + tet_infoline("UtcDaliTextViewTestLayoutOptions02: "); + ToolkitTestApplication application; + + // Check some configurations. + + TextView textView = TextView::New(); + textView.SetSnapshotModeEnabled( false ); // Disables offscreen rendering. + + Stage::GetCurrent().Add( textView ); + + // SplitByWord and ShrinkToFit. + // Centered alignment. + // Centered justification. + // Don't create a text actor per character. + + textView.SetMultilinePolicy( TextView::SplitByWord ); + textView.SetWidthExceedPolicy( TextView::ShrinkToFit ); + textView.SetHeightExceedPolicy( TextView::ShrinkToFit ); + textView.SetTextAlignment( static_cast( Alignment::HorizontalCenter | Alignment::VerticalTop ) ); + textView.SetLineJustification( TextView::Center ); + textView.SetSize( 136.56252f, 100.f ); + + textView.SetText( "Hello world!" ); + + application.SendNotification(); + application.Render(); + + std::vector sizes; + sizes.push_back( Size( 34.14063f, 11.380210f ) ); // + sizes.push_back( Size( 56.90105f, 11.380210f ) ); // + sizes.push_back( Size( 45.52084f, 11.380210f ) ); // By default characters have width and height values of 11.380210. + // The result should be a line with the text 'Hello world' as shown below. + std::vector positions; // ____________ + positions.push_back( Vector3( 0.000008f, 11.380209f, 0.f ) ); // |Hello world!| + positions.push_back( Vector3( 34.14063f, 11.380209f, 0.f ) ); // ------------ + positions.push_back( Vector3( 91.04168f, 11.380209f, 0.f ) ); // + + DALI_TEST_CHECK( positions.size() == textView.GetChildCount() ); // Check text has two text-actors. + + for( std::size_t index = 0, num = textView.GetChildCount(); index < num; ++index ) + { + const Vector3& size = textView.GetChildAt(index).GetCurrentSize(); + const Vector3& position = textView.GetChildAt(index).GetCurrentPosition(); + + DALI_TEST_EQUALS( size.width, sizes[index].width, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, sizes[index].height, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( position.width, positions[index].width, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( position.height, positions[index].height, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + } + + textView.SetSize( 50.f, 50.f ); + textView.SetTextAlignment( static_cast( Alignment::HorizontalCenter | Alignment::VerticalCenter ) ); + textView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); + textView.SetLineJustification( Toolkit::TextView::Left ); + + application.SendNotification(); + application.Render(); + + sizes.clear(); + sizes.push_back( Size( 24.999999f, 8.333333f ) ); // + sizes.push_back( Size( 24.999999f, 8.333333f ) ); // + sizes.push_back( Size( 16.666666f, 8.333333f ) ); // Longest word is 'world!' (6 characters x 11.380210) which doesn't fit in the 50x50 box. + sizes.push_back( Size( 33.333332f, 8.333333f ) ); // The scale factor is 0.732265339, so the character size is 8.333333. + // Text should be split in two lines, centered in the vertical dimension and fitted in the horizontal one. + positions.clear(); // As shown below, the text is two lines and centered in the vertical dimension and + positions.push_back( Vector3( 0.000008f, 25.223114f, 0.f ) ); // it should start in middle height (~25). + positions.push_back( Vector3( 24.999999f, 25.223114f, 0.f ) ); // ______ + positions.push_back( Vector3( 0.000006f, 33.556446f, 0.f ) ); // | | + positions.push_back( Vector3( 16.666666f, 33.556446f, 0.f ) ); // |Hello | + // |world!| + // |______| + // + + DALI_TEST_CHECK( positions.size() == textView.GetChildCount() ); // Check text has two text-actors. + + for( std::size_t index = 0, num = textView.GetChildCount(); index < num; ++index ) + { + const Vector3& size = textView.GetChildAt(index).GetCurrentSize(); + const Vector3& position = textView.GetChildAt(index).GetCurrentPosition(); + + DALI_TEST_EQUALS( size.width, sizes[index].width, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( size.height, sizes[index].height, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( position.width, positions[index].width, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( position.height, positions[index].height, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + } + + // TODO: Add more tests when TextView implementation is finished. +} + +static void UtcDaliTextViewInsertRemoveText() +{ + tet_infoline("UtcDaliTextViewInsertRemoveText: "); + ToolkitTestApplication application; + + std::string text("Hello "); + + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + TextView view = TextView::New( "world!" ); + + view.InsertTextAt( 0, styledText ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Hello world!"), TEST_LOCATION ); + + view.RemoveTextFrom( 4, 5 ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Hellld!"), TEST_LOCATION ); + + view.InsertTextAt( 0, "Hello " ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Hello Hellld!"), TEST_LOCATION ); + + + view.InsertTextAt( 0, "Hello " ); + view.InsertTextAt( 0, "Hello " ); + view.InsertTextAt( 0, "Hello " ); + view.InsertTextAt( 0, "Hello " ); + view.RemoveTextFrom( 4, 2 ); + view.RemoveTextFrom( 4, 2 ); + view.RemoveTextFrom( 4, 2 ); + view.RemoveTextFrom( 4, 2 ); + view.RemoveTextFrom( 4, 2 ); + view.SetText( "Hello world!" ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Hello world!"), TEST_LOCATION ); + + view.ReplaceTextFromTo( 5, 1, "" ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Helloworld!"), TEST_LOCATION ); + + view.ReplaceTextFromTo( 0, 11, styledText ); + + DALI_TEST_EQUALS( view.GetText(), std::string("Hello "), TEST_LOCATION ); +} + +static void UtcDaliTextViewSnapshotEnable() +{ + tet_infoline("UtcDaliTextViewSnapshotEnable: "); + ToolkitTestApplication application; + + // Avoids the frame buffer texture to throw an exception. + application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE ); + + TextView view = TextView::New( "Hello world! This is a snapshot test." ); + + Stage::GetCurrent().Add( view ); + + view.SetSnapshotModeEnabled( true ); // VCC. By default the snapshot mode should be enabled but it has been temporary disabled. + // This line should be removed when text-view is set to use the snapshot mode by default. + + // Snapshot is enabled by default. + DALI_TEST_CHECK( view.IsSnapshotModeEnabled() ); + + application.SendNotification(); + application.Render(); + + // TextView should have only two actors: + // the root (Actor) and the image (ImageActor). + + DALI_TEST_EQUALS( view.GetChildCount(), 2u, TEST_LOCATION ); + + view.SetSnapshotModeEnabled( false ); + DALI_TEST_CHECK( !view.IsSnapshotModeEnabled() ); + + application.SendNotification(); + application.Render(); + + // TextView should have one text-actor per word. + + DALI_TEST_EQUALS( view.GetChildCount(), 7u, TEST_LOCATION ); +} + +static void UtcDaliTextViewScroll() +{ + tet_infoline("UtcDaliTextViewScroll: "); + ToolkitTestApplication application; + + // Avoids the frame buffer texture to throw an exception. + application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE ); + + TextView view = TextView::New( "Hello world! This is a scroll test." ); + view.SetSize( 100.f, 100.f ); + view.SetSnapshotModeEnabled( false ); + + Stage::GetCurrent().Add( view ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( !view.IsScrollEnabled() ); // Scroll should be disabled by default. + + view.SetScrollEnabled( true ); + view.ScrolledSignal().Connect( &TestTextScrolled ); + + DALI_TEST_CHECK( view.IsScrollEnabled() ); + DALI_TEST_CHECK( view.IsSnapshotModeEnabled() ); // Scroll should enable snapshot mode. + + gTextScrolled = false; + gScrollDelta = Vector2::ZERO; + view.SetScrollPosition( Vector2( 400.f, 400.f ) ); + + application.SendNotification(); + application.Render(); + + const Vector2& scrollPosition = view.GetScrollPosition(); + DALI_TEST_EQUALS( scrollPosition, Vector2( 149.153656f, 0.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + DALI_TEST_CHECK( gTextScrolled ); + DALI_TEST_EQUALS( gScrollDelta, Vector2( 149.153656f, 0.f ), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + DALI_TEST_CHECK( view.IsScrollPositionTrimmed() ); +} + +static void UtcDaliTextViewSetProperty() +{ + tet_infoline("UtcDaliTextViewSetAndGetText: "); + ToolkitTestApplication application; + + TextView view = TextView::New( "Hello world!" ); + Stage::GetCurrent().Add( view ); + + //Test multiline policy property + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_MULTILINE_POLICY), "SplitByNewLineChar"); + DALI_TEST_CHECK( Toolkit::TextView::SplitByNewLineChar == view.GetMultilinePolicy() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_MULTILINE_POLICY), "SplitByWord"); + DALI_TEST_CHECK( Toolkit::TextView::SplitByWord == view.GetMultilinePolicy() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_MULTILINE_POLICY), "SplitByChar"); + DALI_TEST_CHECK( Toolkit::TextView::SplitByChar == view.GetMultilinePolicy() ); + + //Test width exceed policy property + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_WIDTH_EXCEED_POLICY), "Original"); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_HEIGHT_EXCEED_POLICY), "Original"); + DALI_TEST_CHECK( Toolkit::TextView::Original == view.GetWidthExceedPolicy() ); + DALI_TEST_CHECK( Toolkit::TextView::Original == view.GetHeightExceedPolicy() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_WIDTH_EXCEED_POLICY), "Fade"); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_HEIGHT_EXCEED_POLICY), "Fade"); + DALI_TEST_CHECK( Toolkit::TextView::Fade == view.GetWidthExceedPolicy() ); + DALI_TEST_CHECK( Toolkit::TextView::Fade == view.GetHeightExceedPolicy() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_WIDTH_EXCEED_POLICY), "ShrinkToFit"); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_HEIGHT_EXCEED_POLICY), "ShrinkToFit"); + DALI_TEST_CHECK( Toolkit::TextView::ShrinkToFit == view.GetWidthExceedPolicy() ); + DALI_TEST_CHECK( Toolkit::TextView::ShrinkToFit == view.GetHeightExceedPolicy() ); + + //Test line justification property + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_LINE_JUSTIFICATION), "Left"); + DALI_TEST_CHECK( Toolkit::TextView::Left == view.GetLineJustification() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_LINE_JUSTIFICATION), "Center"); + DALI_TEST_CHECK( Toolkit::TextView::Center == view.GetLineJustification() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_LINE_JUSTIFICATION), "Right"); + DALI_TEST_CHECK( Toolkit::TextView::Right == view.GetLineJustification() ); + + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_LINE_JUSTIFICATION), "Justified"); + DALI_TEST_CHECK( Toolkit::TextView::Justified == view.GetLineJustification() ); + + //Test fade boundary property + unsigned int testValue = 23; + PixelSize leftFadeBoundary(testValue); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_FADE_BOUNDARY_LEFT), testValue); + DALI_TEST_CHECK( leftFadeBoundary == view.GetFadeBoundary().mLeft ); + + testValue = 26; + PixelSize rightFadeBoundary(testValue); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_FADE_BOUNDARY_RIGHT), testValue); + DALI_TEST_CHECK( rightFadeBoundary == view.GetFadeBoundary().mRight ); + + testValue = 2; + PixelSize topFadeBoundary(testValue); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_FADE_BOUNDARY_TOP), testValue); + DALI_TEST_CHECK( topFadeBoundary == view.GetFadeBoundary().mTop ); + + testValue = 11; + PixelSize bottomFadeBoundary(testValue); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_FADE_BOUNDARY_BOTTOM), testValue); + DALI_TEST_CHECK( bottomFadeBoundary == view.GetFadeBoundary().mBottom ); + + //Test Line height offset property + float testOffsetValue = 14.04f; + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_LINE_HEIGHT_OFFSET), testOffsetValue); + DALI_TEST_CHECK( PointSize(testOffsetValue) == view.GetLineHeightOffset() ); + + //Test alignment property + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_HORIZONTAL_ALIGNMENT), "HorizontalLeft"); + view.SetProperty(view.GetPropertyIndex(TextView::PROPERTY_VERTICAL_ALIGNMENT), "VerticalTop"); + DALI_TEST_CHECK( (Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop) == view.GetTextAlignment() ); +} + +static void UtcDaliTextViewSetSortModifier() +{ + tet_infoline("UtcDaliTextViewSetAndGetText: "); + ToolkitTestApplication application; + + TextView view = TextView::New( "Hello world!" ); + Stage::GetCurrent().Add( view ); + + view.SetSortModifier( 10.f ); + view.SetSnapshotModeEnabled( false ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( RenderableActor::DownCast(view.GetChildAt(0)).GetSortModifier(), 10.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliTextViewUnderlineText() +{ + std::cout << "##############################" << std::endl; + + tet_infoline("UtcDaliTextViewUnderlineText: "); + ToolkitTestApplication application; + + TextView textView = TextView::New( "gggggggggggggg" ); + textView.SetSnapshotModeEnabled( false ); + + textView.SetSize( 150.f, 100.f ); + + Stage::GetCurrent().Add( textView ); + + application.SendNotification(); + application.Render(); + + std::vector positions; + positions.push_back( 6.448784f ); + positions.push_back( 9.862847f ); + positions.push_back( 13.276909f ); + positions.push_back( 16.690973f ); + positions.push_back( 13.276909f ); + positions.push_back( 9.862847f ); + positions.push_back( 6.448784f ); + + for( std::size_t index = 0, num = textView.GetChildCount(); index < num; ++index ) + { + TextStyle style = TextActor::DownCast( textView.GetChildAt(index) ).GetTextStyle(); + + DALI_TEST_EQUALS( 4.17274f, style.GetUnderlineThickness(), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + DALI_TEST_EQUALS( positions[index], style.GetUnderlinePosition(), Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + } +} diff --git a/automated-tests/dali-test-suite/toolbar/.gitignore b/automated-tests/dali-test-suite/toolbar/.gitignore new file mode 100644 index 0000000..3434380 --- /dev/null +++ b/automated-tests/dali-test-suite/toolbar/.gitignore @@ -0,0 +1,2 @@ +utc-Dali-Alignment +utc-Dali-ToolBar diff --git a/automated-tests/dali-test-suite/toolbar/Makefile b/automated-tests/dali-test-suite/toolbar/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/toolbar/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/toolbar/file.list b/automated-tests/dali-test-suite/toolbar/file.list new file mode 100644 index 0000000..3182196 --- /dev/null +++ b/automated-tests/dali-test-suite/toolbar/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-ToolBar \ diff --git a/automated-tests/dali-test-suite/toolbar/tslist b/automated-tests/dali-test-suite/toolbar/tslist new file mode 100644 index 0000000..70926d4 --- /dev/null +++ b/automated-tests/dali-test-suite/toolbar/tslist @@ -0,0 +1 @@ +/dali-test-suite/toolbar/utc-Dali-ToolBar diff --git a/automated-tests/dali-test-suite/toolbar/utc-Dali-ToolBar.cpp b/automated-tests/dali-test-suite/toolbar/utc-Dali-ToolBar.cpp new file mode 100644 index 0000000..37493c1 --- /dev/null +++ b/automated-tests/dali-test-suite/toolbar/utc-Dali-ToolBar.cpp @@ -0,0 +1,288 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +static bool gObjectCreatedCallBackCalled; + +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliToolBarNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliToolBarSetBackground, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliToolBarAddControl01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliToolBarAddControl02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliToolBarRemoveControl01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliToolBarRemoveControl02, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +static void UtcDaliToolBarNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarNew"); + + ToolBar toolbar; + + DALI_TEST_CHECK( !toolbar ); + + toolbar = ToolBar::New(); + + DALI_TEST_CHECK( toolbar ); + + ToolBar toolbar2(toolbar); + + DALI_TEST_CHECK( toolbar2 == toolbar ); + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect(&TestCallback); + { + ToolBar toolbar = ToolBar::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); + + Actor actor = toolbar; + toolbar == ToolBar::DownCast( actor ); + + DALI_TEST_CHECK( toolbar ); +} + +static void UtcDaliToolBarSetBackground() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarSetBackground"); + + try + { + ImageActor toolBarBackground = CreateSolidColorActor( Color::RED ); + + ToolBar toolbar = ToolBar::New(); + toolbar.SetBackground( toolBarBackground ); + + Stage::GetCurrent().Add( toolbar ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); + + application.SendNotification(); // VCC To be removed!! + application.Render(); // VCC To be removed!! +} + +static void UtcDaliToolBarAddControl01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarAddControl01"); + + try + { + ImageActor control1 = CreateSolidColorActor( Color::RED ); + control1.SetSize( 100.f, 100.f ); + ImageActor control2 = CreateSolidColorActor( Color::RED ); + control2.SetSize( 100.f, 100.f ); + ImageActor control3 = CreateSolidColorActor( Color::RED ); + control3.SetSize( 100.f, 100.f ); + ImageActor control4 = CreateSolidColorActor( Color::RED ); + control4.SetSize( 100.f, 100.f ); + ImageActor control5 = CreateSolidColorActor( Color::RED ); + control5.SetSize( 100.f, 100.f ); + + ToolBar toolbar = ToolBar::New(); + toolbar.SetSize( 600.f, 100.f ); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + toolbar.Add( control1 ); + toolbar.AddControl( control2, 0.1f, Alignment::HorizontalLeft, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + toolbar.AddControl( control3, 0.1f, Alignment::HorizontalCenter, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + toolbar.AddControl( control4, 0.1f, Alignment::HorizontalCenter, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + toolbar.AddControl( control5, 0.1f, Alignment::HorizontalRight, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + + ImageActor control6 = CreateSolidColorActor( Color::RED ); + control6.SetSize( 100.f, 100.f ); + ImageActor control7 = CreateSolidColorActor( Color::RED ); + control7.SetSize( 100.f, 100.f ); + ImageActor control8 = CreateSolidColorActor( Color::RED ); + control8.SetSize( 100.f, 100.f ); + + application.Render(); + application.SendNotification(); + application.Render(); + application.SendNotification(); + + toolbar.AddControl( control6, 0.4f, Alignment::HorizontalLeft, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + toolbar.AddControl( control7, 0.2f, Alignment::HorizontalCenter, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + toolbar.AddControl( control8, 0.2f, Alignment::HorizontalRight, Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} + +static void UtcDaliToolBarAddControl02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarAddControl02"); + + bool daliAssert = false; + + try + { + ImageActor control = CreateSolidColorActor( Color::RED ); + + ToolBar toolbar = ToolBar::New(); + + toolbar.AddControl( control, 0.1f, static_cast( 99 ), Alignment::Padding( 1.f, 1.f, 1.f, 1.f ) ); + } + catch( DaliException e ) + { + daliAssert = true; + tet_result(TET_PASS); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + if( !daliAssert ) + { + tet_result(TET_FAIL); + } +} + +static void UtcDaliToolBarRemoveControl01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarRemoveControl01"); + + try + { + ImageActor control = CreateSolidColorActor( Color::RED ); + + ToolBar toolbar = ToolBar::New(); + toolbar.AddControl( control, 0.1f, Alignment::HorizontalLeft ); + + toolbar.RemoveControl( control ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} + +static void UtcDaliToolBarRemoveControl02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliToolBarRemoveControl02"); + + try + { + ImageActor control01 = CreateSolidColorActor( Color::RED ); + ImageActor control02 = CreateSolidColorActor( Color::RED ); + + ToolBar toolbar01 = ToolBar::New(); + ToolBar toolbar02 = ToolBar::New(); + toolbar01.AddControl( control01, 0.1f, Alignment::HorizontalLeft ); + toolbar02.AddControl( control02, 0.1f, Alignment::HorizontalLeft ); + + toolbar02.RemoveControl( control01 ); + } + catch( Dali::DaliException& e ) + { + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "false", TEST_LOCATION); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + try + { + ImageActor control = CreateSolidColorActor( Color::RED ); + + ToolBar toolbar = ToolBar::New(); + toolbar.AddControl( control, 0.1f, Alignment::HorizontalLeft ); + + toolbar.RemoveControl( control ); + toolbar.RemoveControl( control ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} diff --git a/automated-tests/dali-test-suite/transition-effects/.gitignore b/automated-tests/dali-test-suite/transition-effects/.gitignore new file mode 100644 index 0000000..a0f6f7c --- /dev/null +++ b/automated-tests/dali-test-suite/transition-effects/.gitignore @@ -0,0 +1,3 @@ +utc-Dali-CubeTransitionEffect +utc-Dali-CubeTransitionWaveEffect +utc-Dali-CubeTransitionCrossEffect diff --git a/automated-tests/dali-test-suite/transition-effects/Makefile b/automated-tests/dali-test-suite/transition-effects/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/transition-effects/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/transition-effects/file.list b/automated-tests/dali-test-suite/transition-effects/file.list new file mode 100644 index 0000000..d1d503d --- /dev/null +++ b/automated-tests/dali-test-suite/transition-effects/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-CubeTransitionEffect \ diff --git a/automated-tests/dali-test-suite/transition-effects/tslist b/automated-tests/dali-test-suite/transition-effects/tslist new file mode 100644 index 0000000..665718d --- /dev/null +++ b/automated-tests/dali-test-suite/transition-effects/tslist @@ -0,0 +1 @@ +/dali-test-suite/transition-effects/utc-Dali-CubeTransitionEffect diff --git a/automated-tests/dali-test-suite/transition-effects/utc-Dali-CubeTransitionEffect.cpp b/automated-tests/dali-test-suite/transition-effects/utc-Dali-CubeTransitionEffect.cpp new file mode 100644 index 0000000..5c5ab27 --- /dev/null +++ b/automated-tests/dali-test-suite/transition-effects/utc-Dali-CubeTransitionEffect.cpp @@ -0,0 +1,965 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +namespace +{ +const unsigned int NUM_ROWS = 16; +const unsigned int NUM_COLUMNS = 10; +const Vector2 VIEW_AREA_SIZE( 480.0f, 800.0f ); +const float TRANSITION_DURATION = 0.5f; +const float CUBE_DISPLACEMENT = 55.f; +const Vector2 PAN_POSITION1( VIEW_AREA_SIZE.x * 0.75f, VIEW_AREA_SIZE.y * 0.25f ); +const Vector2 PAN_DISPLACEMENT1( -5.f, 5.f ); +const Vector2 PAN_POSITION2( VIEW_AREA_SIZE.x * 0.25f, VIEW_AREA_SIZE.y * 0.75f ); +const Vector2 PAN_DISPLACEMENT2( 5.f, 5.f ); +const int RENDER_FRAME_INTERVAL = 16; +static const float ROTATION_EPSILON = 0.0001f; +const float OFFSCREEN_RENDER_DURATION = 0.05f; + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + +/** + * Simulate time passed by, waiting for certain process to finish + * @param[in] application Test application instance + * @param[in] durationToPass Time to pass in milliseconds. + */ +void Wait(ToolkitTestApplication& application, float durationToPass) +{ + int duration = static_cast(durationToPass*1000.f); + // wait 2 more frames to compensate the two frames used by the bitmapImage waiting for the loading succeeded signal + for(int i = 0; i <= duration/RENDER_FRAME_INTERVAL+2 ; i++) + { + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + } +} + + +ImageActor CreateSolidColorImageActor( ToolkitTestApplication& application, const Vector4& color, unsigned int width, unsigned int height ) +{ + BitmapImage imageData = BitmapImage::New( width, height, Pixel::RGBA8888 ); + ImageActor imageActor = ImageActor::New( imageData ); + Stage::GetCurrent().Add( imageActor ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + unsigned int size = width * height; + + for( size_t i = 0; i < size; i++ ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + imageData.Update(); + + application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE ); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + application.Render(RENDER_FRAME_INTERVAL); + application.SendNotification(); + + return imageActor; +} + +//Callback class to test whether transition completed signal is emitted when the transition animation is finished +class TransitionCompletedCallback : public Dali::ConnectionTracker +{ +public: + TransitionCompletedCallback( bool& signalReceived, CubeTransitionEffect& effect, ImageActor& imageActor ) + : mSignalVerified( signalReceived ), + mCurrentEffect( effect ), + mActorTransitTo( imageActor ) + { + } + + void Callback( CubeTransitionEffect effect, ImageActor imageActor ) + { + tet_infoline( "Verifying TransitionCompletedSignal" ); + + if( mCurrentEffect == effect && mActorTransitTo == imageActor ) + { + mSignalVerified = true; + } + } + + void Reset() + { + mSignalVerified = false; + } + + bool& mSignalVerified; + CubeTransitionEffect& mCurrentEffect; + ImageActor& mActorTransitTo; +}; + +} // namespace + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliCubeTransitionWaveEffectNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionCrossEffectNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionFoldEffectNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectSetGetTransitionDuration, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectSetGetCubeDisplacement, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectGetRoot, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectIsTransiting, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectSetCurrentImage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectSetTargetImage, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionWaveEffectStartTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionCrossEffectStartTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionFoldEffectStartTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectSignalTransitionCompleted, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionEffectPauseResumeTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionWaveEffectStopTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionCrossEffectStopTransition, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliCubeTransitionFoldEffectStopTransition, POSITIVE_TC_IDX ); + + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliCubeTransitionWaveEffectNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionWaveEffectNew "); + + CubeTransitionEffect waveEffect; + + DALI_TEST_CHECK( !waveEffect ); + + waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + + DALI_TEST_CHECK( waveEffect ); + + waveEffect = NULL; + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliCubeTransitionCrossEffectNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionCrossEffectNew "); + + CubeTransitionEffect crossEffect; + + DALI_TEST_CHECK( !crossEffect ); + + crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + + DALI_TEST_CHECK( crossEffect ); + + crossEffect = NULL; + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliCubeTransitionFoldEffectNew() +{ + ToolkitTestApplication application; + tet_infoline( " UtcDaliCubeTransitionFoldEffectNew " ); + + CubeTransitionEffect foldEffect; + + DALI_TEST_CHECK( !foldEffect ); + + foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + + DALI_TEST_CHECK( foldEffect ); + + foldEffect = NULL; + + //Additional check to ensure object is created by checking if it is registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliCubeTransitionEffectSetGetTransitionDuration() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectSetGetTransitionDuration "); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + DALI_TEST_EQUALS( TRANSITION_DURATION, waveEffect.GetTransitionDuration(), TEST_LOCATION ); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + DALI_TEST_EQUALS( TRANSITION_DURATION, crossEffect.GetTransitionDuration(), TEST_LOCATION ); + + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + DALI_TEST_EQUALS( TRANSITION_DURATION, foldEffect.GetTransitionDuration(), TEST_LOCATION ); +} + +static void UtcDaliCubeTransitionEffectSetGetCubeDisplacement() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectSetGetTransitionDuration "); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + DALI_TEST_EQUALS( CUBE_DISPLACEMENT, waveEffect.GetCubeDisplacement(), TEST_LOCATION ); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + DALI_TEST_EQUALS( CUBE_DISPLACEMENT, crossEffect.GetCubeDisplacement(), TEST_LOCATION ); + + //Cube displacement is not used in CubeTransitionFoldEffect +} + +//Test common codes in base class +static void UtcDaliCubeTransitionEffectGetRoot() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectGetRoot "); + + unsigned int totalNum = NUM_ROWS*NUM_COLUMNS; + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + Actor rootActor = waveEffect.GetRoot(); + + // check that we have a total of NUM_ROWS*NUM_COLUMNS cubes; + DALI_TEST_CHECK( totalNum == rootActor.GetChildCount() ); + + // check that every cube has two children + DALI_TEST_CHECK( 2 == rootActor.GetChildAt(0).GetChildCount() ); + DALI_TEST_CHECK( 2 == rootActor.GetChildAt(totalNum/2).GetChildCount() ); + DALI_TEST_CHECK( 2 == rootActor.GetChildAt(totalNum-1).GetChildCount() ); +} + +static void UtcDaliCubeTransitionEffectIsTransiting() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectIsTransiting "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,30,30); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + DALI_TEST_CHECK( !waveEffect.IsTransiting() ); + + waveEffect.SetCurrentImage(imageActor); + waveEffect.SetTargetImage(imageActor); + //transition is started + waveEffect.StartTransition(); + DALI_TEST_CHECK( waveEffect.IsTransiting() ); + //transition is finished + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK( !waveEffect.IsTransiting() ); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + DALI_TEST_CHECK( !crossEffect.IsTransiting() ); + + crossEffect.SetCurrentImage(imageActor); + crossEffect.SetTargetImage(imageActor); + //transition is started + crossEffect.StartTransition(false); + DALI_TEST_CHECK( crossEffect.IsTransiting() ); + //transition is finished + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK( !crossEffect.IsTransiting() ); + + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + DALI_TEST_CHECK( !foldEffect.IsTransiting() ); + + foldEffect.SetCurrentImage( imageActor ); + foldEffect.SetTargetImage( imageActor ); + //transition is started + foldEffect.StartTransition(true); + DALI_TEST_CHECK(foldEffect.IsTransiting() ); + //transition is finished + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK( !foldEffect.IsTransiting() ); + +} + +//Test common codes in base class +static void UtcDaliCubeTransitionEffectSetCurrentImage() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectSetCurrentImage "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,40,40) ; + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetCurrentImage( imageActor ); + + // the current image content is set to the tiles facing the camera + ImageActor tile = ImageActor::DownCast( (waveEffect.GetRoot().GetChildAt(0).GetChildAt(0))); + + //check the pixel area set to the cube + ImageActor::PixelArea pixelAreaDef( 0, 0, VIEW_AREA_SIZE.x / NUM_COLUMNS, VIEW_AREA_SIZE.y / NUM_ROWS); + ImageActor::PixelArea pixelArea = tile.GetPixelArea(); + DALI_TEST_CHECK ( pixelAreaDef == pixelArea ); + + //check the size of the off screen rendered image + Wait( application, OFFSCREEN_RENDER_DURATION ); + ImageAttributes attributes( tile.GetImage().GetAttributes() ); + DALI_TEST_EQUALS( static_cast(attributes.GetWidth()), VIEW_AREA_SIZE.x, TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(attributes.GetHeight()), VIEW_AREA_SIZE.y, TEST_LOCATION ); +} + +//Test common codes in base class +static void UtcDaliCubeTransitionEffectSetTargetImage() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectSetTargetImage "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,30,30); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + waveEffect.SetCurrentImage( imageActor ); + waveEffect.SetTargetImage( imageActor ); + + // the target image content is set to the tiles currently invisible to the camera + ImageActor tile = ImageActor::DownCast( (rootActor.GetChildAt(0).GetChildAt(1))); + + //check the pixel area set to the cube + ImageActor::PixelArea pixelAreaDef( 0, 0, VIEW_AREA_SIZE.x / NUM_COLUMNS, VIEW_AREA_SIZE.y / NUM_ROWS); + ImageActor::PixelArea pixelArea = tile.GetPixelArea(); + DALI_TEST_CHECK ( pixelAreaDef == pixelArea ); + + //check the size of the off screen rendered image + Wait( application, OFFSCREEN_RENDER_DURATION ); + ImageAttributes attributes( tile.GetImage().GetAttributes() ); + DALI_TEST_EQUALS( static_cast(attributes.GetWidth()), VIEW_AREA_SIZE.x, TEST_LOCATION ); + DALI_TEST_EQUALS( static_cast(attributes.GetHeight()), VIEW_AREA_SIZE.y, TEST_LOCATION ); +} + +static void UtcDaliCubeTransitionWaveEffectStartTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionWaveEffectStartTransition "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,30,30); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + Actor cube = rootActor.GetChildAt(0); + + waveEffect.SetCurrentImage( imageActor ); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation value and color values after different transitions + waveEffect.SetTargetImage( imageActor ); + waveEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + waveEffect.SetTargetImage( imageActor ); + waveEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + waveEffect.SetTargetImage( imageActor ); + waveEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + waveEffect.SetTargetImage( imageActor ); + waveEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} + +static void UtcDaliCubeTransitionCrossEffectStartTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionCrossEffectStartTransition "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,30,30); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = crossEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + crossEffect.SetCurrentImage( imageActor ); + Actor cube0 = rootActor.GetChildAt(0); + Actor cube1 = rootActor.GetChildAt(1); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation values and color values after different transitions + crossEffect.SetTargetImage( imageActor ); + crossEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + crossEffect.SetTargetImage( imageActor ); + crossEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 2.f*Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + + crossEffect.SetTargetImage( imageActor ); + crossEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + crossEffect.SetTargetImage( imageActor ); + crossEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 0.f, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} + +static void UtcDaliCubeTransitionFoldEffectStartTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionFoldEffectStartTransition "); + + ImageActor imageActor = CreateSolidColorImageActor(application, Color::BLUE,30,30); + + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + Actor rootActor = foldEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + foldEffect.SetCurrentImage( imageActor ); + Actor cube0 = rootActor.GetChildAt(0); + Actor cube1 = rootActor.GetChildAt(1); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation values and color values after different transitions + foldEffect.SetTargetImage( imageActor ); + foldEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + foldEffect.SetTargetImage( imageActor ); + foldEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + + foldEffect.SetTargetImage( imageActor ); + foldEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + foldEffect.SetTargetImage( imageActor ); + foldEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} + +static void UtcDaliCubeTransitionEffectSignalTransitionCompleted() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectSignalTransitionCompleted "); + + ImageActor firstImageActor = CreateSolidColorImageActor(application, Color::RED,30,30); + ImageActor secondImageActor = CreateSolidColorImageActor(application, Color::GREEN,20,20); + ImageActor thirdImageActor = CreateSolidColorImageActor(application, Color::BLUE,40,40); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + rootActor = crossEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + CubeTransitionEffect foldEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + rootActor = foldEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + bool signalVerified = false; + CubeTransitionEffect currentEffect; + ImageActor actorTransitTo; + TransitionCompletedCallback callback(signalVerified, currentEffect, actorTransitTo); + waveEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + crossEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + foldEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + + //check that the wave effect is used to transit to secondImageActor + currentEffect = waveEffect; + actorTransitTo = secondImageActor; + waveEffect.SetCurrentImage( firstImageActor ); + waveEffect.SetTargetImage( secondImageActor ); + waveEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + //check that the wave effect is used to transit to thirdImageActor + actorTransitTo = thirdImageActor; + waveEffect.SetTargetImage( thirdImageActor ); + waveEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + //check that the cross effect is used to transit to secondImageActor + currentEffect = crossEffect; + actorTransitTo = secondImageActor; + crossEffect.SetCurrentImage( thirdImageActor ); + crossEffect.SetTargetImage( secondImageActor ); + crossEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + //check that the cross effect is used to transit to firstImageActor + actorTransitTo = firstImageActor; + crossEffect.SetTargetImage( firstImageActor ); + crossEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + //check that the fold effect is used to transit to secondImageActor + currentEffect = foldEffect; + actorTransitTo = secondImageActor; + foldEffect.SetCurrentImage( firstImageActor ); + foldEffect.SetTargetImage( secondImageActor ); + foldEffect.StartTransition(); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK( callback.mSignalVerified ); + callback.Reset(); + + //check that the fold effect is used to transit to thirdImageActor + actorTransitTo = thirdImageActor; + foldEffect.SetTargetImage( thirdImageActor ); + foldEffect.StartTransition( false ); + Wait( application, TRANSITION_DURATION ); + DALI_TEST_CHECK( callback.mSignalVerified ); +} + +static void UtcDaliCubeTransitionEffectPauseResumeTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionEffectPauseResumeTransition "); + + ImageActor firstImageActor = CreateSolidColorImageActor(application, Color::RED,30,30); + ImageActor secondImageActor = CreateSolidColorImageActor(application, Color::GREEN,20,20); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + rootActor = crossEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + rootActor = crossEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + + bool signalVerified = false; + CubeTransitionEffect currentEffect; + ImageActor actorTransitTo; + TransitionCompletedCallback callback(signalVerified, currentEffect, actorTransitTo); + waveEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + crossEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + foldEffect.TransitionCompletedSignal().Connect( &callback, &TransitionCompletedCallback::Callback ); + + currentEffect = waveEffect; + actorTransitTo = secondImageActor; + waveEffect.SetCurrentImage( firstImageActor ); + waveEffect.SetTargetImage( secondImageActor ); + // start transition; transit for 0.5*duration; pause for 0.5*duration; + // resume for 0.25*duration; pause for 0.25*duration; resume for another 0.25*duration; + // only until now the transition finished signal can be received + waveEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION*0.5f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + waveEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.5f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + waveEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + waveEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + waveEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + currentEffect = crossEffect; + actorTransitTo = firstImageActor; + crossEffect.SetCurrentImage( secondImageActor ); + crossEffect.SetTargetImage( firstImageActor ); + // start transition; transit for 0.25*duration; pause for 0.2*duration; + // resume for 0.5*duration; pause for 0.2*duration; resume for another 0.25*duration; + // only until now the transition finished signal can be received + crossEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + crossEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.2f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + crossEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.5f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + crossEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.2f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + crossEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(callback.mSignalVerified); + callback.Reset(); + + currentEffect = foldEffect; + actorTransitTo = secondImageActor; + foldEffect.SetCurrentImage( firstImageActor ); + foldEffect.SetTargetImage( secondImageActor ); + // start transition; transit for 0.5*duration; pause for 0.5*duration; + // resume for 0.25*duration; pause for 0.25*duration; resume for another 0.25*duration; + // only until now the transition finished signal can be received + foldEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION*0.5f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + foldEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.5f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + foldEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + foldEffect.PauseTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(!callback.mSignalVerified); + foldEffect.ResumeTransition(); + Wait( application, TRANSITION_DURATION*0.25f ); + DALI_TEST_CHECK(callback.mSignalVerified); +} + +static void UtcDaliCubeTransitionWaveEffectStopTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionWaveEffectStopTransition "); + + ImageActor firstImageActor = CreateSolidColorImageActor(application, Color::RED,30,30); + ImageActor secondImageActor = CreateSolidColorImageActor(application, Color::GREEN,20,20); + + CubeTransitionEffect waveEffect = CubeTransitionWaveEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + waveEffect.SetTransitionDuration( TRANSITION_DURATION ); + waveEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = waveEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + Actor cube = rootActor.GetChildAt(0); + waveEffect.SetCurrentImage( firstImageActor ); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation value and color values after stopping different transitions in the middle + waveEffect.SetTargetImage( secondImageActor ); + waveEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION*0.2f ); + waveEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + waveEffect.SetTargetImage( firstImageActor ); + waveEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION*0.4f ); + waveEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + waveEffect.SetTargetImage( secondImageActor ); + waveEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION*0.6f ); + waveEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + waveEffect.SetTargetImage( firstImageActor ); + waveEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION*0.8f ); + waveEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} + +static void UtcDaliCubeTransitionCrossEffectStopTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionCrossEffectStopTransition "); + + ImageActor firstImageActor = CreateSolidColorImageActor(application, Color::RED,30,30); + ImageActor secondImageActor = CreateSolidColorImageActor(application, Color::GREEN,20,20); + + CubeTransitionEffect crossEffect = CubeTransitionCrossEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + crossEffect.SetTransitionDuration( TRANSITION_DURATION ); + crossEffect.SetCubeDisplacement( CUBE_DISPLACEMENT ); + Actor rootActor = crossEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + crossEffect.SetCurrentImage( firstImageActor ); + Actor cube0 = rootActor.GetChildAt(0); + Actor cube1 = rootActor.GetChildAt(1); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation values and color values after stop the different transitions in the middle + crossEffect.SetTargetImage( secondImageActor ); + crossEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION*0.2f ); + crossEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + crossEffect.SetTargetImage( firstImageActor ); + crossEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION*0.4f ); + crossEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 2.f*Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + crossEffect.SetTargetImage( secondImageActor ); + crossEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION*0.6f ); + crossEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + crossEffect.SetTargetImage( firstImageActor ); + crossEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION*0.8f ); + crossEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 0.f, Vector3::XAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} + +static void UtcDaliCubeTransitionFoldEffectStopTransition() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliCubeTransitionFoldEffectStopTransition "); + + ImageActor firstImageActor = CreateSolidColorImageActor(application, Color::RED,30,30); + ImageActor secondImageActor = CreateSolidColorImageActor(application, Color::GREEN,20,20); + + CubeTransitionEffect foldEffect = CubeTransitionFoldEffect::New( NUM_ROWS, NUM_COLUMNS, VIEW_AREA_SIZE ); + foldEffect.SetTransitionDuration( TRANSITION_DURATION ); + Actor rootActor = foldEffect.GetRoot(); + Stage::GetCurrent().Add( rootActor ); + foldEffect.SetCurrentImage( firstImageActor ); + Actor cube0 = rootActor.GetChildAt(0); + Actor cube1 = rootActor.GetChildAt(1); + + Vector4 fullBrightness = Vector4(1.f,1.f,1.f,1.f); + Vector4 halfBrightness = Vector4(0.5f, 0.5f, 0.5f, 1.f); + + //check the cube rotation values and color values after stop the different transitions in the middle + foldEffect.SetTargetImage( secondImageActor ); + foldEffect.StartTransition(true); + Wait( application, TRANSITION_DURATION*0.2f ); + foldEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + foldEffect.SetTargetImage( firstImageActor ); + foldEffect.StartTransition(PAN_POSITION1, PAN_DISPLACEMENT1); + Wait( application, TRANSITION_DURATION*0.4f ); + foldEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 2.f*Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); + + foldEffect.SetTargetImage( secondImageActor ); + foldEffect.StartTransition(false); + Wait( application, TRANSITION_DURATION*0.6f ); + foldEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( -Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( Math::PI_2, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == halfBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == fullBrightness ); + + foldEffect.SetTargetImage( firstImageActor ); + foldEffect.StartTransition(PAN_POSITION2, PAN_DISPLACEMENT2); + Wait( application, TRANSITION_DURATION*0.8f ); + foldEffect.StopTransition(); + application.SendNotification(); + application.Render(RENDER_FRAME_INTERVAL); + DALI_TEST_EQUALS( cube1.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_EQUALS( cube0.GetCurrentRotation(), Quaternion( 0.f, Vector3::YAXIS), ROTATION_EPSILON, TEST_LOCATION ); + DALI_TEST_CHECK( cube0.GetChildAt(0).GetCurrentColor() == fullBrightness ); + DALI_TEST_CHECK( cube0.GetChildAt(1).GetCurrentColor() == halfBrightness ); +} diff --git a/automated-tests/dali-test-suite/ui-builder/.gitignore b/automated-tests/dali-test-suite/ui-builder/.gitignore new file mode 100644 index 0000000..eaf4664 --- /dev/null +++ b/automated-tests/dali-test-suite/ui-builder/.gitignore @@ -0,0 +1 @@ +utc-Dali-UIBuilder diff --git a/automated-tests/dali-test-suite/ui-builder/file.list b/automated-tests/dali-test-suite/ui-builder/file.list new file mode 100644 index 0000000..868f5c9 --- /dev/null +++ b/automated-tests/dali-test-suite/ui-builder/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-UIBuilder \ diff --git a/automated-tests/dali-test-suite/utc-MODULE-CLASS.cpp.in b/automated-tests/dali-test-suite/utc-MODULE-CLASS.cpp.in new file mode 100644 index 0000000..e782026 --- /dev/null +++ b/automated-tests/dali-test-suite/utc-MODULE-CLASS.cpp.in @@ -0,0 +1,96 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +static void Utc@MODULE@@CLASS@Method01(); +static void Utc@MODULE@@CLASS@Method02(); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +// Add test functionality for all APIs in the class (Positive and Negative) +extern "C" { + struct tet_testlist tet_testlist[] = { + { Utc@MODULE@@CLASS@Method01, POSITIVE_TC_IDX }, + { Utc@MODULE@@CLASS@Method02, NEGATIVE_TC_IDX }, + { NULL, 0 } + }; +} + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + + +// Positive test case for a method +static void Utc@MODULE@@CLASS@Method01() +{ + ToolkitTestApplication application; + + tet_infoline("Journaled printf Output"); + tet_result(TET_FAIL); +#if 0 + tet_result(TET_PASS); +#endif +} + + +// Negative test case for a method +static void Utc@MODULE@@CLASS@Method02() +{ + ToolkitTestApplication application; // Exceptions require ToolkitTestApplication + + try + { + /* My test code and results */ + DALI_TEST_EQUALS(myVar, expectedValue, TEST_LOCATION); + } + catch (Dali::DaliException& e) + { + // Tests that a negative test of an assertion succeeds + tet_printf("Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS(e.mCondition, "assert conditional", TEST_LOCATION); + } +} + diff --git a/automated-tests/dali-test-suite/view/.gitignore b/automated-tests/dali-test-suite/view/.gitignore new file mode 100644 index 0000000..4960a6d --- /dev/null +++ b/automated-tests/dali-test-suite/view/.gitignore @@ -0,0 +1 @@ +utc-Dali-View diff --git a/automated-tests/dali-test-suite/view/Makefile b/automated-tests/dali-test-suite/view/Makefile new file mode 120000 index 0000000..c88d5a7 --- /dev/null +++ b/automated-tests/dali-test-suite/view/Makefile @@ -0,0 +1 @@ +../master-makefile.mk \ No newline at end of file diff --git a/automated-tests/dali-test-suite/view/file.list b/automated-tests/dali-test-suite/view/file.list new file mode 100644 index 0000000..46616fb --- /dev/null +++ b/automated-tests/dali-test-suite/view/file.list @@ -0,0 +1,2 @@ +TARGETS += \ + utc-Dali-View \ diff --git a/automated-tests/dali-test-suite/view/tslist b/automated-tests/dali-test-suite/view/tslist new file mode 100644 index 0000000..5a8a0c3 --- /dev/null +++ b/automated-tests/dali-test-suite/view/tslist @@ -0,0 +1 @@ +/dali-test-suite/view/utc-Dali-View diff --git a/automated-tests/dali-test-suite/view/utc-Dali-View.cpp b/automated-tests/dali-test-suite/view/utc-Dali-View.cpp new file mode 100644 index 0000000..5241976 --- /dev/null +++ b/automated-tests/dali-test-suite/view/utc-Dali-View.cpp @@ -0,0 +1,413 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace Dali; +using namespace Toolkit; + +namespace +{ + +static bool gAnimationStarted = false; + +void StartAnimation( View, Animation& animation, const Orientation& orientation ) +{ + gAnimationStarted = true; +} + + +static bool gObjectCreatedCallBackCalled; +static void TestCallback(BaseHandle handle) +{ + gObjectCreatedCallBackCalled = true; +} + + +} + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +// Add test functionality for all APIs in the class (Positive and Negative) +TEST_FUNCTION( UtcDaliViewNew, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewAddGetRemoveContentLayer01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewAddGetRemoveContentLayer02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewSetGetBackgroundLayer01, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewSetGetBackgroundLayer02, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewSetOrientationFunction, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliViewOrientationChanged, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcSetAutoRotate, POSITIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliViewNew() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewNew"); + + View view1; + DALI_TEST_CHECK( !view1 ); + + view1 = View::New(); + DALI_TEST_CHECK( view1 ); + + View view2( view1 ); + DALI_TEST_CHECK( view2 ); + + View view3 = view2; + DALI_TEST_CHECK( view3 ); + + view1 = NULL; + view2 = NULL; + view3 = NULL; + + //Additional check to ensure object is created by checking if it's registered + ObjectRegistry registry = Stage::GetCurrent().GetObjectRegistry(); + DALI_TEST_CHECK( registry ); + + gObjectCreatedCallBackCalled = false; + registry.ObjectCreatedSignal().Connect( &TestCallback ); + { + View view = View::New(); + } + DALI_TEST_CHECK( gObjectCreatedCallBackCalled ); +} + +static void UtcDaliViewAddGetRemoveContentLayer01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewAddGetRemoveContentLayer01"); + + View view = View::New(); + Layer layer1; + Layer layer2; + Layer layer3; + Layer layer4; + + // Test: add and get layers. + try + { + layer1 = Layer::New(); + layer1.SetName( "Layer1" ); + layer2 = Layer::New(); + layer2.SetName( "Layer2" ); + + unsigned int layerId1 = view.AddContentLayer( layer1 ); + unsigned int layerId2 = view.AddContentLayer( layer2 ); + + layer3 = view.GetContentLayer( layerId1 ); + layer4 = view.GetContentLayer( layerId2 ); + + DALI_TEST_EQUALS( layer1.GetName(), layer3.GetName(), TEST_LOCATION ); + DALI_TEST_EQUALS( layer2.GetName(), layer4.GetName(), TEST_LOCATION ); + } + catch( ... ) + { + tet_printf( "UtcDaliViewAddGetRemoveContentLayer: Exception while adding and geting layers to/from view.\n" ); + tet_result(TET_FAIL); + } + + bool test1 = false; + bool test2 = false; + // Test: remove layers. + try + { + view.RemoveContentLayer( layer3 ); + view.RemoveContentLayer( layer4 ); + test1 = true; + } + catch( ... ) + { + tet_printf( "UtcDaliViewAddGetRemoveContentLayer: Exception while removing layers from view.\n" ); + tet_result(TET_FAIL); + } + + // Test: add same layers again. + try + { + view.AddContentLayer( layer1 ); + view.AddContentLayer( layer2 ); + test2 = true; + } + catch( ... ) + { + tet_printf( "UtcDaliViewAddGetRemoveContentLayer: Exception while adding layers from view after have been removed.\n" ); + tet_result(TET_FAIL); + } + + DALI_TEST_CHECK( test1 && test2 ); +} + +static void UtcDaliViewAddGetRemoveContentLayer02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewAddGetRemoveContentLayer02"); + + View view = View::New(); + + Layer layer1 = Layer::New(); + layer1.SetName( "Layer1" ); + Layer layer2 = Layer::New(); + layer2.SetName( "Layer2" ); + + view.AddContentLayer( layer1 ); + view.AddContentLayer( layer2 ); + + // Test: add a layer twice. + try + { + view.AddContentLayer( layer1 ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + // Test: add an unitialized layer. + try + { + Layer layer; + view.AddContentLayer( layer ); + } + catch( DaliException& e ) + { + tet_printf("Assertion %s failed at %s when an unitialized layer is added.\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS( e.mCondition, "layer", TEST_LOCATION ); + } + + // Test: get a layer which was not added before. + Layer layer = view.GetContentLayer( 100 ); + DALI_TEST_CHECK( !layer ); + + // Test: Remove a layer which was not added before. + try + { + Layer layer = Layer::New(); + view.RemoveContentLayer( layer ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} + +static void UtcDaliViewSetGetBackgroundLayer01() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewSetGetBackgroundLayer01"); + + View view; + Layer layer1, layer2; + + // Test with an actor. + + view = View::New(); + Stage::GetCurrent().Add( view ); + + ImageActor background = CreateSolidColorActor( Color::RED ); + + view.SetBackground( background ); + + layer1 = view.GetBackgroundLayer(); + + DALI_TEST_CHECK( layer1 ); + + background = CreateSolidColorActor( Color::GREEN ); + + view.SetBackground( background ); + + layer2 = view.GetBackgroundLayer(); + + DALI_TEST_CHECK( layer2 ); + + Stage::GetCurrent().Remove( view ); +} + +static void UtcDaliViewSetGetBackgroundLayer02() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewSetGetBackgroundLayer02"); + + bool assert = false; + + try + { + View view = View::New(); + + ImageActor background = CreateSolidColorActor( Color::RED ); + + view.SetBackground( background ); + } + catch( DaliException& e ) + { + tet_printf("Assertion %s failed at %s when trying to add background to the view and the view is not on the stage.\n", e.mCondition.c_str(), e.mLocation.c_str()); + DALI_TEST_EQUALS( e.mCondition, "mBackgroundLayer.OnStage()", TEST_LOCATION ); + assert = true; + } + + DALI_TEST_CHECK( assert ); +} + +static void UtcDaliViewSetOrientationFunction() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewSetOrientationFunction"); + + // Test it doesn't crash + try + { + View view = View::New(); + Stage::GetCurrent().Add( view ); + + view.SetSize( 480, 800 ); + view.SetOrientationFunction( Degree( 0.f ), Degree( 90.f ), Degree( 180.f ), Degree( 270.f ) ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); +} + +static void UtcDaliViewOrientationChanged() +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliViewOrientationChanged"); + + gAnimationStarted = false; + + // Test it doesn't crash + try + { + View view = View::New(); + Stage::GetCurrent().Add( view ); + + view.SetSize( 480, 800 ); + + view.OrientationAnimationStartedSignal().Connect( &StartAnimation ); + + application.SendNotification(); // Remove these two lines causes + application.Render(); // ToolkitTestApplication destructor to crash + + Orientation orientation = application.GetOrientation().GetHandle(); + application.GetOrientation().SetDegrees( 90 ); + view.OrientationChanged( orientation ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + // Check the view animation started. + DALI_TEST_CHECK( gAnimationStarted ); +} + +static void UtcSetAutoRotate() +{ + ToolkitTestApplication application; + tet_infoline(" UtcSetAutoRotate"); + + gAnimationStarted = false; + + View view; + + // Test it doesn't crash + try + { + view = View::New(); + Stage::GetCurrent().Add( view ); + + view.SetSize( 480, 800 ); + + view.OrientationAnimationStartedSignal().Connect( &StartAnimation ); + + application.SendNotification(); + application.Render(); + + Orientation orientation = application.GetOrientation().GetHandle(); + application.GetOrientation().SetDegrees( 90 ); + view.OrientationChanged( orientation ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + // Check the view animation started. + DALI_TEST_CHECK( gAnimationStarted ); + + + gAnimationStarted = false; + + try + { + view = View::New(); + view.SetAutoRotate( false ); // Animation shouldn't start. + Stage::GetCurrent().Add( view ); + + view.SetSize( 480, 800 ); + + application.SendNotification(); + application.Render(); + + Orientation orientation = application.GetOrientation().GetHandle(); + application.GetOrientation().SetDegrees( 180 ); + view.OrientationChanged( orientation ); + } + catch( ... ) + { + tet_result(TET_FAIL); + } + + // Check the view animation didn't start. + DALI_TEST_CHECK( !gAnimationStarted ); +} diff --git a/automated-tests/dali-toolkit-test-utils/dali-toolkit-test-suite-utils.h b/automated-tests/dali-toolkit-test-utils/dali-toolkit-test-suite-utils.h new file mode 100644 index 0000000..ca21543 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/dali-toolkit-test-suite-utils.h @@ -0,0 +1,26 @@ +#ifndef __DALI_TOOLKIT_TEST_SUITE_UTILS_H__ +#define __DALI_TOOLKIT_TEST_SUITE_UTILS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include "toolkit-test-application.h" +#include "toolkit-application.h" + +#endif // __DALI_TOOLKIT_TEST_SUITE_UTILS_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.cpp new file mode 100644 index 0000000..497f53c --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.cpp @@ -0,0 +1,212 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-accessibility-manager.h" + +#include +#include +#include + +namespace Dali +{ + +namespace +{ +ToolkitAccessibilityManager* gToolkitAccessibilityManager(NULL); +} // unnamed namespace + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the AccessibilityManager + */ +class AccessibilityManager : public BaseObject +{ +public: // Creation & Destruction + + static Dali::AccessibilityManager Get(); + + AccessibilityManager(); + AccessibilityManager(ToolkitAccessibilityManager *accessibilityActionDetector); + ~AccessibilityManager(); + +public: + + bool IsEnabled() const; + void SetActionHandler(Dali::AccessibilityActionHandler& handler); + void SetGestureHandler(Dali::AccessibilityGestureHandler& handler); + +public: // Signals + + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalStatusChanged(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionNext(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionPrevious(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionActivate(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionRead(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionReadNext(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionReadPrevious(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionUp(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionDown(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionClearFocus(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionBack(); + Dali::AccessibilityManager::AccessibilityActionSignalV2& SignalActionControlPanelOpen(); + +private: + + ToolkitAccessibilityManager* mToolkitAccessibilityManager; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mStatusChangedSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionNextSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionPreviousSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionActivateSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionReadSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionReadNextSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionReadPreviousSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionUpSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionDownSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionClearFocusSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionBackSignal; + Dali::AccessibilityManager::AccessibilityActionSignalV2 mActionControlPanelOpenSignal; + + bool mIsEnabled; + Dali::AccessibilityActionHandler* mActionHandler; + Dali::AccessibilityGestureHandler* mGestureHandler; + +}; + +Dali::AccessibilityManager AccessibilityManager::Get() +{ + return gToolkitAccessibilityManager->GetAccessibilityManager(); +} + +AccessibilityManager::AccessibilityManager() +: mToolkitAccessibilityManager(NULL), + mIsEnabled(false) +{ +} + +AccessibilityManager::AccessibilityManager(ToolkitAccessibilityManager *accessibilityActionDetector) +: mToolkitAccessibilityManager(accessibilityActionDetector), + mIsEnabled(false) +{ +} + +AccessibilityManager::~AccessibilityManager() +{ +} + +bool AccessibilityManager::IsEnabled() const +{ + return mIsEnabled; +} + +void AccessibilityManager::SetActionHandler(Dali::AccessibilityActionHandler& handler) +{ + mActionHandler = &handler; +} + +void AccessibilityManager::SetGestureHandler(Dali::AccessibilityGestureHandler& handler) +{ + mGestureHandler = &handler; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalStatusChanged() +{ + return mStatusChangedSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionNext() +{ + return mActionNextSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionPrevious() +{ + return mActionPreviousSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionActivate() +{ + return mActionActivateSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionRead() +{ + return mActionReadSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionReadNext() +{ + return mActionReadNextSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionReadPrevious() +{ + return mActionReadPreviousSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionUp() +{ + return mActionUpSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionDown() +{ + return mActionDownSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionClearFocus() +{ + return mActionClearFocusSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionBack() +{ + return mActionBackSignal; +} + +Dali::AccessibilityManager::AccessibilityActionSignalV2& AccessibilityManager::SignalActionControlPanelOpen() +{ + return mActionControlPanelOpenSignal; +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitAccessibilityManager::ToolkitAccessibilityManager() +: mAccessibilityManagerStub(new Internal::Adaptor::AccessibilityManager(this)), + mAccessibilityManager( mAccessibilityManagerStub ) +{ + gToolkitAccessibilityManager = this; +} + +ToolkitAccessibilityManager::~ToolkitAccessibilityManager() +{ + gToolkitAccessibilityManager = NULL; +} + +AccessibilityManager ToolkitAccessibilityManager::GetAccessibilityManager() +{ + return mAccessibilityManager; +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.h b/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.h new file mode 100644 index 0000000..1e0f708 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-accessibility-manager.h @@ -0,0 +1,75 @@ +#ifndef __DALI_TOOLKIT_ACCESSIBILITY_MANAGER_H__ +#define __DALI_TOOLKIT_ACCESSIBILITY_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// PUBLIC INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class AccessibilityManager; +} +} + +/** + * This creates a stubbed AccessibilityManager so that internal Toolkit Adaptor calls work. + */ +class ToolkitAccessibilityManager +{ +public: // Construction & Destruction + + ToolkitAccessibilityManager(); + ~ToolkitAccessibilityManager(); + +public: // Getters + + AccessibilityManager GetAccessibilityManager(); + +public: // Signal Emissions + + AccessibilityManager::AccessibilityActionSignalV2& SignalStatusChanged(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionNext(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionPrevious(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionActivate(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionRead(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionReadNext(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionReadPrevious(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionUp(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionDown(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionClearFocus(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionBack(); + AccessibilityManager::AccessibilityActionSignalV2& SignalActionControlPanelOpen(); + +private: + + Internal::Adaptor::AccessibilityManager* mAccessibilityManagerStub; + friend class Internal::Adaptor::AccessibilityManager; + AccessibilityManager mAccessibilityManager; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_ACCESSIBILITY_MANAGER_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.cpp new file mode 100644 index 0000000..5cc41af --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.cpp @@ -0,0 +1,249 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-adaptor.h" + +#include +#include +#include + +namespace Dali +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Stub for RenderSurface + */ +class RenderSurface +{ +}; + +typedef Dali::Rect PositionSize; + +/** + * Stub for the Adaptor + */ +class Adaptor +{ +public: + + typedef SignalV2< void ( Adaptor& ) > AdaptorSignalV2; + + typedef std::pair SingletonPair; + typedef std::map SingletonContainer; + typedef SingletonContainer::const_iterator SingletonConstIter; + +public: + + Adaptor(ToolkitAdaptor& toolkitAdaptor); + ~Adaptor(); + +public: + + void Start(); + void Pause(); + void Resume(); + void Stop(); + bool AddIdle(boost::function callBack); + void FeedEvent(TouchPoint& point, int timeStamp); + bool MoveResize(const PositionSize& positionSize); + void SurfaceResized(const PositionSize& positionSize); + void ReplaceSurface(RenderSurface& surface); + void RenderSync(); + RenderSurface& GetSurface(); + + void RegisterSingleton(const std::type_info& info, Dali::BaseHandle singleton); + Dali::BaseHandle GetSingleton(const std::type_info& info) const; + +public: // static methods + static Adaptor& Get(); + static bool IsAvailable(); + +public: // Signals + + AdaptorSignalV2& SignalResize(); + + void EmitSignalResize() + { + mResizeSignal.Emit( *this ); + } + +private: + + // Undefined + Adaptor(const Adaptor&); + Adaptor& operator=(Adaptor&); + + AdaptorSignalV2 mResizeSignal; + RenderSurface mRenderSurface; + ToolkitAdaptor& mToolkitAdaptor; + + SingletonContainer mSingletonContainer; +}; + +namespace +{ +Adaptor* gAdaptor = NULL; + +} + +Adaptor::Adaptor(ToolkitAdaptor& toolkitAdaptor) +: mToolkitAdaptor(toolkitAdaptor) +{ +} + +Adaptor::~Adaptor() +{ + +} + +void Adaptor::Start() +{ + mToolkitAdaptor.mFunctionsCalled.Start = true; +} + +void Adaptor::Pause() +{ + mToolkitAdaptor.mFunctionsCalled.Pause = true; +} + +void Adaptor::Resume() +{ + mToolkitAdaptor.mFunctionsCalled.Resume = true; +} + +void Adaptor::Stop() +{ + mToolkitAdaptor.mFunctionsCalled.Stop = true; +} + +bool Adaptor::AddIdle(boost::function callBack) +{ + mToolkitAdaptor.mFunctionsCalled.AddIdle = true; + mToolkitAdaptor.mLastIdleAdded = callBack; + return true; +} + +void Adaptor::FeedEvent(TouchPoint& point, int timeStamp) +{ + mToolkitAdaptor.mFunctionsCalled.FeedEvent = true; + mToolkitAdaptor.mLastTouchPointFed = point; + mToolkitAdaptor.mLastTimeStampFed = timeStamp; +} + +bool Adaptor::MoveResize(const PositionSize& positionSize) +{ + mToolkitAdaptor.mFunctionsCalled.MoveResize = true; + mToolkitAdaptor.mLastSizeSet = positionSize; + return true; +} + +void Adaptor::SurfaceResized(const PositionSize& positionSize) +{ + mToolkitAdaptor.mFunctionsCalled.SurfaceResized = true; + mToolkitAdaptor.mLastSizeSet = positionSize; +} + +void Adaptor::ReplaceSurface(RenderSurface& surface) +{ + mToolkitAdaptor.mFunctionsCalled.ReplaceSurface = true; +} + +void Adaptor::RenderSync() +{ + mToolkitAdaptor.mFunctionsCalled.RenderSync = true; +} + +RenderSurface& Adaptor::GetSurface() +{ + mToolkitAdaptor.mFunctionsCalled.GetSurface = true; + return mRenderSurface; +} + +Adaptor& Adaptor::Get() +{ + DALI_ASSERT_ALWAYS(gAdaptor); + gAdaptor->mToolkitAdaptor.mFunctionsCalled.Get = true; + return *gAdaptor; +} + +bool Adaptor::IsAvailable() +{ + bool available(false); + + if (gAdaptor) + { + gAdaptor->mToolkitAdaptor.mFunctionsCalled.IsAvailable = true; + available = true; + } + + return available; +} + +void Adaptor::RegisterSingleton(const std::type_info& info, Dali::BaseHandle singleton) +{ + mToolkitAdaptor.mFunctionsCalled.RegisterSingleton = true; + + if(singleton) + { + mSingletonContainer.insert(SingletonPair(info.name(), singleton)); + } +} + +Dali::BaseHandle Adaptor::GetSingleton(const std::type_info& info) const +{ + mToolkitAdaptor.mFunctionsCalled.GetSingleton = true; + + Dali::BaseHandle object = Dali::BaseHandle(); + + SingletonConstIter iter = mSingletonContainer.find(info.name()); + if(iter != mSingletonContainer.end()) + { + object = (*iter).second; + } + + return object; +} + +Adaptor::AdaptorSignalV2& Adaptor::SignalResize() +{ + mToolkitAdaptor.mFunctionsCalled.SignalResize = true; + return mResizeSignal; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitAdaptor::ToolkitAdaptor() +: mLastTouchPointFed(0, TouchPoint::Down, 0.0f, 0.0f), + mLastTimeStampFed(0), + mAdaptorStub(new Adaptor(*this)) +{ + gAdaptor = mAdaptorStub; +} + +ToolkitAdaptor::~ToolkitAdaptor() +{ + delete mAdaptorStub; + gAdaptor = NULL; +} + +void ToolkitAdaptor::EmitSignalResize() +{ + mAdaptorStub->EmitSignalResize(); +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.h b/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.h new file mode 100644 index 0000000..e1acdd7 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-adaptor.h @@ -0,0 +1,259 @@ +#ifndef __DALI_TOOLKIT_TOOLKIT_ADAPTOR_H__ +#define __DALI_TOOLKIT_TOOLKIT_ADAPTOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +#include "toolkit-style-monitor.h" +#include "toolkit-accessibility-manager.h" +#include "toolkit-physical-keyboard.h" +#include "toolkit-clipboard-event-notifier.h" +#include "toolkit-timer.h" + +namespace Dali +{ + +class Adaptor; + +typedef Dali::Rect PositionSize; + +/** + * This creates a stubbed Adaptor so that internal Toolkit Adaptor calls work. + * Furthermore, it provides an interface to see if certain methods were invoked. + */ +class ToolkitAdaptor +{ +public: // Construction & Destruction + + ToolkitAdaptor(); + ~ToolkitAdaptor(); + +public: // Getters + + boost::function GetLastIdleAdded() const + { + return mLastIdleAdded; + } + + TouchPoint GetLastTouchPointFed() const + { + return mLastTouchPointFed; + } + + int GetLastTimeStampFed() const + { + return mLastTimeStampFed; + } + + PositionSize GetLastSizeSet() const + { + return mLastSizeSet; + } + + ToolkitStyleMonitor& GetToolkitStyleMonitor() + { + return mStyleMonitor; + } + + ToolkitAccessibilityManager& GetAccessibilityManager() + { + return mAccessibilityManager; + } + + ToolkitClipboardEventNotifier& GetClipboardEventNotifier() + { + return mClipboardEventNotifier; + } + +public: // Signal Emissions + + void EmitSignalResize(); + +public: // TEST FUNCTIONS + + // Enumeration of Adaptor methods + enum TestFuncEnum + { + StartType, + PauseType, + ResumeType, + StopType, + AddIdleType, + FeedEventType, + MoveResizeType, + SurfaceResizedType, + ReplaceSurfaceType, + RenderSyncType, + GetSurfaceType, + GetType, + IsAvailableType, + RegisterSingletonType, + GetSingletonType, + SignalResizeType, + }; + + void Reset() + { + mFunctionsCalled.Reset(); + } + + bool WasCalled(TestFuncEnum func) + { + switch(func) + { + case StartType: return mFunctionsCalled.Start; + case PauseType: return mFunctionsCalled.Pause; + case ResumeType: return mFunctionsCalled.Resume; + case StopType: return mFunctionsCalled.Stop; + case AddIdleType: return mFunctionsCalled.AddIdle; + case FeedEventType: return mFunctionsCalled.FeedEvent; + case MoveResizeType: return mFunctionsCalled.MoveResize; + case SurfaceResizedType: return mFunctionsCalled.SurfaceResized; + case ReplaceSurfaceType: return mFunctionsCalled.ReplaceSurface; + case RenderSyncType: return mFunctionsCalled.RenderSync; + case GetSurfaceType: return mFunctionsCalled.GetSurface; + case GetType: return mFunctionsCalled.Get; + case IsAvailableType: return mFunctionsCalled.IsAvailable; + case RegisterSingletonType: return mFunctionsCalled.RegisterSingleton; + case GetSingletonType: return mFunctionsCalled.GetSingleton; + case SignalResizeType: return mFunctionsCalled.SignalResize; + } + return false; + } + + void ResetCallStatistics(TestFuncEnum func) + { + switch(func) + { + case StartType: mFunctionsCalled.Start = false; break; + case PauseType: mFunctionsCalled.Pause = false; break; + case ResumeType: mFunctionsCalled.Resume = false; break; + case StopType: mFunctionsCalled.Stop = false; break; + case AddIdleType: mFunctionsCalled.AddIdle = false; break; + case FeedEventType: mFunctionsCalled.FeedEvent = false; break; + case MoveResizeType: mFunctionsCalled.MoveResize = false; break; + case SurfaceResizedType: mFunctionsCalled.SurfaceResized = false; break; + case ReplaceSurfaceType: mFunctionsCalled.ReplaceSurface = false; break; + case RenderSyncType: mFunctionsCalled.RenderSync = false; break; + case GetSurfaceType: mFunctionsCalled.GetSurface = false; break; + case GetType: mFunctionsCalled.Get = false; break; + case IsAvailableType: mFunctionsCalled.IsAvailable = false; break; + case RegisterSingletonType: mFunctionsCalled.RegisterSingleton = false; break; + case GetSingletonType: mFunctionsCalled.GetSingleton = false; break; + case SignalResizeType: mFunctionsCalled.SignalResize = false; break; + } + } + +private: + + struct TestFunctions + { + TestFunctions() + : Start(false), + Pause(false), + Resume(false), + Stop(false), + AddIdle(false), + FeedEvent(false), + MoveResize(false), + SurfaceResized(false), + ReplaceSurface(false), + RenderSync(false), + GetSurface(false), + Get(false), + IsAvailable(false), + RegisterSingleton(false), + GetSingleton(false), + SignalResize(false) + { + } + + void Reset() + { + Start = false; + Pause = false; + Resume = false; + Stop = false; + AddIdle = false; + FeedEvent = false; + MoveResize = false; + SurfaceResized = false; + ReplaceSurface = false; + RenderSync = false; + GetSurface = false; + Get = false; + IsAvailable = false; + RegisterSingleton = false; + GetSingleton = false; + SignalResize = false; + } + + bool Start; + bool Pause; + bool Resume; + bool Stop; + bool AddIdle; + bool FeedEvent; + bool MoveResize; + bool SurfaceResized; + bool ReplaceSurface; + bool RenderSync; + bool GetSurface; + bool Get; + bool IsAvailable; + bool RegisterSingleton; + bool GetSingleton; + bool SignalResize; + }; + + TestFunctions mFunctionsCalled; + + // Last set information + boost::function mLastIdleAdded; + TouchPoint mLastTouchPointFed; + int mLastTimeStampFed; + PositionSize mLastSizeSet; + + // Contains Test functions for the Style Monitor + ToolkitStyleMonitor mStyleMonitor; + + // Stub for Timer + ToolkitTimer mToolkitTimer; + + // Stub for AccessibilityManager + ToolkitAccessibilityManager mAccessibilityManager; + + // Stub for PhysicalKeyboard + ToolkitPhysicalKeyboard mPhysicalKeyboard; + + // Stub for ClipboardEventNotifier + ToolkitClipboardEventNotifier mClipboardEventNotifier; + + // The Adaptor Stub + Adaptor* mAdaptorStub; + friend class Adaptor; +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_ADAPTOR_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-application.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-application.cpp new file mode 100644 index 0000000..d616efe --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-application.cpp @@ -0,0 +1,103 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-application.h" + +#include + +#include +#include +#include + +namespace Dali +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** + * Stub for the Application + */ +class Application +{ +public: + +public: + + Application(ToolkitApplication& toolkitApplication); + ~Application(); + +public: + + Orientation& GetOrientation(); + +public: // static methods + +public: // Signals + +private: + + // Undefined + Application(const Application&); + Application& operator=(Application&); + + ToolkitApplication& mToolkitApplication; + + Dali::Orientation* mOrientation; +}; + +namespace +{ +Application* gApplication = NULL; +} + +Application::Application(ToolkitApplication& toolkitApplication) +: mToolkitApplication(toolkitApplication), + mOrientation( new Dali::Orientation() ) +{ +} + +Application::~Application() +{ + delete mOrientation; +} + +Orientation& Application::GetOrientation() +{ + return *mOrientation; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitApplication::ToolkitApplication() +: mApplicationStub(new Application(*this)) +{ + gApplication = mApplicationStub; +} + +ToolkitApplication::~ToolkitApplication() +{ + delete mApplicationStub; + gApplication = NULL; +} + +Application& ToolkitApplication::GetApplication() +{ + DALI_ASSERT_ALWAYS(gApplication); + return *gApplication; +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-application.h b/automated-tests/dali-toolkit-test-utils/toolkit-application.h new file mode 100644 index 0000000..8b376a0 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-application.h @@ -0,0 +1,91 @@ +#ifndef __DALI_TOOLKIT_TOOLKIT_APPLICATION_H__ +#define __DALI_TOOLKIT_TOOLKIT_APPLICATION_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +namespace Dali +{ + +class Application; + +/** + * This creates a stubbed Application so that Application calls work. + * Furthermore, it provides an interface to see if certain methods were invoked. + */ +class ToolkitApplication +{ +public: // Construction & Destruction + + ToolkitApplication(); + ~ToolkitApplication(); + +public: // Getters + + Application& GetApplication(); + +public: // Signal Emissions + +public: // TEST FUNCTIONS + + // Enumeration of Application methods + enum TestFuncEnum + { + }; + + void Reset() + { + mFunctionsCalled.Reset(); + } + + bool WasCalled(TestFuncEnum func) + { + switch(func) + { + } + return false; + } + + void ResetCallStatistics(TestFuncEnum func) + { + switch(func) + { + } + } + +private: + + struct TestFunctions + { + TestFunctions() + { + } + + void Reset() + { + } + }; + + TestFunctions mFunctionsCalled; + + // The Application Stub + Application* mApplicationStub; + friend class Application; +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_APPLICATION_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.cpp new file mode 100644 index 0000000..168625e --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.cpp @@ -0,0 +1,106 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-clipboard-event-notifier.h" + +#include +#include +#include + +namespace Dali +{ + +namespace +{ +ToolkitClipboardEventNotifier* gToolkitClipboardEventNotifier(NULL); +} // unnamed namespace + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the ClipboardEventNotifier + */ +class ClipboardEventNotifier : public BaseObject +{ +public: // Creation & Destruction + + static Dali::ClipboardEventNotifier Get(); + + ClipboardEventNotifier(); + ClipboardEventNotifier(ToolkitClipboardEventNotifier *clipboardEventNotifier); + ~ClipboardEventNotifier(); + +public: // Signals + + Dali::ClipboardEventNotifier::ClipboardEventSignalV2& SignalContentSelected() + { + return mClipboardSignal; + } + +private: + + ToolkitClipboardEventNotifier* mToolkitClipboardEventNotifier; + Dali::ClipboardEventNotifier::ClipboardEventSignalV2 mClipboardSignal; + +}; + +Dali::ClipboardEventNotifier ClipboardEventNotifier::Get() +{ + return gToolkitClipboardEventNotifier->GetClipboardEventNotifier(); +} + +ClipboardEventNotifier::ClipboardEventNotifier() +: mToolkitClipboardEventNotifier(NULL) +{ +} + +ClipboardEventNotifier::ClipboardEventNotifier(ToolkitClipboardEventNotifier *clipboardEventNotifier) +: mToolkitClipboardEventNotifier(clipboardEventNotifier) +{ +} + +ClipboardEventNotifier::~ClipboardEventNotifier() +{ +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitClipboardEventNotifier::ToolkitClipboardEventNotifier() +: mClipboardEventNotifierStub(new Internal::Adaptor::ClipboardEventNotifier(this)), + mClipboardEventNotifier( mClipboardEventNotifierStub ) +{ + gToolkitClipboardEventNotifier = this; +} + +ToolkitClipboardEventNotifier::~ToolkitClipboardEventNotifier() +{ + gToolkitClipboardEventNotifier = NULL; +} + +ClipboardEventNotifier ToolkitClipboardEventNotifier::GetClipboardEventNotifier() +{ + return mClipboardEventNotifier; +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.h b/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.h new file mode 100644 index 0000000..4cb9a98 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_CLIPBOARD_EVENT_NOTIFIER_H__ +#define __DALI_TOOLKIT_CLIPBOARD_EVENT_NOTIFIER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// PUBLIC INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class ClipboardEventNotifier; +} +} + +/** + * This creates a stubbed ClipboardEventNotifier so that internal Toolkit Adaptor calls work. + */ +class ToolkitClipboardEventNotifier +{ +public: // Constants + +public: // Construction & Destruction + + ToolkitClipboardEventNotifier(); + ~ToolkitClipboardEventNotifier(); + +public: // Getters + + ClipboardEventNotifier GetClipboardEventNotifier(); + +public: // Signal Emissions + + ClipboardEventNotifier::ClipboardEventSignalV2& SignalContentSelected(); + +public: // TEST FUNCTIONS + +private: + + Internal::Adaptor::ClipboardEventNotifier* mClipboardEventNotifierStub; + friend class Internal::Adaptor::ClipboardEventNotifier; + ClipboardEventNotifier mClipboardEventNotifier; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_CLIPBOARD_EVENT_NOTIFIER_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-orientation.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-orientation.cpp new file mode 100644 index 0000000..62598d3 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-orientation.cpp @@ -0,0 +1,143 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-orientation.h" + +#include +#include +#include + +namespace Dali +{ + +namespace +{ +ToolkitOrientation* gToolkitOrientation(NULL); +} // unnamed namespace + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the Orientation + */ +class Orientation : public BaseObject +{ +public: // Creation & Destruction + + Orientation(); + Orientation(ToolkitOrientation *orientation); + ~Orientation(); + +public: // Setters & Getters + + void SetDegrees( int degrees ) + { + mOrientation = degrees; + } + + int GetDegrees() const; + float GetRadians() const; + +public: // Signals + + Dali::Orientation::OrientationSignalV2& ChangedSignal(); + + void EmitChangedSignal() + { + mChangedSignal.Emit(Dali::Orientation(this)); + } + +private: + + Dali::Orientation::OrientationSignalV2 mChangedSignal; + + ToolkitOrientation* mToolkitOrientation; + + int mOrientation; +}; + +Orientation::Orientation() +: mToolkitOrientation(NULL), + mOrientation(0) +{ +} + +Orientation::Orientation(ToolkitOrientation *orientation) +: mToolkitOrientation(orientation), + mOrientation(0) +{ +} + +Orientation::~Orientation() +{ +} + +int Orientation::GetDegrees() const +{ + mToolkitOrientation->mFunctionsCalled.GetDegrees = true; + return mOrientation; +} + +float Orientation::GetRadians() const +{ + mToolkitOrientation->mFunctionsCalled.GetRadians = true; + return Math::PI * (float)mOrientation / 180.0f; +} + +Dali::Orientation::OrientationSignalV2& Orientation::ChangedSignal() +{ + mToolkitOrientation->mFunctionsCalled.ChangedSignal = true; + return mChangedSignal; +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitOrientation::ToolkitOrientation() +: mOrientationStub(new Internal::Adaptor::Orientation(this)), + mOrientation( mOrientationStub ) +{ + gToolkitOrientation = this; +} + +ToolkitOrientation::~ToolkitOrientation() +{ + gToolkitOrientation = NULL; +} + +Orientation ToolkitOrientation::GetHandle() +{ + return mOrientation; +} + +void ToolkitOrientation::SetDegrees( int degrees ) +{ + mOrientationStub->SetDegrees( degrees ); +} + +void ToolkitOrientation::EmitChangedSignal() +{ + mOrientationStub->EmitChangedSignal(); +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-orientation.h b/automated-tests/dali-toolkit-test-utils/toolkit-orientation.h new file mode 100644 index 0000000..31c31f1 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-orientation.h @@ -0,0 +1,129 @@ +#ifndef __DALI_TOOLKIT_TOOLKIT_ORIENTATION_H__ +#define __DALI_TOOLKIT_TOOLKIT_ORIENTATION_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class Orientation; +} +} + +/** + * This creates a stubbed Orientation so that internal Toolkit Adaptor calls work. + * Furthermore, it provides an interface to see if certain methods were invoked. + */ +class ToolkitOrientation +{ +public: // Construction & Destruction + + ToolkitOrientation(); + ~ToolkitOrientation(); + +public: // Getters + + Orientation GetHandle(); + +public: // Setters + + void SetDegrees( int degrees ); + +public: // Signal Emissions + + void EmitChangedSignal(); + +public: // TEST FUNCTIONS + + // Enumeration of Adaptor methods + enum TestFuncEnum + { + GetDegrees, + GetRadians, + ChangedSignal, + }; + + void Reset() + { + mFunctionsCalled.Reset(); + } + + bool WasCalled(TestFuncEnum func) + { + switch(func) + { + case GetDegrees: return mFunctionsCalled.GetDegrees; + case GetRadians: return mFunctionsCalled.GetRadians; + case ChangedSignal: return mFunctionsCalled.ChangedSignal; + } + return false; + } + + void ResetCallStatistics(TestFuncEnum func) + { + switch(func) + { + case GetDegrees: mFunctionsCalled.GetDegrees = false; break; + case GetRadians: mFunctionsCalled.GetRadians = false; break; + case ChangedSignal: mFunctionsCalled.ChangedSignal = false; break; + } + } + +private: + + struct TestFunctions + { + TestFunctions() + : GetDegrees(false), + GetRadians(false), + ChangedSignal(false) + { + } + + void Reset() + { + GetDegrees = false; + GetRadians = false; + ChangedSignal = false; + } + + bool GetDegrees; + bool GetRadians; + bool ChangedSignal; + }; + + TestFunctions mFunctionsCalled; + + // The stub + Internal::Adaptor::Orientation* mOrientationStub; + friend class Internal::Adaptor::Orientation; + Orientation mOrientation; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_ORIENTATION_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.cpp new file mode 100644 index 0000000..4789b86 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.cpp @@ -0,0 +1,119 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-physical-keyboard.h" + +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the PhysicalKeyboard + */ +class PhysicalKeyboard : public BaseObject +{ +public: // Creation & Destruction + + PhysicalKeyboard(); + PhysicalKeyboard(ToolkitPhysicalKeyboard *toolkitPhysicalKeyboard); + ~PhysicalKeyboard(); + static Dali::PhysicalKeyboard Get(); + +public: + + bool IsAttached() const; + +public: // Signals + + Dali::PhysicalKeyboard::Signal& StatusChangedSignal(); + +private: + + ToolkitPhysicalKeyboard* mToolkitPhysicalKeyboard; + Dali::PhysicalKeyboard::Signal mStatusChangedSignal; + + bool mIsAttached; +}; + +namespace +{ +PhysicalKeyboard* gPhysicalKeyboard = NULL; +} + +PhysicalKeyboard::PhysicalKeyboard() +: mToolkitPhysicalKeyboard(NULL), + mIsAttached(true) +{ +} + +PhysicalKeyboard::PhysicalKeyboard(ToolkitPhysicalKeyboard *toolkitPhysicalKeyboard) +: mToolkitPhysicalKeyboard(toolkitPhysicalKeyboard), + mIsAttached(true) +{ +} + +PhysicalKeyboard::~PhysicalKeyboard() +{ +} + +Dali::PhysicalKeyboard PhysicalKeyboard::Get() +{ + DALI_ASSERT_ALWAYS(gPhysicalKeyboard); + return Dali::PhysicalKeyboard(gPhysicalKeyboard); +} + +bool PhysicalKeyboard::IsAttached() const +{ + return mIsAttached; +} + +Dali::PhysicalKeyboard::Signal& PhysicalKeyboard::StatusChangedSignal() +{ + return mStatusChangedSignal; +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitPhysicalKeyboard::ToolkitPhysicalKeyboard() +: mPhysicalKeyboardStub(new Internal::Adaptor::PhysicalKeyboard(this)), + mPhysicalKeyboard( mPhysicalKeyboardStub ) +{ + Dali::Internal::Adaptor::gPhysicalKeyboard = mPhysicalKeyboardStub; +} + +ToolkitPhysicalKeyboard::~ToolkitPhysicalKeyboard() +{ +} + +PhysicalKeyboard ToolkitPhysicalKeyboard::GetPhysicalKeyboard() +{ + return mPhysicalKeyboard; +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.h b/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.h new file mode 100644 index 0000000..9e4abb9 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-physical-keyboard.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_PHYSICAL_KEYBOARD_H__ +#define __DALI_TOOLKIT_PHYSICAL_KEYBOARD_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// PUBLIC INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class PhysicalKeyboard; +} +} + +/** + * This creates a stubbed PhysicalKeyboard so that internal Toolkit Adaptor calls work. + */ +class ToolkitPhysicalKeyboard +{ +public: // Constants + +public: // Construction & Destruction + + ToolkitPhysicalKeyboard(); + ~ToolkitPhysicalKeyboard(); + +public: // Getters + + Dali::PhysicalKeyboard GetPhysicalKeyboard(); + +public: // Signal Emissions + + PhysicalKeyboard::Signal& StatusChangedSignal(); + +public: // TEST FUNCTIONS + +private: + + Internal::Adaptor::PhysicalKeyboard* mPhysicalKeyboardStub; + friend class Internal::Adaptor::PhysicalKeyboard; + PhysicalKeyboard mPhysicalKeyboard; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_PHYSICAL_KEYBOARD_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.cpp new file mode 100644 index 0000000..8f7d24a --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.cpp @@ -0,0 +1,138 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-style-monitor.h" + +#include +#include +#include + +namespace Dali +{ + +namespace +{ +ToolkitStyleMonitor* gToolkitStyleMonitor(NULL); +} // unnamed namespace + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the StyleMonitor + */ +class StyleMonitor : public BaseObject +{ +public: // Creation & Destruction + + static Dali::StyleMonitor Get(); + StyleMonitor(); + StyleMonitor(ToolkitStyleMonitor *styleMonitor); + ~StyleMonitor(); + +public: // Style Information + + std::string GetDefaultFontFamily() const; + float GetDefaultFontSize() const; + +public: // Signals + + Dali::StyleMonitor::StyleChangeSignalV2& StyleChangeSignal(); + + void EmitStyleChangeSignal(StyleChange styleChange) + { + mStyleChangeSignal.Emit(Dali::StyleMonitor(this), styleChange); + } + +private: + + Dali::StyleMonitor::StyleChangeSignalV2 mStyleChangeSignal; + + ToolkitStyleMonitor* mToolkitStyleMonitor; +}; + +Dali::StyleMonitor StyleMonitor::Get() +{ + return gToolkitStyleMonitor->GetStyleMonitor(); +} + +StyleMonitor::StyleMonitor() +: mToolkitStyleMonitor(NULL) +{ +} + +StyleMonitor::StyleMonitor(ToolkitStyleMonitor *styleMonitor) +: mToolkitStyleMonitor(styleMonitor) +{ +} + +StyleMonitor::~StyleMonitor() +{ +} + +std::string StyleMonitor::GetDefaultFontFamily() const +{ + mToolkitStyleMonitor->mFunctionsCalled.GetDefaultFontFamily = true; + return ToolkitStyleMonitor::DEFAULT_FONT_FAMILY; +} + +float StyleMonitor::GetDefaultFontSize() const +{ + mToolkitStyleMonitor->mFunctionsCalled.GetDefaultFontSize = true; + return ToolkitStyleMonitor::DEFAULT_FONT_SIZE; +} + +Dali::StyleMonitor::StyleChangeSignalV2& StyleMonitor::StyleChangeSignal() +{ + mToolkitStyleMonitor->mFunctionsCalled.SignalStyleChange = true; + return mStyleChangeSignal; +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string ToolkitStyleMonitor::DEFAULT_FONT_FAMILY("DefaultFont"); +const float ToolkitStyleMonitor::DEFAULT_FONT_SIZE(1.0f); + +ToolkitStyleMonitor::ToolkitStyleMonitor() +: mStyleMonitorStub(new Internal::Adaptor::StyleMonitor(this)), + mStyleMonitor( mStyleMonitorStub ) +{ + gToolkitStyleMonitor = this; +} + +ToolkitStyleMonitor::~ToolkitStyleMonitor() +{ + gToolkitStyleMonitor = NULL; +} + +StyleMonitor ToolkitStyleMonitor::GetStyleMonitor() +{ + return mStyleMonitor; +} + +void ToolkitStyleMonitor::EmitSignalStyleChange(StyleChange styleChange) +{ + mStyleMonitorStub->EmitStyleChangeSignal(styleChange); +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.h b/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.h new file mode 100644 index 0000000..0be6640 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-style-monitor.h @@ -0,0 +1,130 @@ +#ifndef __DALI_TOOLKIT_TOOLKIT_STYLE_MONITOR_H__ +#define __DALI_TOOLKIT_TOOLKIT_STYLE_MONITOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class StyleMonitor; +} +} + +/** + * This creates a stubbed StyleMonitor so that internal Toolkit Adaptor calls work. + * Furthermore, it provides an interface to see if certain methods were invoked. + */ +class ToolkitStyleMonitor +{ +public: // Constants + + static const std::string DEFAULT_FONT_FAMILY; + static const float DEFAULT_FONT_SIZE; + +public: // Construction & Destruction + + ToolkitStyleMonitor(); + ~ToolkitStyleMonitor(); + +public: // Getters + + StyleMonitor GetStyleMonitor(); + +public: // Signal Emissions + + void EmitSignalStyleChange(StyleChange styleChange); + +public: // TEST FUNCTIONS + + // Enumeration of Adaptor methods + enum TestFuncEnum + { + GetDefaultFontFamilyType, + GetDefaultFontSizeType, + SignalStyleChangeType, + }; + + void Reset() + { + mFunctionsCalled.Reset(); + } + + bool WasCalled(TestFuncEnum func) + { + switch(func) + { + case GetDefaultFontFamilyType: return mFunctionsCalled.GetDefaultFontFamily; + case GetDefaultFontSizeType: return mFunctionsCalled.GetDefaultFontSize; + case SignalStyleChangeType: return mFunctionsCalled.SignalStyleChange; + } + return false; + } + + void ResetCallStatistics(TestFuncEnum func) + { + switch(func) + { + case GetDefaultFontFamilyType: mFunctionsCalled.GetDefaultFontFamily = false; break; + case GetDefaultFontSizeType: mFunctionsCalled.GetDefaultFontSize = false; break; + case SignalStyleChangeType: mFunctionsCalled.SignalStyleChange = false; break; + } + } + +private: + + struct TestFunctions + { + TestFunctions() + : GetDefaultFontFamily(false), + GetDefaultFontSize(false), + SignalStyleChange(false) + { + } + + void Reset() + { + GetDefaultFontFamily = false; + GetDefaultFontSize = false; + SignalStyleChange = false; + } + + bool GetDefaultFontFamily; + bool GetDefaultFontSize; + bool SignalStyleChange; + }; + + TestFunctions mFunctionsCalled; + + // The StyleMonitor stub + Internal::Adaptor::StyleMonitor* mStyleMonitorStub; + friend class Internal::Adaptor::StyleMonitor; + StyleMonitor mStyleMonitor; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_STYLE_MONITOR_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-test-application.h b/automated-tests/dali-toolkit-test-utils/toolkit-test-application.h new file mode 100644 index 0000000..b7b40a0 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-test-application.h @@ -0,0 +1,69 @@ +#ifndef __DALI_TOOLKIT_TEST_APPLICATION_H__ +#define __DALI_TOOLKIT_TEST_APPLICATION_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include "toolkit-adaptor.h" +#include "toolkit-orientation.h" + +namespace Dali +{ + +/** + * Adds some functionality on top of TestApplication that is required by the Toolkit. + */ +class ToolkitTestApplication : public TestApplication +{ +public: + + ToolkitTestApplication( size_t surfaceWidth = DEFAULT_SURFACE_WIDTH, + size_t surfaceHeight = DEFAULT_SURFACE_HEIGHT, + float horizontalDpi = DEFAULT_HORIZONTAL_DPI, + float verticalDpi = DEFAULT_VERTICAL_DPI ) + : TestApplication( false, surfaceWidth, surfaceHeight, horizontalDpi, verticalDpi ) + { + Initialize(); + } + + ~ToolkitTestApplication() + { + // Need to delete core before we delete the adaptor. + delete mCore; + mCore = NULL; + } + + ToolkitAdaptor& GetAdaptor() + { + return mAdaptor; + } + + ToolkitOrientation& GetOrientation() + { + return mOrientation; + } + +private: + ToolkitAdaptor mAdaptor; + ToolkitOrientation mOrientation; +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TEST_APPLICATION_H__ diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-timer.cpp b/automated-tests/dali-toolkit-test-utils/toolkit-timer.cpp new file mode 100644 index 0000000..52327b5 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-timer.cpp @@ -0,0 +1,168 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "toolkit-timer.h" +// INTERNAL INCLUDES + +#include +#include +#include + +namespace Dali +{ + +namespace +{ + +Timer gTimer; + +/* + * This is a global signal that all users of this stub will connect to. + * If we were to use a timer stub with real ticks we may wish to have this as a member variable created with each New Timer. + * Currently unable to get the boost singal connect to work with the stub timer when using a member hence this global. + */ +Dali::Timer::TimerSignalV2 gTimerTick; +} + +/** + * Stub for the Timer + */ + +Timer Timer::New( unsigned int milliSec ) +{ + DALI_ASSERT_ALWAYS( gTimer ); + + return gTimer; +} + +Timer::TimerSignalV2& Timer::TickSignal() +{ + return gTimerTick; +} + +bool Timer::IsRunning() const +{ + return true; +} + +Timer::Timer( const Timer& timer ) +: BaseHandle(timer) +{ +} + +Timer::~Timer() +{ +} + + +void Timer::Start() +{ +} + +void Timer::Stop() +{ +} + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * Stub for the Internal Timer + */ +class Timer : public BaseObject +{ +public: // Creation & Destruction + + Timer(); + Timer(ToolkitTimer *timer); + ~Timer(); + + void Start(); + + void Stop(); + + bool IsRunning() const; + +public: // Signals + +private: + + ToolkitTimer* mToolkitTimer; + bool mRunning; +}; + +Timer::Timer() +: mToolkitTimer(NULL), + mRunning( true ) +{ +} + +Timer::Timer(ToolkitTimer *timer) +: mToolkitTimer(timer), + mRunning( true ) +{ +} + +Timer::~Timer() +{ +} + +void Timer::Start() +{ + DALI_ASSERT_ALWAYS( gTimer ); +} + +void Timer::Stop() +{ + DALI_ASSERT_ALWAYS( gTimer ); +} + +bool Timer::IsRunning() const +{ + DALI_ASSERT_ALWAYS( gTimer ); + return mRunning; +} + +} // namespace Adaptor + +} // namespace Internal + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ToolkitTimer::ToolkitTimer() +: mTimerStub(new Internal::Adaptor::Timer(this)), + mTimer( mTimerStub ) +{ + if ( mTimer ) + { + gTimer = mTimer; + } +} + +ToolkitTimer::~ToolkitTimer() +{ + gTimer.Reset(); +} + +Timer ToolkitTimer::GetTimer() +{ + return mTimer; +} + +} // namespace Dali diff --git a/automated-tests/dali-toolkit-test-utils/toolkit-timer.h b/automated-tests/dali-toolkit-test-utils/toolkit-timer.h new file mode 100644 index 0000000..2035759 --- /dev/null +++ b/automated-tests/dali-toolkit-test-utils/toolkit-timer.h @@ -0,0 +1,69 @@ +#ifndef __DALI_TOOLKIT_TOOLKIT_TIMER_H__ +#define __DALI_TOOLKIT_TOOLKIT_TIMER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// PUBLIC INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ +namespace Adaptor +{ +class Timer; + +} +} + +/** + * This creates a stubbed Timer so that internal Toolkit Adaptor calls work. + */ +class ToolkitTimer +{ +public: // Constants + +public: // Construction & Destruction + + ToolkitTimer(); + ~ToolkitTimer(); + +public: + + Timer GetTimer(); + +public: // Signal Emissions + + Dali::Timer::TimerSignalV2& TickSignal(); + +public: // TEST FUNCTIONS + +private: + + Internal::Adaptor::Timer* mTimerStub; + friend class Internal::Adaptor::Timer; + Timer mTimer; // Hold a handle ourselves. +}; + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOLKIT_TIMER_H__ diff --git a/automated-tests/debug.sh b/automated-tests/debug.sh new file mode 100755 index 0000000..c01d55a --- /dev/null +++ b/automated-tests/debug.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TEMP=`getopt -o ds: --long desktop,scenario: \ + -n 'build_out.sh' -- "$@"` + +if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +scenario=all +env=./_export_env.sh + +while true ; do + case "$1" in + -d|--desktop) env=./_export_desktop.sh ; shift ;; + -s|--scenario) scenario="$2" ; shift 2 ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + + +# Source correct environment +. $env + +echo PATH=$PATH +echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH +echo TET_ROOT=$TET_ROOT +echo TET_SUITE_ROOT=$TET_SUITE_ROOT +echo ARCH=$ARCH + +RESULT_DIR=results-$ARCH +HTML_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tmp_script=/tmp/tetexec$$ + +cat > $tmp_script <&2 ; exit 1 ; fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +scenario=all +env=./_export_env.sh + +while true ; do + case "$1" in + -d|--desktop) env=./_export_desktop.sh ; shift ;; + -s|--scenario) scenario="$2" ; shift 2 ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + + +# Source correct environment +. $env + +echo PATH=$PATH +echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH +echo TET_ROOT=$TET_ROOT +echo TET_SUITE_ROOT=$TET_SUITE_ROOT +echo ARCH=$ARCH + +RESULT_DIR=results-$ARCH +HTML_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tmp_script=/tmp/tetexec$$ + +cat > $tmp_script <&2 ; exit 1 ; fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +scenario=all +env=./_export_env.sh + +while true ; do + case "$1" in + -d|--desktop) env=./_export_desktop.sh ; shift ;; + -s|--scenario) scenario="$2" ; shift 2 ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + + +# Source correct environment +. $env + + +echo PATH=$PATH +echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH +echo TET_ROOT=$TET_ROOT +echo TET_SUITE_ROOT=$TET_SUITE_ROOT +echo ARCH=$ARCH + +RESULT_DIR=results-$ARCH +HTML_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tcc -e -j $JOURNAL_RESULT -p ./ $scenario +./tbp.pl $JOURNAL_RESULT + diff --git a/automated-tests/execute_target.sh b/automated-tests/execute_target.sh new file mode 100755 index 0000000..7aaed5f --- /dev/null +++ b/automated-tests/execute_target.sh @@ -0,0 +1,39 @@ +TEMP=`getopt -o ds: --long desktop,scenario: \ + -n 'build_out.sh' -- "$@"` + +if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +scenario=all +env=./_export_target_env.sh + +while true ; do + case "$1" in + -d|--desktop) env=./_export_desktop.sh ; shift ;; + -s|--scenario) scenario="$2" ; shift 2 ;; + --) shift ; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + + +# Source correct environment +. $env + + +echo PATH=$PATH +echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH +echo TET_ROOT=$TET_ROOT +echo TET_SUITE_ROOT=$TET_SUITE_ROOT +echo ARCH=$ARCH + +RESULT_DIR=results-$ARCH +HTML_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tcc -e -j $JOURNAL_RESULT -p ./ $scenario +grw -c 3 -f chtml -o $HTML_RESULT $JOURNAL_RESULT diff --git a/automated-tests/rules.mk.in b/automated-tests/rules.mk.in new file mode 100644 index 0000000..c0df185 --- /dev/null +++ b/automated-tests/rules.mk.in @@ -0,0 +1,11 @@ +LDFLAGS = `pkg-config --libs $(PKGS)` +LDFLAGS += $(TET_ROOT)/lib/tet3/tcm_s.o +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -ltcm_s +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -lapi_s + +CXXFLAGS = -I. -I../../.. `pkg-config --cflags $(PKGS)` +CXXFLAGS += -I$(TET_ROOT)/inc/tet3 +CXXFLAGS += -Wall -g -O0 -Wno-write-strings +CXXFLAGS += -DDALI_IMAGE_DIR=\"$(DALI_IMAGE_DIR)\" +CXXFLAGS += -DDALI_STYLE_DIR=\"$(DALI_STYLE_DIR)\" +CXXFLAGS += @DALI_TOOLKIT_CFLAGS@ diff --git a/automated-tests/tbp.pl b/automated-tests/tbp.pl new file mode 100755 index 0000000..f69a4d3 --- /dev/null +++ b/automated-tests/tbp.pl @@ -0,0 +1,339 @@ +#!/usr/bin/perl + +use strict; + +my $num; +my $scen; +my $ref; +my $part; +my $timestamp; +my $testcase; +my $built=0; +my $build_failed=0; +my $build_count=0; +my $build_failure_count=0; +my $ic=0; +my $ic2=0; +my $x=0; +my $tc=0; +my $v; +my $test; +my $time; +my $date; +my %build_tests; +my %build_summary; + +my $executed_testcases=0; +my $execute_no_file=0; +my $executed=0; +my %execute; +my %execute_summary; + +sub parse_file +{ + while(<>) + { + ($num, $scen, $ref) = split(m!\|!, $_); + chomp $ref; + + if($num == 0) + { + ($v, $time, $date) = split(/\s/, $scen); + } +# Execution + elsif($num == 10) # 10|0 /dali-test-suite/actors/utc-Dali-Actor 16:58:27|TC Start, scenario ref 2-0 + { + ($part, $testcase, $timestamp) = split(/\s/, $scen); + $executed_testcases++; + } + elsif($num == 50) # 50||(exec.c, 131): can't exec /home/SERILOCAL/david.steele/Git/HQ-Dali/dali-core/automated-tests/./tet_tmp_dir/24242aa/dali-test-suite/geometry/utc-Dali-MeshData, reply code = ER_NOENT + { + $execute_no_file++; + } + elsif($num == 80) # 80|19 0 16:58:47|TC End, scenario ref 21-0 + { + } + elsif($num == 400) #400|13 1 142 16:58:40|IC Start + { + ($test, $ic, $x, $timestamp) = split(/\s/, $scen); + } + elsif($num == 410) #410|19 1 9 16:58:46|IC End + { + } + elsif($num == 200) #200|13 1 16:58:40|TP Start + { + $execute_summary{"Total"}++; + } + elsif($num == 220) + { + ($test, $tc, $ic2, $timestamp) = split(/\s/, $scen); + $execute{$testcase}->{$ic}->{$tc} = $ref; + $execute_summary{$ref}++; + $executed++; + } + +# Build + elsif($num == 110) # Build + { + ($part, $testcase, $timestamp) = split(/\s/, $scen); + $build_failed=0; + $build_count++; + $build_summary{"Total"}++; + } + + elsif($num == 100) + { + if( ( $ref =~ /utc-/ && $ref =~ m!error!i ) + || + ($ref =~ /^Makefile/ && $ref =~ m!Stop!i ) + || + ($ref =~ /^make/ && ($ref =~ m!Stop!i || $ref =~ m!Error 1! ) ) ) + { + $build_failed = 1; + } + } + + elsif($num == 130) + { + if($build_failed) + { + $build_failure_count++; + $build_summary{"Failure"}++; + } + else + { + $build_summary{"Success"}++; + } + $build_tests{$testcase} = !$build_failed; + } + } + $built = $build_count - $build_failure_count; +} + + +sub heading +{ + print < + + +TETware Test Run Report + + +

+

+TETware Test Run Report

+

+ + + + + + + + +
Date of test run:$date
Start time:$time

+
+EOH +} + +sub summary +{ + my $heading = shift; + my $summary_ref = shift; + + print < + +
$heading

+

+ + + + +EOS1 + + my ($success_string, @blah) = grep(/(Success|PASS)/, keys(%$summary_ref)); + my $successes = $summary_ref->{$success_string}; + print < + + + +EOS2 + + foreach my $key (sort(grep(!/(Success|PASS|Total)/, keys(%$summary_ref)))) + { + my $fails = $summary_ref->{$key}; + print < + + + +EOS3 + } + + my $total = $summary_ref->{"Total"}; + print < + + + +
ResultCount
$success_string$successes
$key$fails
Total$total

+EOS4 +} + + +sub build_breakdown +{ + print < +

+
+Build mode result breakdown

+

+ + + + +EOB + + foreach my $key (sort(keys(%build_tests))) + { + my $success = $build_tests{$key}; + my $class = $success?"success":"failure"; + my $Class = $success?"Success":"Failure"; + my $color = $success?"#33cc33":"#ff5555"; + print("\n"); + print("\n"); + } + print("
TestcaseResult
$key$Class

"); +} + + +sub execute_breakdown +{ + print < +

+
+Execute mode result breakdown

+EOE + + #Constructed with: + #$execute{$testcase}->{$ic}->{$tc} = $ref; + + my %results; + foreach my $testcase (sort(keys(%execute))) + { + #print STDOUT "$testcase\n"; + foreach my $ic (sort(keys(%{$execute{$testcase}}))) + { + my $ic_ref = $execute{$testcase}->{$ic}; + foreach my $tc (sort { $a <=> $b } (keys(%{$ic_ref}))) + { + my $result = $execute{$testcase}->{$ic}->{$tc}; + chomp($result); + $results{$result}->{$testcase} .= ", $ic.$tc"; + #print STDOUT "$testcase $ic.$tc $result\n"; + #print STDOUT "STRUCT:" . "\$results\{" . $result . "\}->\{$testcase\} => " . $results{$result}->{$testcase} . "\n"; + } + } + } + + foreach my $result ( "PASS", (sort(grep(!/PASS/, keys(%results))))) + { + print "

$result

\n"; + print < + + + + +EOE2 + + my $bgcolor = "#ff5555"; + if ($result =~ /PASS/) + { + $bgcolor = "#33cc33"; + } + + #print STDOUT "Result: $result OUT:" . $results{$result} . "\n"; + foreach my $testcase (sort(keys(%{$results{$result}}))) + { + #print STDOUT "$testcase\n"; + my $tests = substr($results{$result}->{$testcase}, 2); + print < + + + +EOE3 + } + print "
TestcaseTest purposes (IC.TP)
$testcase$tests
"; + } +} + + +sub footer +{ + print "
\n\n\n"; +} + +sub report +{ + my $file = shift; + open(my $fh, ">", $file) || die "Can't create $file"; + select $fh; + heading; + + if($build_count) + { + summary("Build mode summary", \%build_summary); + build_breakdown; + } + if(scalar(keys(%execute))) + { + summary("Execute mode summary", \%execute_summary); + execute_breakdown; + } + footer; + select STDOUT; + close $fh; +} + + +## MAIN + +die "No args" if scalar(@ARGV) == 0; +my $htmlname = $ARGV[0]; +$htmlname =~ s/.journal/.html/; + +parse_file(); +report($htmlname); + + +if($build_count) +{ + foreach my $key (sort(keys(%build_tests))) + { + if(!$build_tests{$key}) + { + print STDOUT "$key: Failed\n"; + } + } + print STDOUT "Built $built of $build_count\n"; +} + +if($executed) +{ + my $num_exes=$executed_testcases-$execute_no_file; + print STDOUT "Executed $num_exes of $executed_testcases testcases\n"; + my $passed = $execute_summary{"PASS"}; + my $total = $execute_summary{"Total"}; + my $passRate = ($num_exes / $executed_testcases ) * ( $passed / $total )*100; + print STDOUT "Passed $passed of $total - Pass rate=" . sprintf("%4.1f%%", $passRate) ."\n"; +} + +print "Report output: $htmlname\n"; diff --git a/automated-tests/tet_scen b/automated-tests/tet_scen new file mode 100644 index 0000000..bd9f75e --- /dev/null +++ b/automated-tests/tet_scen @@ -0,0 +1,126 @@ +all + "Starting Full Test Suite" + ^TEST + "Completed Full Test Suite" + +##### All PUBLIC and INTERNAL Tests ##### + +# Test scenario +TEST + ^PUBLIC + ^INTERNAL + +##### PUBLIC Tests ##### + +PUBLIC + ^alignment + ^bubble-emitter + ^builder + ^buttons + ^cluster + ^control + ^default-controls + ^focus-manager + ^item-view + ^navigation-frame + ^page-turn-view + ^popup + ^scroll-view + ^selectors + ^shader-effects + ^slider + ^super-blur-view + ^table-view + ^text-input + ^text-view + ^toolbar + ^transition-effects + ^view + +alignment + :include:/dali-test-suite/alignment/tslist + +text-input + :include:/dali-test-suite/text-input/tslist + +text-view + :include:/dali-test-suite/text-view/tslist + +toolbar + :include:/dali-test-suite/toolbar/tslist + +buttons + :include:/dali-test-suite/buttons/tslist + +default-controls + :include:/dali-test-suite/default-controls/tslist + +view + :include:/dali-test-suite/view/tslist + +table-view + :include:/dali-test-suite/table-view/tslist + +item-view + :include:/dali-test-suite/item-view/tslist + +cluster + :include:/dali-test-suite/cluster/tslist + +shader-effects + :include:/dali-test-suite/shader-effects/tslist + +selectors + :include:/dali-test-suite/selectors/tslist + +scroll-view + :include:/dali-test-suite/scroll-view/tslist + +popup + :include:/dali-test-suite/popup/tslist + +builder + :include:/dali-test-suite/builder/tslist + +control + :include:/dali-test-suite/control/tslist + +focus-manager + :include:/dali-test-suite/focus-manager/tslist + +transition-effects + :include:/dali-test-suite/transition-effects/tslist + +page-turn-view + :include:/dali-test-suite/page-turn-view/tslist + +super-blur-view + :include:/dali-test-suite/super-blur-view/tslist + +navigation-frame + :include:/dali-test-suite/navigation-frame/tslist + +bubble-emitter + :include:/dali-test-suite/bubble-emitter/tslist + +slider + :include:/dali-test-suite/slider/tslist + +##### INTERNAL Tests ##### + +INTERNAL + :include:/dali-internal-test-suite/text-input/tslist + :include:/dali-internal-test-suite/text-view/tslist + +internal-text-input + :include:/dali-internal-test-suite/text-input/tslist + +internal-text-view + :include:/dali-internal-test-suite/text-view/tslist + +text-view-full + :include:/dali-test-suite/text-view/tslist + :include:/dali-internal-test-suite/text-view/tslist + +##### DEBUG ##### +# EOF diff --git a/automated-tests/tetbuild.cfg b/automated-tests/tetbuild.cfg new file mode 100644 index 0000000..6a05af6 --- /dev/null +++ b/automated-tests/tetbuild.cfg @@ -0,0 +1,5 @@ +TET_OUTPUT_CAPTURE=True # capture option for build operation checking +TET_BUILD_TOOL=make # build with using make command +TET_BUILD_FILE=-f Makefile # execution file (Makefile) for build +TET_API_COMPLIANT=False # use TET API in Test Case ? +TET_PASS_TC_NAME=True # report passed TC name in Journal file? diff --git a/automated-tests/tetclean.cfg b/automated-tests/tetclean.cfg new file mode 100644 index 0000000..2a0477a --- /dev/null +++ b/automated-tests/tetclean.cfg @@ -0,0 +1,5 @@ +TET_OUTPUT_CAPTURE=True # capture option +TET_CLEAN_TOOL= make clean # clean tool +TET_CLEAN_FILE= Makefile # file for clean +TET_API_COMPLIANT=True # TET API useage +TET_PASS_TC_NAME=True # showing name , passed TC diff --git a/automated-tests/tetexec.cfg b/automated-tests/tetexec.cfg new file mode 100644 index 0000000..ef3e452 --- /dev/null +++ b/automated-tests/tetexec.cfg @@ -0,0 +1,5 @@ +TET_OUTPUT_CAPTURE=True # capturing execution or not +TET_EXEC_TOOL= # ex) exec : execution tool set up/ Optional +TET_EXEC_FILE= # ex) exectool : execution file/ Optional +TET_API_COMPLIANT=True # Test case or Tool usesTET API? +TET_PASS_TC_NAME=True # showing Passed TC name ? diff --git a/build/slp/.gitignore b/build/slp/.gitignore new file mode 100644 index 0000000..ea033f1 --- /dev/null +++ b/build/slp/.gitignore @@ -0,0 +1,19 @@ +/gmon.out +/aclocal.m4 +/autom4te.cache +/config.guess +/config.log +/config.status +/config.sub +/configure +/depcomp +/documentation.list +/install-sh +/libtool +/ltmain.sh +/missing +/demo/dali-demo +/dali-toolkit.pc +/dali.pc +/dali-core/dali-shaders.cpp +/dali-core/dali-shaders.h diff --git a/build/slp/Makefile.am b/build/slp/Makefile.am new file mode 100644 index 0000000..d730e17 --- /dev/null +++ b/build/slp/Makefile.am @@ -0,0 +1,55 @@ +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd. +# +# Licensed under the Flora License, Version 1.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://floralicense.org/license/ +# +# 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. +# + +SUBDIRS = dali-toolkit + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = dali-toolkit.pc + +MAINTAINERCLEANFILES = \ + aclocal.m4 \ + autom4te.cache \ + config.guess \ + config.sub \ + configure \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + `find "$(srcdir)" -type f -name Makefile.in -print` \ + `find . \( -name "*.gcov" -o -name "*.gcno" -o -name "*.gcda" \) -print` + +CLEANFILES = \ + `find . \( -name "*.gcov" -o -name "*.gcno" -o -name "*.gcda" \) -print` + +COVERAGE_DIR=.cov +COVERAGE_OUTPUT_DIR=doc/coverage + +cov_data: + @test -z $(COVERAGE_DIR) || mkdir -p $(COVERAGE_DIR) + @rm -f $(COVERAGE_DIR)/* + @cp dali-toolkit/.libs/*.gcda dali-toolkit/.libs/*.gcno $(COVERAGE_DIR) + @for i in `find $(COVERAGE_DIR) -name "libdali_toolkit_la-*.gcda" -o -name "libdali_toolkit_la-*.gcno"` ;\ + do mv $$i `echo $$i | sed s/libdali_toolkit_la-//` ; echo $$i ; done + @cd $(COVERAGE_DIR) ; lcov --base-directory . --directory . -c -o dali.info + @cd $(COVERAGE_DIR) ; lcov --remove dali.info "*boost*" "/usr/include/*" "*/dali-env/*" -o dali.info + @test -z $(COVERAGE_OUTPUT_DIR) || mkdir -p $(COVERAGE_OUTPUT_DIR) + +coverage: cov_data + @genhtml -o $(COVERAGE_OUTPUT_DIR) $(COVERAGE_DIR)/dali.info + +reset_coverage: + @lcov -z --directory `pwd` diff --git a/build/slp/README b/build/slp/README new file mode 100644 index 0000000..ea76b45 --- /dev/null +++ b/build/slp/README @@ -0,0 +1,3 @@ +autoreconf --install +./configure --prefix=$DESKTOP_PREFIX +make install -j3 diff --git a/build/slp/configure.ac b/build/slp/configure.ac new file mode 100644 index 0000000..af20022 --- /dev/null +++ b/build/slp/configure.ac @@ -0,0 +1,96 @@ +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd. +# +# Licensed under the Flora License, Version 1.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://floralicense.org/license/ +# +# 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. +# + +m4_define([dali_version],[0.1.0]) +AC_INIT([dali], [dali_version]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) + +AC_PROG_CXX +AC_PROG_LIBTOOL + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +LT_INIT + +DALI_TOOLKIT_VERSION=dali_version +AC_SUBST(DALI_TOOLKIT_VERSION) + +PKG_CHECK_MODULES(DALICORE, dali-core) +PKG_CHECK_MODULES(DALI, dali) +PKG_CHECK_MODULES(DLOG, dlog) +PKG_CHECK_MODULES(FRIBIDI, fribidi) + +DALI_TOOLKIT_CFLAGS=-DPLATFORM_SLP + +AC_ARG_ENABLE(exportall, + [AC_HELP_STRING([--enable-exportall], + [enables the exporting of all the symbols in the library])], + [enable_exportall=yes], + [enable_exportall=no]) + +AC_ARG_ENABLE([debug], + [AC_HELP_STRING([--enable-debug], + [Turns on debugging])], + [enable_debug=$enableval], + [enable_debug=no]) + +if test "x$enable_debug" = "xyes"; then + DALI_TOOLKIT_CFLAGS="$DALI_TOOLKIT_CFLAGS -DDEBUG_ENABLED" +fi + +if test "x$enable_debug" = "xno" -a "x$enable_exportall" = "xno"; then + DALI_TOOLKIT_CFLAGS="$DALI_TOOLKIT_CFLAGS -fvisibility=hidden -DHIDE_DALI_INTERNALS" +fi + + +if test x$DALI_DATA_RW_DIR != x; then + dataReadWriteDir=${DALI_DATA_RW_DIR}/ +else + dataReadWriteDir=${prefix}/share/dali/ +fi + +if test x$DALI_DATA_RO_DIR != x; then + dataReadOnlyDir=${DALI_DATA_RO_DIR}/ +else + dataReadOnlyDir=${prefix}/share/dali/ +fi + +AC_SUBST(dataReadWriteDir) +AC_SUBST(dataReadOnlyDir) +AC_SUBST(DALI_TOOLKIT_CFLAGS) + +# Specify the include directory for development headers +#devincludepath=${includedir}/dali/internal +devincludepath=${includedir} +AC_SUBST(devincludepath) + +AC_CONFIG_FILES([ + Makefile + dali-toolkit/Makefile + dali-toolkit.pc + ../../automated-tests/rules.mk +]) + +AC_OUTPUT + +echo " +Configuration +------------- + Prefix: $prefix + Debug Build: $enable_debug + Data Dir (Read/Write): $dataReadWriteDir + Data Dir (Read Only): $dataReadOnlyDir +" diff --git a/build/slp/dali-toolkit.pc.in b/build/slp/dali-toolkit.pc.in new file mode 100644 index 0000000..48827c3 --- /dev/null +++ b/build/slp/dali-toolkit.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +apiversion=@DALI_TOOLKIT_VERSION@ +libdir=${exec_prefix}/lib +includedir=@devincludepath@ + +Name: Samsung OpenGLES Toolkit (including Toolkit) +Description: 3D Canvas Toolkit using OpenGLES (with the toolkit) +Version: ${apiversion} +Requires: dali-core +Libs: -L${libdir} -ldali-toolkit +Cflags: -I${includedir} diff --git a/build/slp/dali-toolkit/Makefile.am b/build/slp/dali-toolkit/Makefile.am new file mode 100644 index 0000000..c9cdc77 --- /dev/null +++ b/build/slp/dali-toolkit/Makefile.am @@ -0,0 +1,215 @@ +# +# Copyright (c) 2014 Samsung Electronics Co., Ltd. +# +# Licensed under the Flora License, Version 1.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://floralicense.org/license/ +# +# 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. +# + +# Build the Dali Toolkit library + +toolkit_images_dir = ../../../dali-toolkit/images +toolkit_src_dir = ../../../dali-toolkit/internal +public_api_src_dir = ../../../dali-toolkit/public-api +capi_devel_src_dir = ../../../capi/dali-toolkit/public-api + +include ../../../dali-toolkit/images/file.list +include ../../../dali-toolkit/internal/file.list +include ../../../dali-toolkit/public-api/file.list +include ../../../capi/dali-toolkit/public-api/file.list + +lib_LTLIBRARIES = libdali-toolkit.la + +libdali_toolkit_la_SOURCES = \ + $(toolkit_src_files) \ + $(public_api_src_files) + +resources_dir = ../../../resources +daliimagedir = ${dataReadOnlyDir}/toolkit/images/ +daliimage_DATA = ${dali_toolkit_image_files} + +libdali_toolkit_la_DEPENDENCIES = + +libdali_toolkit_la_CXXFLAGS = -DDALI_COMPILATION \ + -DDALI_IMAGE_DIR="\"${daliimagedir}\"" \ + -Werror -Wall \ + -I../../.. \ + -I../../../capi \ + $(DALI_TOOLKIT_CFLAGS) \ + $(DALICORE_CFLAGS) \ + $(DALI_CFLAGS) \ + $(DLOG_CFLAGS) \ + $(FRIBIDI_CFLAGS) \ + $(HTMLCXX_CFLAGS) + +libdali_toolkit_la_LIBADD = \ + $(DALICORE_LIBS) \ + $(DALI_LIBS) \ + $(DLOG_LIBS) \ + $(FRIBIDI_LIBS) \ + $(HTMLCXX_LIBS) + +# Install headers +topleveldir = $(devincludepath)/dali-toolkit +toplevel_HEADERS = ../../../dali-toolkit/dali-toolkit.h + +publicapidir = $(devincludepath)/dali-toolkit/public-api +publicapicontrolsdir = $(publicapidir)/controls +publicapialignmentdir = $(publicapidir)/controls/alignment +publicapibloomviewdir = $(publicapidir)/controls/bloom-view +publicapibuttonsdir = $(publicapidir)/controls/buttons +publicapiclusterdir = $(publicapidir)/controls/cluster +publicapidefaultcontrolsdir = $(publicapidir)/controls/default-controls +publicapieffectsviewdir = $(publicapidir)/controls/effects-view +publicapigaussianblurviewdir = $(publicapidir)/controls/gaussian-blur-view +publicapiimageviewdir = $(publicapidir)/controls/image-view +publicapiitemviewdir = $(publicapidir)/controls/scrollable/item-view +publicapimagnifierdir = $(publicapidir)/controls/magnifier +publicapipopupdir = $(publicapidir)/controls/popup +publicapipageturnviewdir = $(publicapidir)/controls/page-turn-view +publicapiscrollcomponentdir = $(publicapidir)/controls/scroll-component +publicapiscrollabledir = $(publicapidir)/controls/scrollable +publicapiscrollviewdir = $(publicapidir)/controls/scrollable/scroll-view +publicapisliderdir = $(publicapidir)/controls/slider +publicapitableviewdir = $(publicapidir)/controls/table-view +publicapitextviewdir = $(publicapidir)/controls/text-view +publicapitextinputdir = $(publicapidir)/controls/text-input +publicapitoolbardir = $(publicapidir)/controls/tool-bar +publicapiselectorsdir = $(publicapidir)/controls/selectors +publicapishadowviewdir = $(publicapidir)/controls/shadow-view +publicapibubbleemitterdir = $(publicapidir)/controls/bubble-effect +publicapisuperblurviewdir = $(publicapidir)/controls/super-blur-view +publicapiviewdir = $(publicapidir)/controls/view +publicapinavigationframedir = $(publicapidir)/controls/navigation-frame +publicapifactorydir = $(publicapidir)/factory +publicapifocusmanagerdir = $(publicapidir)/focus-manager +publicapimarkupprocessordir = $(publicapidir)/markup-processor +publicapishadereffectsdir = $(publicapidir)/shader-effects +publicapibubbleeffectdir = $(publicapidir)/shader-effects/bubble-effect +publicapistyledtextutilitiesdir = $(publicapidir)/styled-text-utilities +publicapibuilderdir = $(publicapidir)/builder +publicapiutilitiesdir = $(publicapidir)/utilities +publicapitransitioneffectsdir = $(publicapidir)/transition-effects + +publicapi_HEADERS = $(public_api_header_files) +publicapicontrols_HEADERS = $(public_api_controls_header_files) +publicapialignment_HEADERS = $(public_api_alignment_header_files) +publicapibloomview_HEADERS = $(public_api_bloom_view_header_files) +publicapibuttons_HEADERS = $(public_api_buttons_header_files) +publicapicluster_HEADERS = $(public_api_cluster_header_files) +publicapidefaultcontrols_HEADERS = $(public_api_default_controls_header_files) +publicapieffectsview_HEADERS = $(public_api_effects_view_header_files) +publicapigaussianblurview_HEADERS = $(public_api_gaussian_blur_view_header_files) +publicapiimageview_HEADERS = $(public_api_image_view_header_files) +publicapiitemview_HEADERS = $(public_api_item_view_header_files) +publicapimagnifier_HEADERS = $(public_api_magnifier_header_files) +publicapipopup_HEADERS = $(public_api_popup_header_files) +publicapipageturnview_HEADERS = $(public_api_page_turn_view_header_files) +publicapiscrollcomponent_HEADERS = $(public_api_scroll_component_header_files) +publicapiscrollable_HEADERS = $(public_api_scrollable_header_files) +publicapiscrollview_HEADERS = $(public_api_scroll_view_header_files) +publicapislider_HEADERS = $(public_api_slider_header_files) +publicapitableview_HEADERS = $(public_api_table_view_header_files) +publicapitextview_HEADERS = $(public_api_text_view_header_files) +publicapitextinput_HEADERS = $(public_api_text_input_header_files) +publicapitoolbar_HEADERS = $(public_api_tool_bar_header_files) +publicapiselectors_HEADERS = $(public_api_selectors_header_files) +publicapishadowview_HEADERS = $(public_api_shadow_view_header_files) +publicapibubbleemitter_HEADERS = $(public_api_bubble_emitter_header_files) +publicapisuperblurview_HEADERS = $(public_api_super_blur_view_header_files) +publicapiview_HEADERS = $(public_api_view_header_files) +publicapinavigationframe_HEADERS = $(public_api_navigation_frame_header_files) +publicapifactory_HEADERS = $(public_api_factory_header_files) +publicapifocusmanager_HEADERS = $(public_api_focus_manager_header_files) +publicapimarkupprocessor_HEADERS = $(public_api_markup_processor_header_files) +publicapishadereffects_HEADERS = $(public_api_shader_effects_header_files) +publicapibubbleeffect_HEADERS = $(public_api_bubble_effect_header_files) +publicapistyledtextutilities_HEADERS = $(public_api_styled_text_utilities_header_files) +publicapibuilder_HEADERS = $(public_api_builder_header_files) +publicapiutilities_HEADERS = $(public_api_utilities_header_files) +publicapitransitioneffects_HEADERS = $(public_api_transition_effects_header_files) + + +capideveldir = $(devincludepath)/dali-toolkit/public-api +capidevelcontrolsdir = $(capideveldir)/controls +capidevelalignmentdir = $(capideveldir)/controls/alignment +capidevelbloomviewdir = $(capideveldir)/controls/bloom-view +capidevelbuttonsdir = $(capideveldir)/controls/buttons +capidevelclusterdir = $(capideveldir)/controls/cluster +capideveldefaultcontrolsdir = $(capideveldir)/controls/default-controls +capideveleffectsviewdir = $(capideveldir)/controls/effects-view +capidevelgaussianblurviewdir = $(capideveldir)/controls/gaussian-blur-view +capidevelimageviewdir = $(capideveldir)/controls/image-view +capidevelitemviewdir = $(capideveldir)/controls/scrollable/item-view +capidevelmagnifierdir = $(capideveldir)/controls/magnifier +capidevelpopupdir = $(capideveldir)/controls/popup +capidevelpageturnviewdir = $(capideveldir)/controls/page-turn-view +capidevelscrollcomponentdir = $(capideveldir)/controls/scroll-component +capidevelscrollabledir = $(capideveldir)/controls/scrollable +capidevelscrollviewdir = $(capideveldir)/controls/scrollable/scroll-view +capidevelsliderdir = $(capideveldir)/controls/slider +capideveltableviewdir = $(capideveldir)/controls/table-view +capideveltextviewdir = $(capideveldir)/controls/text-view +capideveltextinputdir = $(capideveldir)/controls/text-input +capideveltoolbardir = $(capideveldir)/controls/tool-bar +capidevelselectorsdir = $(capideveldir)/controls/selectors +capidevelshadowviewdir = $(capideveldir)/controls/shadow-view +capidevelbubbleemitterdir = $(capideveldir)/controls/bubble-effect +capidevelsuperblurviewdir = $(capideveldir)/controls/super-blur-view +capidevelviewdir = $(capideveldir)/controls/view +capidevelnavigationframedir = $(capideveldir)/controls/navigation-frame +capidevelfactorydir = $(capideveldir)/factory +capidevelfocusmanagerdir = $(capideveldir)/focus-manager +capidevelmarkupprocessordir = $(capideveldir)/markup-processor +capidevelshadereffectsdir = $(capideveldir)/shader-effects +capidevelbubbleeffectdir = $(capideveldir)/shader-effects/bubble-effect +capidevelstyledtextutilitiesdir = $(capideveldir)/styled-text-utilities +capidevelbuilderdir = $(capideveldir)/builder +capidevelutilitiesdir = $(capideveldir)/utilities +capideveltransitioneffectsdir = $(capideveldir)/transition-effects + +capidevel_HEADERS = $(capi_devel_header_files) +capidevelcontrols_HEADERS = $(capi_devel_controls_header_files) +capidevelalignment_HEADERS = $(capi_devel_alignment_header_files) +capidevelbloomview_HEADERS = $(capi_devel_bloom_view_header_files) +capidevelbuttons_HEADERS = $(capi_devel_buttons_header_files) +capidevelcluster_HEADERS = $(capi_devel_cluster_header_files) +capideveldefaultcontrols_HEADERS = $(capi_devel_default_controls_header_files) +capideveleffectsview_HEADERS = $(capi_devel_effects_view_header_files) +capidevelgaussianblurview_HEADERS = $(capi_devel_gaussian_blur_view_header_files) +capidevelimageview_HEADERS = $(capi_devel_image_view_header_files) +capidevelitemview_HEADERS = $(capi_devel_item_view_header_files) +capidevelmagnifier_HEADERS = $(capi_devel_magnifier_header_files) +capidevelpopup_HEADERS = $(capi_devel_popup_header_files) +capidevelpageturnview_HEADERS = $(capi_devel_page_turn_view_header_files) +capidevelscrollcomponent_HEADERS = $(capi_devel_scroll_component_header_files) +capidevelscrollable_HEADERS = $(capi_devel_scrollable_header_files) +capidevelscrollview_HEADERS = $(capi_devel_scroll_view_header_files) +capidevelslider_HEADERS = $(capi_devel_slider_header_files) +capideveltableview_HEADERS = $(capi_devel_table_view_header_files) +capideveltextview_HEADERS = $(capi_devel_text_view_header_files) +capideveltextinput_HEADERS = $(capi_devel_text_input_header_files) +capideveltoolbar_HEADERS = $(capi_devel_tool_bar_header_files) +capidevelselectors_HEADERS = $(capi_devel_selectors_header_files) +capidevelshadowview_HEADERS = $(capi_devel_shadow_view_header_files) +capidevelbubbleemitter_HEADERS = $(capi_devel_bubble_emitter_header_files) +capidevelsuperblurview_HEADERS = $(capi_devel_super_blur_view_header_files) +capidevelview_HEADERS = $(capi_devel_view_header_files) +capidevelnavigationframe_HEADERS = $(capi_devel_navigation_frame_header_files) +capidevelfactory_HEADERS = $(capi_devel_factory_header_files) +capidevelfocusmanager_HEADERS = $(capi_devel_focus_manager_header_files) +capidevelmarkupprocessor_HEADERS = $(capi_devel_markup_processor_header_files) +capidevelshadereffects_HEADERS = $(capi_devel_shader_effects_header_files) +capidevelbubbleeffect_HEADERS = $(capi_devel_bubble_effect_header_files) +capidevelstyledtextutilities_HEADERS = $(capi_devel_styled_text_utilities_header_files) +capidevelbuilder_HEADERS = $(capi_devel_builder_header_files) +capidevelutilities_HEADERS = $(capi_devel_utilities_header_files) +capideveltransitioneffects_HEADERS = $(capi_devel_transition_effects_header_files) diff --git a/capi/dali-toolkit/public-api/controls/alignment/alignment.h b/capi/dali-toolkit/public-api/controls/alignment/alignment.h new file mode 100644 index 0000000..6c1ebb8 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/alignment/alignment.h @@ -0,0 +1,201 @@ +#ifndef __DALI_ALIGNMENT_H__ +#define __DALI_ALIGNMENT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Alignment; +} + +/** + * Alignment is a container which provides an easy way to align other actors inside its boundary. + * + * Additionaly it provides a scaling property to resize the contained actors @see Scaling. + * @note The use of scaling property will override all constraints applied to actors. + * + * All actors added to an alignment are going to be set with the same anchor point and parent origin. And, if the scaling property is set to a value + * different than ScaleNone, constraints as well. + */ +class Alignment : public Control +{ +public: + /** + * Different types of alignment. + */ + enum Type + { + HorizontalLeft = 1, + HorizontalCenter = 2, + HorizontalRight = 4, + VerticalTop = 8, + VerticalCenter = 16, + VerticalBottom = 32 + }; + + /** + * Scaling determines how actors are scaled, to match the alignment's boundary. + */ + enum Scaling + { + ScaleNone, ///< The original size is kept. + ScaleToFill, ///< Scale added actors to fill alignment's boundary. Aspect ratio is not maintained. + ScaleToFitKeepAspect, ///< Scale added actors to fit within the alignment's boundary. Aspect ratio is maintained. + ScaleToFillKeepAspect, ///< Scale added actors to fill the alignment's boundary. Aspect ratio is maintained, and the actor may exceed the alignment's boundary. + ShrinkToFit, ///< If added actors are larger than the alignment's boundary it will be shrunk down to fit. Aspect ratio is not maintained + ShrinkToFitKeepAspect, ///< If added actors are larger than the alignment's boundary it will be shrunk down to fit. Aspect ratio is maintained + }; + + struct Padding + { + Padding() + : left( 0.f ), + right( 0.f ), + top( 0.f ), + bottom( 0.f ) + { + } + + Padding( float l, float r, float t, float b ) + : left( l ), + right( r ), + top( t ), + bottom( b ) + { + } + + float left; + float right; + float top; + float bottom; + }; + + /** + * Create an Alignment handle; this can be initialised with Alignment::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + Alignment(); + + /** + * Creates an alignment control. + * @param [in] horizontal Specifies how to align actors horizontally. Could be HorizontalLeft, HorizontalCenter or HorizontalRight. By default HorizontalCenter. + * @param [in] vertical Specifies how to align actors vertically. Could be VerticalTop, VerticalCenter or VerticalBottom. By default VerticalCenter. + * @return A handle to the Alignment control. + */ + static Alignment New( Type horizontal = HorizontalCenter, Type vertical = VerticalCenter ); + + /** + * Copy constructor. Creates another handle that points to the same real object. + */ + Alignment(const Alignment& alignment); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Alignment(); + + /** + * Downcast an Object handle to Alignment. If handle points to a Alignment the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Alignment or an uninitialized handle + */ + static Alignment DownCast( BaseHandle handle ); + + /** + * Sets the new alignment. By default ( HorizontalCenter | VerticalCenter ). + * @param [in] type The new alignment option. + */ + void SetAlignmentType( Type type ); + + /** + * Get the current alignment combined into a single value. + * The values can be tested by using the & operator + * and the desired flag. e.g. if (GetAlignmentType() & HorizontalCentre) ... + * @return the alignment value. + */ + Type GetAlignmentType() const; + + /** + * Sets how added actors scale to fit the alignment's boundary. + * @see Scaling. + * @param[in] scaling The scaling property. + */ + void SetScaling( Scaling scaling ); + + /** + * Retrieves the scaling property. + * @see Scaling. + * @return The scaling. + */ + Scaling GetScaling() const; + + /** + * Set a padding value. + * + * @param [in] padding The left, right, top, bottom padding values. + */ + void SetPadding( const Padding& padding ); + + /** + * @return The left, right, top, bottom padding values. + */ + const Padding& GetPadding() const; + + /** + * Assignment operator. Changes this handle to point to another real object. + */ + Alignment& operator=(const Alignment& alignment); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Alignment( Internal::Alignment& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Alignment( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_LAYOUT_H__ diff --git a/capi/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.h b/capi/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.h new file mode 100644 index 0000000..f292f44 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.h @@ -0,0 +1,182 @@ +#ifndef __DALI_TOOLKIT_BUBBLE_EMMITER_H__ +#define __DALI_TOOLKIT_BUBBLE_EMMITER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + /** + * BubbleEmitter implementation class + */ + class BubbleEmitter; +} + +/** + * BubbleEmitter is used to display lots of moving bubbles on the stage. + * + * This is done by applying BubbleEffect to multiple specifically created meshActors. + */ +class BubbleEmitter : public Control +{ +public: + + /** + * Create an empty BubbleEmitter handle + */ + BubbleEmitter(); + + /** + * Virtual destructor. + */ + ~BubbleEmitter(); + + /** + * Create an initialized BubbleEmitter + * @param[in] winSize The size of the bubble moving area, usually the same size as the background image actor. + * @param[in] shapeImage The alpha channnel of this texture defines the bubble shape. + * @param[in] maximumNumberOfBubble The maximum number of bubble needed. + * @param[in] bubbleSizeRange The size range of the bubbles; x component is the low bound, and y component is the up bound. + * @return The initialized BubbleEmitter object. + */ + static BubbleEmitter New( const Vector2& winSize, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ); + + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + BubbleEmitter( const BubbleEmitter& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + BubbleEmitter& operator=( const BubbleEmitter& rhs ); + + /** + * Downcast an Object handle to SuperBlurView. + * If handle points to a BubbleEmitter, the downcast produces valid handle. + * If not, the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a BubbleEmitter or an uninitialized handle + */ + static BubbleEmitter DownCast( BaseHandle handle ); + + /** + * Return the root actor of all bubbles, should then be added to stage. + * @return The bubble root actor. + */ + Actor GetRootActor(); + + /** + * Set Background image. + * The bubbles pick color from this image with HSV values adjusted. + * @param[in] bgImage The background image which provide color to bubbles. + * @param[in] hsvDelta The hsv channel difference used to adjust the background image color. + * If set these vector as Vector3::Zero, original colors are used. + */ + void SetBackground( Image bgImage, const Vector3& hsvDelta ); + + /** + * Set bubble shape. + * The bubble mesh is a rectangular patch, but its displayed shape is decided by the alpha channel of the shape image. + * @param[in] shapeImage The image whose alpha channel defines the bubble shape. + */ + void SetShapeImage( Image shapeImage ); + + /** + * Set the scale factor applied to all the bubbles. + * @param [in] scale The scale factor applied on bubbles. + */ + void SetBubbleScale( float scale ); + + /** + * Set the density of the bubble. + * Ideally every bubble's moving track is controlled by different uniforms in BubbleEffect shaders. + * To increase the density, 'density' number of bubbles are sharing one group of uniforms, but with random offsets between these bubbles. + * The available density is one to nine. The default density is five. + * By set the density bigger than one, instead of emit one bubble each time, a 'density' number of bubbles are emitted. + * @param[in] density The density of the bubble. + */ + void SetBubbleDensity( unsigned int density ); + + /** + * Enable different blending mode for rendering + * @param[in] enable If false, the default blending function for RenderableActor is used. + */ + void SetBlendMode( bool enable ); + + /** + * Add a bubble movement to the animation. + * @param[in] animation The animation reference. + * By passing the animation into BubbleEmitter, the animation's duration and how many bubbles contained within this animation are freely decided in App. + * @param[in] emitPosition The start position of the bubble movement. + * @param[in] direction The direction used to constrain the bubble to move in an adjacent direction around it. + * @param[in] displacement The displacement used to bound the moving distance of the bubble. + */ + void EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ); + + /** + * Start an animation to enlarge every activated bubble's size and moving speed. + * @param[in] duration The duration of the animation + * @param[in] multiple The bubble size and moving speed will be increased gradually to multiple speed during the animation. + */ + void StartExplosion( float duration, float multiple ); + + /** + * Reset all the parameters controlling the bubbles after animation. + */ + void Restore(); + +public: // Not intended for developer use + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + DALI_INTERNAL BubbleEmitter(Internal::BubbleEmitter& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + DALI_INTERNAL BubbleEmitter(Dali::Internal::CustomActor* internal); +}; + +} // namespace Toolkit + +} // namespace Dali +/** + * @} + */ +#endif /* __DALI_TOOLKIT_BUBBLE_EMMITER_H__ */ diff --git a/capi/dali-toolkit/public-api/controls/buttons/button.h b/capi/dali-toolkit/public-api/controls/buttons/button.h new file mode 100644 index 0000000..6cdb696 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/buttons/button.h @@ -0,0 +1,156 @@ +#ifndef __DALI_TOOLKIT_BUTTON_H__ +#define __DALI_TOOLKIT_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Internal DALI_INTERNAL +{ +class CustomActor; +} + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Button; +} + +/** + * Button is a base class for different kind of buttons. + * This class provides the dimmed property and the clicked signal. + * + * A ClickedSignal() is emitted when the button is touched and the touch + * point doesn't leave the boundary of the button. + * + * When the \e dimmed property is set to \e true, no signal is emitted. + */ +class Button : public Control +{ +public: + //Signal Names + static const char* const SIGNAL_CLICKED; + + /** + * The names of custom properties installed by this control. + */ + // Property Names + static const char* const PROPERTY_DIMMED; ///< name "dimmed", type bool. + +public: + + /** + * Create an uninitialized Button. Only derived versions can be instantiated. + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + Button(); + + /** + * Copy constructor. + */ + Button( const Button& button ); + + /** + * Assignment operator. + */ + Button& operator=( const Button& button ); + + /** + * Downcast an Object handle to Button. If handle points to a Button the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Button or an uninitialized handle + */ + static Button DownCast( BaseHandle handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Button(); + + /** + * Sets the button as \e dimmed. + * + * No signals are emitted when the \e dimmed property is set. + * + * @param[in] dimmed property. + */ + void SetDimmed( bool dimmed ); + + /** + * @return \e true if the button is \e dimmed. + */ + bool IsDimmed() const; + + /** + * Sets the animation time. + * @param [in] animationTime The animation time in seconds. + */ + void SetAnimationTime( float animationTime ); + + /** + * Retrieves button's animation time. + * @return The animation time in seconds. + */ + float GetAnimationTime() const; + +public: //Signals + + // Button Clicked + + typedef SignalV2< bool ( Button ) > ClickedSignalV2; + + /** + * Signal emitted when the button is touched and the touch point doesn't leave the boundary of the button. + */ + ClickedSignalV2& ClickedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Button( Internal::Button& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Button( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_BUTTON_H__ diff --git a/capi/dali-toolkit/public-api/controls/buttons/push-button.h b/capi/dali-toolkit/public-api/controls/buttons/push-button.h new file mode 100644 index 0000000..eb92f14 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/buttons/push-button.h @@ -0,0 +1,366 @@ +#ifndef __DALI_TOOLKIT_PUSH_BUTTON_H__ +#define __DALI_TOOLKIT_PUSH_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include "button.h" + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +// Forward declarations + +namespace Internal DALI_INTERNAL +{ +// Forward declarations + +class PushButton; +} + +/** + * PushButton provides a button functionality. A PushButton changes its appearance when is pressed and returns to its original when is released. + * + * By default a PushButton emits a PushButton::PressedSignal() signal when the button is pressed, a Button::ClickedSignal() signal when it's clicked + * and a PushButton::ReleasedSignal() signal when it's released or having pressed it, the touch point leaves the boundary of the button. + * + * PushButton provides the following properties which modify signals emitted: + *
    + *
  • \e autorepeating + * + * When \e autorepeating is set to \e true, a PushButton::PressedSignal(), PushButton::ReleasedSignal() and Button::ClickedSignal() signals are emitted at regular + * intervals while the button is touched. + * + * The intervals could be modified with the PushButton::SetInitialAutoRepeatingDelay and PushButton::SetNextAutoRepeatingDelay methods. + * + * A \e toggle button can't be \e autorepeating. If the \e autorepeating property is set to \e true, then the \e toggle property is set to + * false but no signal is emitted. + * + *
  • \e toggle + * + * When \e toggle is set to \e true, a PushButton::ToggledSignal() signal is emitted, with the toggle state, every time the button is touched instead + * of emit PushButton::PressedSignal(), Button::ClickedSignal() and PushButton::ReleasedSignal() signals. + * + * An \e autorepeating button can't be \e toggle. If the \e toggle property is set to \e true, then the \e autorepeating property is set to false. + *
+ * + * The button's appearance could be modified by setting images or actors with PushButton::SetButtonImage, PushButton::SetBackgroundImage, + * PushButton::SetPressedImage, PushButton::SetDimmedBackgroundImage and PushButton::SetDimmedImage or setting a text with + * PushButton::SetLabelText. + * + * The \e background is always shown and doesn't change if the button is pressed or released. The \e button image is shown over the \e background image when the + * button is not pressed and is replaced by the \e pressed image when the button is pressed. The text label is placed always on the top of all images. + * + * When the button is dimmed, \e background, \e button and \e pressed images are replaced by their \e dimmed images. + * + * The methods used to modify the button's appearance could receive Dali::Actor objects as a parameter, so more complex images could be defined. + * + * Is not mandatory set all images. A button could be defined only by setting its \e background image or by setting its \e background and \e pressed images. + */ +class PushButton : public Button +{ +public: + + //Signal Names + static const char* const SIGNAL_TOGGLED; + static const char* const SIGNAL_PRESSED; + static const char* const SIGNAL_RELEASED; + + //Action Names + static const char* const ACTION_PUSH_BUTTON_CLICK; + +public: + + /** + * Create an uninitialized PushButton; this can be initialized with PushButton::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + PushButton(); + + /** + * Copy constructor. + */ + PushButton( const PushButton& pushButton ); + + /** + * Assignment operator. + */ + PushButton& operator=( const PushButton& pushButton ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~PushButton(); + + /** + * Create an initialized PushButton. + * @return A handle to a newly allocated Dali resource. + */ + static PushButton New(); + + /** + * Downcast an Object handle to PushButton. If handle points to a PushButton the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a PushButton or an uninitialized handle + */ + static PushButton DownCast( BaseHandle handle ); + + /** + * Sets the \e autorepeating property. + * + * If the \e autorepeating property is set to \e true, then the \e toggle property is set to false + * but no signal is emitted. + * + * @param[in] autoRepeating \e autorepeating property. + */ + void SetAutoRepeating( bool autoRepeating ); + + /** + * @return \e true if the \e autorepeating property is set. + */ + bool IsAutoRepeating() const; + + /** + * Sets the initial autorepeating delay. + * + * By default this value is set to 0.15 seconds. + * + * @pre initialAutoRepeatingDelay must be greater than zero. + * @param[in] initialAutoRepeatingDelay in seconds. + */ + void SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay ); + + /** + * @return the initial autorepeating delay in seconds. + */ + float GetInitialAutoRepeatingDelay() const; + + /** + * Sets the next autorepeating delay. + * + * By default this value is set to 0.05 seconds. + * + * @pre nextAutoRepeatingDelay must be greater than zero. + * @param[in] nextAutoRepeatingDelay in seconds. + */ + void SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay ); + + /** + * @return the next autorepeating delay in seconds. + */ + float GetNextAutoRepeatingDelay() const; + + /** + * Sets the \e toggle property. + * + * If the \e toggle property is set to \e true, then the \e autorepeating property is set to false. + * + * @param[in] toggle property. + */ + void SetToggleButton( bool toggle ); + + /** + * @return \e true if the \e toggle property is set. + */ + bool IsToggleButton() const; + + /** + * Sets the button as toggled or not toggled. + * + * \e toggle property must be set to \e true. + * + * Emits a PushButton::ToggledSignal() signal. + * + * @param[in] toggle state. + */ + void SetToggled( bool toggle ); + + /** + * @return \e true if the \e toggle property is set and the button is toggled. + */ + bool IsToggled() const; + + /** + * Sets the button image. + * + * @param[in] image The button image. + */ + void SetButtonImage( Image image ); + + /** + * @copydoc SetButtonImage( Image image ) + */ + void SetButtonImage( Actor image ); + + /** + * Gets the button image. + * @return An actor with the button image. + */ + Actor GetButtonImage() const; + + /** + * Sets the background image. + * + * @param[in] image The background image. + */ + void SetBackgroundImage( Image image ); + + /** + * @copydoc SetBackgroundImage( Image image ) + */ + void SetBackgroundImage( Actor image ); + + /** + * Gets the background image. + * @return An actor with the background image. + */ + Actor GetBackgroundImage() const; + + /** + * Sets the pressed image. + * + * @param[in] image The pressed image. + */ + void SetPressedImage( Image image ); + + /** + * @copydoc SetPressedImage( Image image ) + */ + void SetPressedImage( Actor image ); + + /** + * Gets the pressed image. + * @return An actor with the pressed image. + */ + Actor GetPressedImage() const; + + /** + * Sets the dimmed background image. + * + * @param[in] image The dimmed background image. + */ + void SetDimmedBackgroundImage( Image image ); + + /** + * @copydoc SetDimmedBackgroundImage( Image image ) + */ + void SetDimmedBackgroundImage( Actor image ); + + /** + * Gets the dimmed background image. + * @return An actor with the dimmed background image. + */ + Actor GetDimmedBackgroundImage() const; + + /** + * Sets the dimmed button image. + * + * @param[in] image The dimmed button image. + */ + void SetDimmedImage( Image image ); + + /** + * @copydoc SetDimmedImage( Image image ) + */ + void SetDimmedImage( Actor image ); + + /** + * Gets the dimmed image. + * @return An actor with the dimmed image. + */ + Actor GetDimmedImage() const; + + /** + * Sets the text label. + * + * @param[in] text Label text. + */ + void SetLabelText( const std::string& text ); + + /** + * @copydoc SetLabelText( const std::string& text ) + */ + void SetLabelText( Actor text ); + + /** + * Gets the label text. + * @return An actor with the label text. + */ + Actor GetLabelText() const; + +public: //Signals + + // PushButton Toggled + + typedef SignalV2< bool ( Button, bool ) > ToggledSignalV2; + + // PushButton Pressed + + typedef SignalV2< bool ( Button ) > PressedSignalV2; + + // PushButton Released + + typedef SignalV2< bool ( Button ) > ReleasedSignalV2; + + /** + * Signal emitted when the \e toggle property is set and the button is touched. + */ + ToggledSignalV2& ToggledSignal(); + + /** + * Signal emitted when the button is touched. + */ + PressedSignalV2& PressedSignal(); + + /** + * Signal emitted when the button is touched and the touch point leaves the boundary of the button. + */ + ReleasedSignalV2& ReleasedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + PushButton( Internal::PushButton& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + PushButton( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_PUSH_BUTTON_H__ diff --git a/capi/dali-toolkit/public-api/controls/cluster/cluster-style.h b/capi/dali-toolkit/public-api/controls/cluster/cluster-style.h new file mode 100644 index 0000000..fccedc8 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/cluster/cluster-style.h @@ -0,0 +1,174 @@ +#ifndef __DALI_TOOLKIT_CLUSTER_STYLE_H__ +#define __DALI_TOOLKIT_CLUSTER_STYLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +struct TimePeriod; +class Actor; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ClusterStyle; +class ClusterStyleRandom; +} + +class ClusterStyle; + +typedef IntrusivePtr ClusterStylePtr; + +/** + * A ClusterStyle describes the constraints, which are imposed on the child actors in the cluster. + */ +class ClusterStyle : public Dali::BaseHandle +{ +public: + + static const unsigned int UNLIMITED_CHILDREN; + +public: + + /** + * Virtual destructor. + */ + virtual ~ClusterStyle(); + + /** + * Query the maximum number of children this Style can handle. + * If return value is UNLIMITED_CHILDREN, then this style has no + * limit. + * @return The maximum number of children. + */ + unsigned int GetMaximumNumberOfChildren() const; + + /** + * Applies style (position) to child actor over a specified time duration. + * + * @param[in] child The child actor to apply + * @param[in] index The style position index for the actor to transform to. + * @param[in] alpha The alpha function to use. + * @param[in] durationSeconds The time period to apply this style. + */ + void ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * Applies style to background actor over a specified time duration. + * @param[in] background The background actor to apply + * @param[in] alpha The alpha function to use. + * @param[in] durationSeconds The time period to apply this style. + */ + void ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * Applies style to title actor over a specified time duration. + * @param[in] title The title actor to apply + * @param[in] alpha The alpha function to use. + * @param[in] durationSeconds The time period to apply this style. + */ + void ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds); + +protected: + + /** + * Create a new ClusterStyle; Only derived versions are instantiatable. + */ + ClusterStyle(); + +public: // Not intended for application developers + + /** + * This constructor is used by Dali New() methods + * @param [in] internal A pointer to a newly allocated Dali resource + */ + ClusterStyle(Internal::ClusterStyle* internal); +}; + +/** + * A ClusterStyle describes the constraints, which are imposed on the child actors in the cluster. + */ +class ClusterStyleStandard : public ClusterStyle +{ +public: + + enum StyleType + { + ClusterStyle1, + ClusterStyle2, + ClusterStyle3, + ClusterStyle4, + TotalClusterStyles + }; + +public: + + /** + * Create an initialized style + */ + static ClusterStyleStandard New(StyleType style); + +public: // Not intended for application developers + + /** + * This constructor is used by Dali New() methods + * @param [in] internal A pointer to a newly allocated Dali resource + */ + ClusterStyleStandard(Internal::ClusterStyle* internal); +}; + +/** + * A ClusterStyle describes the constraints, which are imposed on the child actors in the cluster. + */ +class ClusterStyleRandom : public ClusterStyle +{ +public: + + /** + * Create an initialized style + */ + static ClusterStyleRandom New(); + +public: // Not intended for application developers + + /** + * This constructor is used by Dali New() methods + * @param [in] internal A pointer to a newly allocated Dali resource + */ + ClusterStyleRandom(Internal::ClusterStyle* internal); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_CLUSTER_STYLE_H__ diff --git a/capi/dali-toolkit/public-api/controls/control-impl.h b/capi/dali-toolkit/public-api/controls/control-impl.h new file mode 100644 index 0000000..f4643db --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/control-impl.h @@ -0,0 +1,592 @@ +#ifndef __DALI_TOOLKIT_CONTROL_IMPL_H__ +#define __DALI_TOOLKIT_CONTROL_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class StyleChangeProcessor; +class RelayoutControllerImpl; +class KeyInputFocusManager; +} + +typedef std::pair< Actor, Vector2 > ActorSizePair; +typedef std::vector< ActorSizePair > ActorSizeContainer; + +/** + * This is the internal base class for all controls. + * It will provide some common functionality required by all controls. + * Implements ConnectionTrackerInterface so that signals (typically connected to member functions) will + * be disconnected automatically when the control is destroyed. + */ +class ControlImpl : public CustomActorImpl, public ConnectionTrackerInterface +{ +public: + + // Creation + + /** + * Create a new ControlImpl instance that does not require touch by default. + * If touch is required then the user can connect to this class' touch signal. + * @return A handle to the ConntrolImpl instance. + */ + static Control New(); + + // Destruction + + /** + * Virtual destructor + */ + virtual ~ControlImpl(); + + // Actions + + /** + * This method should be overridden by deriving classes when they wish to be notified when they + * are activated. + */ + virtual void OnActivated() { } + + /** + * This method should be overridden by deriving classes when they wish to respond the accessibility + * pan gesture. + * @param[in] gesture The pan gesture. + * @return true if the pan gesture has been consumed by this control + */ + virtual bool OnAccessibilityPan(PanGesture gesture); + + /** + * This method should be overridden by deriving classes when they wish to respond the accessibility + * up and down action (i.e. value change of slider control) + * @param[in] isIncrease Whether the value should be increased or decreased + * @return true if the value changed action has been consumed by this control + */ + virtual bool OnAccessibilityValueChange(bool isIncrease); + + /** + * Sets whether this control supports two dimensional keyboard navigation (i.e. whether it knows how to handle the keyboard + * focus movement between its child actors). The control doesn't support it by default. + * @param[in] isSupported Whether this control supports two dimensional keyboard navigation. + */ + void SetKeyboardNavigationSupport(bool isSupported); + + /** + * Gets whether this control supports two dimensional keyboard navigation. + * @return true if this control supports two dimensional keyboard navigation. + */ + bool IsKeyboardNavigationSupported(); + + /** + * Sets whether this control is a focus group for keyboard navigation (i.e. the scope of keyboard focus movement + * can be limitied to its child actors). The control is not a focus group by default. + * @param[in] isFocusGroup Whether this control is set as a focus group for keyboard navigation. + */ + void SetAsKeyboardFocusGroup(bool isFocusGroup); + + /** + * Gets whether this control is a focus group for keyboard navigation. + * @return true if this control is set as a focus group for keyboard navigation. + */ + bool IsKeyboardFocusGroup(); + + /** + * Gets the next keyboard focusable actor in this control towards the given direction. + * A control needs to override this function in order to support two dimensional keyboard navigation. + * @param[in] currentFocusedActor The current focused actor. + * @param[in] direction The direction to move the focus towards. + * @param[in] loopEnabled Whether the focus movement should be looped within the control. + * @return the next keyboard focusable actor in this control or an empty handle if no actor can be focused. + */ + virtual Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + + /** + * Informs this control that its chosen focusable actor will be focused. This allows the application to + * preform any actions if wishes before the focus is actually moved to the chosen actor. + * @param[in] commitedFocusableActor The commited focusable actor. + */ + virtual void OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor) { } + + /** + * Performs actions as requested using the action name. + * @param[in] object The object on which to perform the action. + * @param[in] actionName The action to perform. + * @param[in] attributes The attributes with which to perfrom this action. + * @return true if action has been accepted by this control + */ + static bool DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes); + +public: + + /** + * @copydoc Dali::Toolkit::Control::KeyEventSignal() + */ + Toolkit::Control::KeyEventSignalV2& KeyEventSignal(); + +protected: + + // Construction + + /** + * Second phase initialization. + */ + void Initialize(); + + // Gesture Detection + + /** + * Allows deriving classes to enable any of the gesture detectors that are available. + * Gesture detection can be enabled one at a time or in bitwise format as shown: + * @code + * EnableGestureDetection(Gesture::Type(Gesture::Pinch | Gesture::Tap | Gesture::Pan)); + * @endcode + * @param[in] type The gesture type(s) to enable. + */ + void EnableGestureDetection(Gesture::Type type); + + /** + * Allows deriving classes to disable any of the gesture detectors. + * Like EnableGestureDetection, this can also be called using bitwise or. + * @param[in] type The gesture type(s) to disable. + * @see EnableGetureDetection + */ + void DisableGestureDetection(Gesture::Type type); + + /** + * If deriving classes wish to fine tune pinch gesture detection then they can access the gesture + * detector through this API and modify the detection. + * @return The pinch gesture detector. + * @pre Pinch detection should have been enabled via EnableGestureDetection(). + * @see EnableGestureDetection + */ + PinchGestureDetector GetPinchGestureDetector() const; + + /** + * If deriving classes wish to fine tune pan gesture detection then they can access the gesture + * detector through this API and modify the detection. + * @return The pan gesture detector. + * @pre Pan detection should have been enabled via EnableGestureDetection(). + * @see EnableGestureDetection + */ + PanGestureDetector GetPanGestureDetector() const; + + /** + * If deriving classes wish to fine tune tap gesture detection then they can access the gesture + * detector through this API and modify the detection. + * @return The tap gesture detector. + * @pre Tap detection should have been enabled via EnableGestureDetection(). + * @see EnableGestureDetection + */ + TapGestureDetector GetTapGestureDetector() const; + + /** + * If deriving classes wish to fine tune long press gesture detection then they can access the + * gesture detector through this API and modify the detection. + * @return The long press gesture detector. + * @pre Long press detection should have been enabled via EnableGestureDetection(). + * @see EnableGestureDetection + */ + LongPressGestureDetector GetLongPressGestureDetector() const; + +private: // For derived classes to override + + /** + * This method is called after the Control has been initialized. Derived classes should do + * any second phase initialization by overriding this method. + */ + virtual void OnInitialize() { } + + /** + * This method should be overridden by deriving classes when they wish to be notified when the + * style changes. + * @param[in] change Information denoting what has changed. + */ + virtual void OnStyleChange(StyleChange change) { } + + /** + * Called whenever a pinch gesture is detected on this control. This can be overridden by + * deriving classes when pinch detection is enabled. The default behaviour is to scale the + * control by the pinch scale. + * @note If overridden, then the default behaviour will not occur. + * @note Pinch detection should be enabled via EnableGestureDetection(). + * @param[in] pinch The pinch gesture. + * @see EnableGestureDetection + */ + virtual void OnPinch(PinchGesture pinch); + + /** + * Called whenever a pan gesture is detected on this control. This should be overridden by + * deriving classes when pan detection is enabled. + * @note There is no default behaviour with panning. + * @note Pan detection should be enabled via EnableGestureDetection(). + * @param[in] pan The pan gesture. + * @see EnableGestureDetection + */ + virtual void OnPan(PanGesture pan) { } + + /** + * Called whenever a tap gesture is detected on this control. This should be overridden by + * deriving classes when tap detection is enabled. + * @note There is no default behaviour with a tap. + * @note Tap detection should be enabled via EnableGestureDetection(). + * @param[in] tap The tap gesture. + * @see EnableGestureDetection + */ + virtual void OnTap(TapGesture tap) { } + + /** + * Called whenever a long press gesture is detected on this control. This should be overridden by + * deriving classes when long press detection is enabled. + * @note There is no default behaviour associated with a long press. + * @note Long press detection should be enabled via EnableGestureDetection(). + * @param[in] longPress The long press gesture. + * @see EnableGestureDetection + */ + virtual void OnLongPress(LongPressGesture longPress) { } + + /** + * Called whenever the control is added to the stage. Could be overridden by derived classes. + */ + virtual void OnControlStageConnection() { } + + /** + * Called whenever the control is removed from the stage. Could be overridden by derived classes. + */ + virtual void OnControlStageDisconnection() { } + + /** + * Called whenever an Actor is added to the control. Could be overridden by derived classes. + * + * @param[in] child The added actor. + */ + virtual void OnControlChildAdd( Actor& child ) { } + + /** + * Called whenever an Actor is removed from the control. Could be overridden by derived classes. + * + * @param[in] child The removed actor. + */ + virtual void OnControlChildRemove( Actor& child ) { } + + /** + * Called whenever the Control's size is set. Could be overridden by derived classes. + * + * @param[in] size The new size. + */ + virtual void OnControlSizeSet( const Vector3& size ) { } + + /** + * Called after the Dali::Stage::SignalMessageQueueFlushed() signal is emitted if this control requested to be relaid-out. + * Should be overridden by derived classes if they need to layout actors differently after certain operations like add or + * remove actors, resize or after changing especific properties. + * + * @param[in] size The allocated size. + * @param[in,out] container The control should add actors to this container that it is not able + * to allocate a size for. + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + +private: // From CustomActorImpl, derived classes can override these. + + /** + * Sends a request to relayout this control. The control will be relaid out after the Dali::Stage::SignalMessageQueueFlushed() signal is emitted. + * + * It calls OnControlStageConnection() to notify derived classes. + * + * @see Dali::CustomActorImpl::OnStageConnection() + */ + virtual void OnStageConnection(); + + /** + * Calls OnControlStageDisconnection() to notify derived classed. + * + * @see Dali::CustomActorImpl::OnStageDisconnection() + */ + virtual void OnStageDisconnection(); + + /** + * Sends a request to relayout this control. The control will be relaid out after the Dali::Stage::SignalMessageQueueFlushed() signal is emitted. + * It calls OnControlChildAdd() to notify derived classes. + * + * @note This method shouldn't be overridden by derived classes. + * + * @param[in] child The added actor. + * + * @see Dali::CustomActorImpl::OnChildAdd(Actor&) + */ + virtual void OnChildAdd(Actor& child); + + /** + * Sends a request to relayout this control. The control will be relaid out after the Dali::Stage::SignalMessageQueueFlushed() signal is emitted. + * It calls OnControlChildRemove() to notify derived classes. + * + * @note This method shouldn't be overridden by derived classes. + * + * @param[in] child The removed actor. + * + * @see Dali::CustomActorImpl::OnChildRemove(Actor&) + */ + virtual void OnChildRemove(Actor& child); + + /** + * It stores the size set by size negotiation and relayout. It also keeps a backup of the size set through the Actor's API used in the size negotiation. + * It calls the OnControlSizeSet() to notify derived classes. + * + * @param[in] targetSize The new size. + * + * @see Dali::CustomActorImpl::OnSizeSet(const Vector3&) + */ + virtual void OnSizeSet(const Vector3& targetSize); + + /** + * @copydoc Dali::CustomActorImpl::OnSizeAnimation(Animation&, const Vector3&) + */ + virtual void OnSizeAnimation(Animation& animation, const Vector3& targetSize); + + /** + * @copydoc Dali::CustomActorImpl::OnTouchEvent(const TouchEvent&) + */ + virtual bool OnTouchEvent(const TouchEvent& event); + + /** + * @copydoc Dali::CustomActorImpl::OnKeyEvent(const KeyEvent&) + */ + virtual bool OnKeyEvent(const KeyEvent& event); + + /** + * @copydoc Dali::CustomActorImpl::OnMouseWheelEvent(const MouseWheelEvent&) + */ + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event); + + /** + * @copydoc Dali::CustomActorImpl::OnKeyInputFocusGained() + */ + virtual void OnKeyInputFocusGained(); + + /** + * @copydoc Dali::CustomActorImpl::OnKeyInputFocusLost() + */ + virtual void OnKeyInputFocusLost(); + + /** + * @copydoc Dali::CustomActorImpl::GetChildByAlias(const std::string& actorAlias) + */ + virtual Actor GetChildByAlias(const std::string& actorAlias); + +private: + + /** + * Perform the activated action. + * @param[in] attributes The attributes to perfrom this action. + */ + void DoActivatedAction(const PropertyValueContainer& attributes); + +protected: // Construction + + /** + * Create a ControlImpl. + * @param[in] requiresTouchEvents True if the OnTouchEvent() callback is required. + */ + ControlImpl(bool requiresTouchEvents); + +public: + + // Size negotiation + + /** + * @copydoc Control::SetSizePolicy() + */ + void SetSizePolicy( Control::SizePolicy widthPolicy, Control::SizePolicy heightPolicy ); + + /** + * @copydoc Control::GetSizePolicy() + */ + void GetSizePolicy( Control::SizePolicy& widthPolicy, Control::SizePolicy& heightPolicy ) const; + + /** + * @copydoc Control::SetMinimumSize() + */ + void SetMinimumSize( const Vector3& size ); + + /** + * @copydoc Control::GetMinimumSize() + */ + const Vector3& GetMinimumSize() const; + + /** + * @copydoc Control::SetMaximumSize() + */ + void SetMaximumSize( const Vector3& size ); + + /** + * @copydoc Control::GetMaximumSize() + */ + const Vector3& GetMaximumSize() const; + + /** + * @copydoc Control::GetNaturalSize() + */ + virtual Vector3 GetNaturalSize(); + + /** + * @copydoc Control::GetHeightForWidth() + */ + virtual float GetHeightForWidth( float width ); + + /** + * @copydoc Control::GetWidthForHeight() + */ + virtual float GetWidthForHeight( float height ); + + /** + * Retrieves the current Control's size. + * + * @return The control's size. + */ + const Vector3& GetControlSize() const; + + /** + * Retrieves the Control's size set by the Application / Control. + * @return The control's size. + */ + const Vector3& GetSizeSet() const; + + //KeyInput + + /** + * @copydoc Control::SetKeyInputFocus() + */ + void SetKeyInputFocus(); + + /** + * @copydoc Control::HasKeyInputFocus() + */ + bool HasKeyInputFocus(); + + /** + * @copydoc Control::ClearKeyInputFocus() + */ + void ClearKeyInputFocus(); + + /** + * @copydoc ConnectionTrackerInterface::SignalConnected + */ + virtual void SignalConnected( SlotObserver* slotObserver, CallbackBase* callback ); + + /** + * @copydoc ConnectionTrackerInterface::SignalDisconnected + */ + virtual void SignalDisconnected( SlotObserver* slotObserver, CallbackBase* callback ); + + /** + * @copydoc ConnectionTrackerInterface::GetConnectionCount + */ + virtual std::size_t GetConnectionCount() const; + +protected: + + /** + * Sends a request to be relaid-out. This methos is called from OnStageConnection(), OnChildAdd(), OnChildRemove(), SetSizePolicy(), SetMinimumSize() and SetMaximumSize(). + * + * This method could also be called from derived classes every time a control's poperty change and it needs to be relaid-out. + * After the Dali::Stage::SignalMessageQueueFlushed() is emitted a relayout process starts and all controls which called this method will be relaid-out. + * + * @note RelayoutRequest() only sends a request per Control before the Dali::Stage::SignalMessageQueueFlushed() signal is emitted. That means a control will be relaid-out + * only once, even if more than one request is sent between two consecutive signals. + */ + void RelayoutRequest(); + + /** + * Helper method for controls to Relayout their children if they do not know whether that child is + * a control or not. + * + * @param[in] actor The actor to relayout. + * @param[in] size The size to allocate to the actor. + * @param[in,out] container The container that holds actors that have not been allocated a size yet. + */ + static void Relayout( Actor actor, Vector2 size, ActorSizeContainer& container ); + +private: // Used by the RelayoutController + + /** + * Called by the RelayoutController to negotiate the size of a control. The size allocated by the + * the algorithm is passed in which the control must adhere to. A container is passed in as well + * which the control should populate with actors it has not / or does not need to handle in its + * size negotiation. + * + * @param[in] size The allocated size. + * @param[in,out] container The container that holds actors that are fed back into the + * RelayoutController algorithm. + */ + void NegotiateSize( Vector2 size, ActorSizeContainer& container ); + +private: + + /** + * Called by NegotiateSize when the size to allocate to the control has been calculated. + * It calls the OnRelaidOut() method which can be overridden by derived classes. + * + * @param[in] size The allocated size. + * @param[in,out] container The control should add actors to this container that it is not able + * to allocate a size for. + */ + void Relayout( Vector2 size, ActorSizeContainer& container ); + + /** + * Used by the KeyInputFocusManager to emit key event signals. + * @param[in] event The key event. + * @return True if the event was consumed. + */ + bool EmitKeyEventSignal(const KeyEvent& event); + + + +private: + + // Undefined + ControlImpl(const ControlImpl&); + ControlImpl& operator=(const ControlImpl&); + + class Impl; + Impl *mImpl; + + friend class Internal::StyleChangeProcessor; + friend class Internal::RelayoutControllerImpl; ///< Relayout controller needs to call Relayout() which is private. + friend class Internal::KeyInputFocusManager; ///< KeyInputFocusManager needs to call which is private. +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_CONTROL_IMPL_H__ diff --git a/capi/dali-toolkit/public-api/controls/control.h b/capi/dali-toolkit/public-api/controls/control.h new file mode 100644 index 0000000..64812fa --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/control.h @@ -0,0 +1,349 @@ +#ifndef __DALI_TOOLKIT_CONTROL_H__ +#define __DALI_TOOLKIT_CONTROL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +//Forward declarations. + +class ControlImpl; + +/** + * Control is the base class for all controls. + * The implementation of the control must be supplied; see ControlImpl for more details. + * @see ControlImpl + */ +class Control : public CustomActor, public ConnectionTrackerInterface +{ +public: + + //Action Names + static const char* const ACTION_CONTROL_ACTIVATED; + + //Signal Names + static const char* const SIGNAL_KEY_EVENT; + + /** + * Describes how a control could be resized. + */ + enum SizePolicy + { + Fixed, ///< Size can't grow or shrink. + Minimum, ///< Size can grow but shrink up to a minimum level. + Maximum, ///< Size can shrink but grow up to a maximum value. + Range, ///< Size can grow or shrink between a minimum and a maximum values. + Flexible, ///< Size can grow or shrink with no limits. + }; + + /** + * Describes what a control should do when a contained actor/control exceeds the boundary of the control. + */ + enum ExceedPolicy + { + Crop, ///< Control's contents will be cropped. + Shrink, ///< Control's contents will be shrunk. + Scroll ///< Control's contents will be added to a scroll. + }; + + /** + * Describes the direction to move the keyboard focus towards + */ + enum KeyboardFocusNavigationDirection + { + Left, ///< Move keyboard focus towards the left direction + Right, ///< Move keyboard focus towards the right direction + Up, ///< Move keyboard focus towards the up direction + Down ///< Move keyboard focus towards the down direction + }; + + // Typedefs + + // Key Event + typedef SignalV2 KeyEventSignalV2; + +public: // Creation & Destruction + + /** + * Create a new instance of a Control. + * @return A handle to a new Control. + */ + static Control New(); + + /** + * Create an uninitialized Control handle. Only derived versions can be instantiated. + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + Control(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + Control(const Control& uiControl); + + /** + * Virtual destructor. + * Dali::Object derived classes do not contain member data. + */ + virtual ~Control(); + +public: // operators + + /** + * Assignment operator. Changes this handle to point to another real object + */ + Control& operator=( const Control& handle ); + +public: + + /** + * Downcast an Object handle to Control. If handle points to a Control the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Control or an uninitialized handle + */ + static Control DownCast( BaseHandle handle ); + + /** + * Retrieve the Control implementation. + * @return The implementation. + */ + ControlImpl& GetImplementation(); + + /** + * Retrieve the Control implementation. + * @return The implementation. + */ + const ControlImpl& GetImplementation() const; + + /** + * Sets the size policies for the width and height dimensions. + * + * @param[in] widthPolicy Size policy for the width dimension. + * @param[in] heightPolicy Size policy for the height dimension. + */ + void SetSizePolicy( SizePolicy widthPolicy, SizePolicy heightPolicy ); + + /** + * Retrieves the size policies for the width and height dimensions. + * + * @param[out] widthPolicy Width's size policy. + * @param[out] heightPolicy Height's size policy. + */ + void GetSizePolicy( SizePolicy& widthPolicy, SizePolicy& heightPolicy ) const; + + /** + * Sets the minimum size for the control. + * + * @param[in] size The minimum size. + */ + void SetMinimumSize( const Vector3& size ); + + /** + * Retrieves the minimum size. + * + * @return The minimum size. + */ + const Vector3& GetMinimumSize() const; + + /** + * Sets the maximum size + * + * @param[in] size The maximum size. + */ + void SetMaximumSize( const Vector3& size ); + + /** + * Retrieves the maximum size. + * + * @return The maximum size. + */ + const Vector3& GetMaximumSize() const; + + /** + * Works out the natural size. + * + * Natural size is the control's size with any restriction. + * + * @return The natural size. + */ + Vector3 GetNaturalSize(); + + /** + * Works out the control's height for a given width. + * + * @param[in] width The control's width. + * + * @return The control's height for the given width. + */ + float GetHeightForWidth( float width ); + + /** + * Works out the control's width for a given height. + * + * @param[in] height The control's height. + * + * @return The control's width for the given height. + */ + float GetWidthForHeight( float height ); + + /** + * This sets the control to receive key events. The key event can originate from a virtual or physical keyboard. + * @pre The Control has been initialized. + * @pre The Control should be on the stage before setting keyboard focus. + * @return True if the control has foucs, False otherwise. + */ + void SetKeyInputFocus(); + + /** + * Quries whether the control has key input focus. + * Note: The control can be set to have the focus and still not receive all the key events if another control has over ridden it. + * As the key input focus mechanism works like a stack, the top most control receives all the key events, and passes on the + * unhandled events to the controls below in the stack. A control in the stack will regain key input focus when there are no more + * controls above it in the focus stack. + * To query for the conrol which is on top of the focus stack use Dali::Toolkit::KeyInputFocusManager::GetCurrentKeyboardFocusActor() + * @pre The Control has been initialized. + * @pre The Control should be on the stage before setting keyboard focus. + */ + bool HasKeyInputFocus(); + + /** + * Once an actor is Set to receive key input focus this function is called to stop it receiving key events. + * A check is performed to ensure it was previously set, if this check fails then nothing is done. + * @pre The Actor has been initialized. + */ + void ClearKeyInputFocus(); + +//signals +public: + + /** + * This signal is emitted when key event is received + * A callback of the following type may be connected: + * @code + * bool YourCallbackName(Control control, const KeyEvent& event); + * @endcode + * The return value of True, indicates that the touch event should be consumed. + * Otherwise the signal will be emitted on the next sensitive parent of the actor. + * @pre The Control has been initialized. + * @return The signal to connect to. + */ + KeyEventSignalV2& KeyEventSignal(); + +protected: + + /** + * @copydoc ConnectionTrackerInterface::SignalConnected + */ + virtual void SignalConnected( SlotObserver* slotObserver, CallbackBase* callback ); + + /** + * @copydoc ConnectionTrackerInterface::SignalDisconnected + */ + virtual void SignalDisconnected( SlotObserver* slotObserver, CallbackBase* callback ); + + /** + * @copydoc ConnectionTrackerInterface::GetConnectionCount + */ + virtual std::size_t GetConnectionCount() const; + +public: // Not intended for application developers + + /** + * Create an initialised Control. + * @param[in] implementation The implementation for this control. + * @return A handle to a newly allocated Dali resource. + */ + Control(ControlImpl& implementation); + + /** + * This constructor is used by CustomActor within Dali core to create additional Control handles + * using an Internal CustomActor pointer. + * @param [in] internal A pointer to a newly allocated Dali resource + */ + Control(Dali::Internal::CustomActor* internal); + +public: // Templates for Deriving Classes + + /** + * Template to allow deriving controls to DownCast handles to deriving handle classes. + * @tparam T The handle class + * @tparam I The implementation class + * @param[in] handle Handle to an object + * @return Handle to a class T or an uninitialized handle. + * @see DownCast(BaseHandle) + */ + template + static T DownCast( BaseHandle handle ) + { + T result; + + CustomActor custom = Dali::CustomActor::DownCast( handle ); + if ( custom ) + { + CustomActorImpl& customImpl = custom.GetImplementation(); + + I* impl = dynamic_cast(&customImpl); + + if (impl) + { + result = T(customImpl.GetOwner()); + } + } + + return result; + } + + /** + * Template to allow deriving controls to verify whether the Internal::CustomActor* is actually an + * implementation of their class. + * @tparam I The implementation class + * @param[in] internal Pointer to the Internal::CustomActor + */ + template + void VerifyCustomActorPointer(Dali::Internal::CustomActor* internal) + { + // Can have a NULL pointer so we only need to check if the internal implementation is our class + // when there is a value. + if (internal) + { + DALI_ASSERT_DEBUG(dynamic_cast(&CustomActor(internal).GetImplementation())); + } + } + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_CONTROL_H__ diff --git a/capi/dali-toolkit/public-api/controls/default-controls/push-button-factory.h b/capi/dali-toolkit/public-api/controls/default-controls/push-button-factory.h new file mode 100644 index 0000000..63c8177 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/default-controls/push-button-factory.h @@ -0,0 +1,90 @@ +#ifndef __DALI_TOOLKIT_PUSH_BUTTON_FACTORY_H__ +#define __DALI_TOOLKIT_PUSH_BUTTON_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * Creates a push button with the given images. + * Images will be shrunk to fit the button size keeping their aspect ratio. + * @note Images won't be scaled to fill the whole button size. + * @note If an image path is empty, this image is not set to the button. + * + * @param[in] releasedImagePath Image path to be shown when the button is released. + * @param[in] pressedImagePath Image path to be shown when the button is pressed. + * @param[in] backgroundImagePath Image path to be shown as button background. + * @param[in] dimmedReleasedImagePath Image path to be shown when the button is released and dimmed. + * @param[in] dimmedBackgroundImagePath Image path to be shown as button background when the button is dimmed. + */ +PushButton CreatePushButton( const std::string& releasedImagePath, const std::string& pressedImagePath, const std::string& backgroundImagePath, + const std::string& dimmedReleasedImagePath, const std::string& dimmedBackgroundImagePath ); + +/** + * Creates a push button with the given images. + * Images will be shrunk to fit the button size keeping their aspect ratio. + * @note Images won't be scaled to fill the whole button size. + * @note If an image is an empty handle, this image is not set to the button. + * + * @param[in] releasedImageActor Image to be shown when the button is released. + * @param[in] pressedImageActor Image to be shown when the button is pressed. + * @param[in] backgroundImageActor Image to be shown as button background. + * @param[in] dimmedReleasedImageActor Image to be shown when the button is released and dimmed. + * @param[in] dimmedBackgroundImageActor Image to be shown as button background when the button is dimmed. + */ +PushButton CreatePushButton( Actor releasedImageActor, Actor pressedImageActor, Actor backgroundImageActor, + Actor dimmedReleasedImageActor, Actor dimmedBackgroundImageActor ); + +/** + * Creates a push button with the given background image. + * Background image will be shrunk to fit the button size keeping its aspect ratio. + * @note Background image won't be scaled to fill the whole button size. + * + * @param[in] backgroundImagePath Image path to be shown as button background. + */ +PushButton CreatePushButton( const std::string& backgroundImagePath ); + +/** + * Creates a push button with the given background image. + * Background image will be shrunk to fit the button size keeping its aspect ratio. + * @note Background image won't be scaled to fill the whole button size. + * + * @param[in] backgroundImageActor Image to be shown as button background. + */ +PushButton CreatePushButton( Actor backgroundImageActor ); + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_PUSH_BUTTON_FACTORY_H__ diff --git a/capi/dali-toolkit/public-api/controls/default-controls/solid-color-actor.h b/capi/dali-toolkit/public-api/controls/default-controls/solid-color-actor.h new file mode 100644 index 0000000..999692c --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/default-controls/solid-color-actor.h @@ -0,0 +1,55 @@ +#ifndef __DALI_TOOLKIT_SOLID_COLOR_ACTOR_H__ +#define __DALI_TOOLKIT_SOLID_COLOR_ACTOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * Creates a Dali::ImageActor with a solid color, optionally it creates a border. + * + * If the \e border parameter is set to \e true, the Dali::ImageActor's style is set to Dali::ImageActor::STYLE_NINE_PATCH. + * + * @param[in] color The ImageActor's color. + * @param[in] border If \e true, a border is created. By default, the value is set to \e false. + * @param[in] borderColor The color for the ImageActor's border. By default, the value is set to Color::WHITE. + * @param[in] borderSize The size for the ImageActor's border. By default, the value is set to 1 pixel. It supports under 10 pixel for clear result of gl blend + */ +ImageActor CreateSolidColorActor( const Vector4& color, bool border = false, const Vector4& borderColor = Color::WHITE, const unsigned int borderSize = 1 ); + + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SOLID_COLOR_ACTOR_H__ diff --git a/capi/dali-toolkit/public-api/controls/image-view/masked-image-view.h b/capi/dali-toolkit/public-api/controls/image-view/masked-image-view.h new file mode 100644 index 0000000..d01cead --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/image-view/masked-image-view.h @@ -0,0 +1,270 @@ +#ifndef __DALI_TOOLKIT_MASKED_IMAGE_VIEW_H__ +#define __DALI_TOOLKIT_MASKED_IMAGE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class MaskedImageView; +} + +/** + * MaskedImageView displays the result of an image created from a masking operation, which is described below: + * + * - Firstly a target image size is chosen. The MaskedImageView handles the creation of this image internally. Initially the + * target image will be filled according to the BACKGROUND_COLOR property. + * - A source image is provided and positioned with the target image area. The position of the source image (in pixels), can + * be controlled using the SOURCE_OFFSET and SOURCE_SIZE properties. By default the source image is centered within the target + * image, and stretched to fill. Note that by default, no attempt is made to maintain the aspect ratio of the source image. + * - A mask image is provided and positioned in the same way as the source image, using the MASK_OFFSET and MASK_SIZE properties. + * - Conceptually the source image is then painted using the mask image as a stencil. Areas of the source which overlap with opaque + * areas of the mask, will be painted into the target image. However where the mask is transparent, the source will be faded away. + * Note that the edge of the mask image will be stretched to cover the entire target area. + * + * Initially MaskedImageView will perform the masking operation on a per-frame basis. This can impact performance, and may be + * avoided by calling Pause() e.g. when the source & mask positions are not being modified. The Resume() method can then be called + * to continue the masking operation when required. + */ +class MaskedImageView : public Control +{ +public: + + /** + * The custom properties installed by this control + */ + enum CustomProperty + { + BACKGROUND_COLOR, ///< Name "background-color", type VECTOR4 + SOURCE_SIZE, ///< Name "source-size", type VECTOR2 + SOURCE_OFFSET, ///< Name "source-offset", type VECTOR2 + MASK_SIZE, ///< Name "mask-size", type VECTOR2 + MASK_OFFSET, ///< Name "mask-offset", type VECTOR2 + + CUSTOM_PROPERTY_COUNT + }; + + enum EditMode + { + EDIT_DISABLED, + EDIT_SOURCE, + EDIT_MASK + }; + + enum ImageRotation + { + ROTATE_0, ///< No rotation + ROTATE_90, ///< Image is rotated clockwise by 90 degrees + ROTATE_180, ///< Image is rotated clockwise by 180 degrees + ROTATE_270 ///< Image is rotated clockwise by 270 degrees + }; + + static const float DEFAULT_MAXIMUM_SOURCE_SCALE; ///< Default SetMaximumSourceScale() value + + /** + * Creates an empty MaskedImageView handle + */ + MaskedImageView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + MaskedImageView( const MaskedImageView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + MaskedImageView& operator=( const MaskedImageView& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~MaskedImageView(); + + /** + * Create the MaskedImageView control + * @param[in] targetWidth The width of the target image + * @param[in] targetHeight The height of the target image + * @param[in] sourceImage The source image + * @param[in] maskImage The mask image + * @return A handle to the MaskedImageView control. + */ + static MaskedImageView New( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ); + + /** + * Downcast an Object handle to MaskedImageView. If handle points to an MaskedImageView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a MaskedImageView or an uninitialized handle + */ + static MaskedImageView DownCast( BaseHandle handle ); + + /** + * Set the image used as a source in the masking operation + * @param[in] sourceImage The source image + */ + void SetSourceImage( Image sourceImage ); + + /** + * Retrieve the image used as a source in the masking operation + * @return sourceImage The source image + */ + Image GetSourceImage(); + + /** + * Set the image used as a mask in the masking operation + * @param[in] maskImage The mask image + */ + void SetMaskImage( Image maskImage ); + + /** + * Retrieve the image used as a mask in the masking operation + * @return sourceImage The mask image + */ + Image GetMaskImage(); + + /** + * Get the property index for a custom MaskedImageView property. + * @param[in] customProperty A custom property enum defined in this class. + * @return The property index e.g. for use with Animation::AnimateTo() + */ + Property::Index GetPropertyIndex( CustomProperty customProperty ) const; + + /** + * Pause the masking operation to improve performance. + * Call this when the source & mask positions etc. are not being modified. + */ + void Pause(); + + /** + * Resume the masking operation. + */ + void Resume(); + + /** + * Query whether the masking operation has been paused. + * @return True if the masking operation has been paused. + */ + bool IsPaused() const; + + /** + * Enable or disable an edit mode. The default is EDIT_DISABLED. + * @param[in] editMode The edit mode required. + */ + void SetEditMode( EditMode editMode ); + + /** + * Query which edit mode is enabled. + */ + EditMode GetEditMode() const; + + /** + * Set the aspect ratio to be preserved when editing the source image. + * @param[in] widthOverHeight The aspect ratio i.e. width divided by height. If a value + * of zero or less is set, then the aspect ratio of the source image will be ignored. + */ + void SetSourceAspectRatio( float widthOverHeight ); + + /** + * Query the aspect ratio preserved when editing the source image. + * @return The aspect ratio (width divided by height) or zero if no aspect ratio is set. + */ + float GetSourceAspectRatio() const; + + /** + * Set the maximum scale applied when editing the source image. + * The minimum scale is implied by the target width/height i.e. the source image will + * always fill that area when edit mode is enabled. + * @param[in] scale The maximum scale. + */ + void SetMaximumSourceScale( float scale ); + + /** + * Query the maximum scale applied when editing the source image. + * @return The maximum scale. + */ + float GetMaximumSourceScale() const; + + /** + * Set the rotation applied to the source image. + * @param[in] rotation The new rotation; by default the source image is not rotated (ROTATE_0). + */ + void SetSourceRotation( ImageRotation rotation ); + + /** + * Query the rotation applied to the source image. + * @return The current rotation. + */ + ImageRotation GetSourceRotation() const; + +public: /* Signals */ + + typedef SignalV2< void (MaskedImageView& source) > MaskedImageViewSignal; + + /** + * Signal emitted when the render task which targets the frame buffer of the masked image has finished. + * This signal carries information of the control handle to the callback function. + */ + MaskedImageViewSignal& MaskFinishedSignal(); + + /** + * @deprecated Use MaskFinishedSignal() instead. + * Signal emitted when the render task which targets the frame buffer of the masked image has finished. + */ + Dali::RenderTask::RenderTaskSignalV2& RenderFinishedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + MaskedImageView(Internal::MaskedImageView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + MaskedImageView(Dali::Internal::CustomActor* internal); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_MASKED_IMAGE_VIEW_H__ diff --git a/capi/dali-toolkit/public-api/controls/popup/popup.h b/capi/dali-toolkit/public-api/controls/popup/popup.h new file mode 100644 index 0000000..968acb9 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/popup/popup.h @@ -0,0 +1,277 @@ +#ifndef __DALI_TOOLKIT_POPUP_H__ +#define __DALI_TOOLKIT_POPUP_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Popup; +} + +class Button; + +/** + * Popup contains content that can come into focus when + * activated, and out of focus when deactivated. + * + * Content: + * + * The content within a popup consists of: + * + * 1. Title + * 2. Buttons + * 3. Background/Frame (i.e. Scale-9 image) + * 4. Custom Content (Actors) + * + * All of which are optional. + * + * States: + * + * A popup can be in a number of states: + * + * 1. HIDE (invisible) + * 2. SHOW (visible at normal size) + * 3. SHOW_MAXIMIZED (visible occupying full parent size) + * 4. Or custom defined. + * + * Transition Effects: + * + * A popup can use various custom transition effects e.g. + * + * Alpha fade, Scaling transition, position/rotation, shader effects. + * + */ +class Popup : public Control +{ + +public: + + //Signal Names + static const char* const SIGNAL_TOUCHED_OUTSIDE; + static const char* const SIGNAL_HIDDEN; + + /** + * The names of custom properties installed by this control. + */ + // Property Names + static const char* const PROPERTY_TITLE; ///< name "title", type string. + static const char* const PROPERTY_STATE; ///< name "state", type string. + + /** + * Current popup state + */ + enum PopupState + { + POPUP_NONE, ///< Init status + POPUP_HIDE, ///< Hidden (not visible) + POPUP_SHOW, ///< Shown (visible in default size) + }; + + typedef SignalV2< void () > TouchedOutsideSignalV2; + typedef SignalV2< void () > HiddenSignalV2; + + /** + * Signal emitted when user has touched outside of the Dialog. + */ + TouchedOutsideSignalV2& OutsideTouchedSignal(); + + /** + * Signal emitted when popup has been hidden. + */ + HiddenSignalV2& HiddenSignal(); + +public: + + /** + * Creates an empty Popup handle + */ + Popup(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + Popup( const Popup& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + Popup& operator=( const Popup& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Popup(); + + /** + * Create the Poup control + * @return A handle to the Popup control. + */ + static Popup New(); + + /** + * Downcast an Object handle to Popup. If handle points to a Popup the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Popup or an uninitialized handle + */ + static Popup DownCast( BaseHandle handle ); + +public: + + /** + * Sets the background image for this Popup. + * + * The background is resized (stretched according to scale settings), + * to the size of the Popup. + * + * @param[in] image The Background ImageActor to cover background + */ + void SetBackgroundImage( Actor image ); + + /** + * Sets a title for this Popup. + * + * By default a TextView is created with following settings: black color, split-by-word multi-line policy and split exceed policy. + * + * @param[in] text The text to appear as the heading for this Popup + */ + void SetTitle( const std::string& text ); + + /** + * Sets a title for this Popup. + * + * @param[in] titleActor The TextView to appear as the heading for this Popup + */ + void SetTitle( TextView titleActor ); + + /** + * Gets the text (TextView) for this Popup. + * + * @return The TextView representing this popup is returned. + */ + TextView GetTitle() const; + + /** + * Adds a button to this Popup. + * + * Buttons are added to the bottom of the Popup and Centered. + * + * By default the first button added will have the focus, and the focus will + * shift to other buttons based on the sequence in which they are added to the popup. + * + * @param[in] button The button to be added to this Popup + */ + void AddButton( Button button ); + + /** + * Sets state of Popup, such as HIDE, and SHOW. + * + * The Popup will instantaneously jump to this state. + * + * @param[in] state The state of the popup + */ + void SetState( PopupState state ); + + /** + * Sets state of Popup, such as HIDE, and SHOW. + * + * The Popup will smoothly animate to this state. + * + * @param[in] state The state of the popup + * @param[in] duration The time to animate to this new state. + */ + void SetState( PopupState state, float duration ); + + /** + * Gets the state of the popup. + * @return The state of the popup. + */ + PopupState GetState() const; + + /** + * Shows the popup. + * + * The Popup will animate to the SHOW state + */ + void Show(); + + /** + * Hides the popup. + * + * The Popup will animate to the HIDE state + */ + void Hide(); + + /** + * Shows the tail. + * + * The tail position is specified relative to it's Parent. + * To display at top center for instace, pass: + * + * ParentOrigin::TOP_CENTER + * + * @note The tail images are defined inside PopupStyle as + * tailUpImage, tailDownImage, tailLeftImage, and tailRightImage + * + * @param[in] position A position around the perimeter of the Parent. + */ + void ShowTail(const Vector3& position); + + /** + * Hides the tail. + */ + void HideTail(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Popup(Internal::Popup& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Popup( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_POPUP_H__ diff --git a/capi/dali-toolkit/public-api/controls/scroll-component/scroll-bar.h b/capi/dali-toolkit/public-api/controls/scroll-component/scroll-bar.h new file mode 100755 index 0000000..4714448 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scroll-component/scroll-bar.h @@ -0,0 +1,125 @@ +#ifndef __DALI_TOOLKIT_SCROLL_BAR_H__ +#define __DALI_TOOLKIT_SCROLL_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +// Forward declarations + +namespace Internal DALI_INTERNAL +{ +// Forward declarations + +class ScrollBar; +} + +/** + * ScrollBar is a UI component that can be added to the sides of the scrollable controls + * indicating the current scroll position. + */ +class ScrollBar : public ScrollComponent +{ + +public: + + /** + * Create an uninitialized ScrollBar; this can be initialized with ScrollBar::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + * or horizontally (false) + */ + ScrollBar(); + + /** + * Copy constructor. + */ + ScrollBar( const ScrollBar& scrollBar ); + + /** + * Assignment operator. + */ + ScrollBar& operator=( const ScrollBar& scrollBar ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~ScrollBar(); + + /** + * Create an initialized ScrollBar + * @param[in] container Reference to the container of scroll bar + * @param[in] vertical Whether ScrollBar should be oriented vertically (true) + * or horizontally (false) + * @return A pointer to the created ScrollBar. + */ + static ScrollBar New(Scrollable& container, bool vertical); + + /** + * Downcast an Object handle to ScrollBar. If handle points to a ScrollBar the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollBar or an uninitialized handle + */ + static ScrollBar DownCast( BaseHandle handle ); + + /** + * Show ScrollBar + */ + void Show(); + + /** + * Hide ScrollBar + */ + void Hide(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ScrollBar( Internal::ScrollBar& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ScrollBar( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_BAR_H__ diff --git a/capi/dali-toolkit/public-api/controls/scroll-component/scroll-component.h b/capi/dali-toolkit/public-api/controls/scroll-component/scroll-component.h new file mode 100644 index 0000000..b1674b7 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scroll-component/scroll-component.h @@ -0,0 +1,101 @@ +#ifndef __DALI_TOOLKIT_SCROLL_COMPONENT_H__ +#define __DALI_TOOLKIT_SCROLL_COMPONENT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollComponent; +} + +/** + * Base class for derived ScrollComponents + * ScrollComponents such as ScrollBar are derived from this class. + * To instantiate these ScrollBars and other derived components + */ +class ScrollComponent : public Control +{ + +public: + /** + * Create an uninitialized ScrollComponent; this can be initialized with ScrollComponent::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + ScrollComponent(); + + /** + * Copy constructor. + */ + ScrollComponent( const ScrollComponent& scrollComponent ); + + /** + * Assignment operator. + */ + ScrollComponent& operator=( const ScrollComponent& scrollComponent ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~ScrollComponent(); + + /** + * Downcast an Object handle to ScrollComponent. If handle points to a ScrollComponent the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollComponent or an uninitialized handle + */ + static ScrollComponent DownCast( BaseHandle handle ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ScrollComponent( Internal::ScrollComponent& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ScrollComponent( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_COMPONENT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.h b/capi/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.h new file mode 100644 index 0000000..b6b4d1b --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.h @@ -0,0 +1,288 @@ +#ifndef __DALI_TOOLKIT_GRID_LAYOUT_H__ +#define __DALI_TOOLKIT_GRID_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class GridLayout; + +typedef IntrusivePtr GridLayoutPtr; + +/** + * An ItemView layout which arranges items in a grid. + */ +class GridLayout : public ItemLayout +{ +public: + + typedef boost::function ItemSizeFunction; + + /** + * Create a new grid layout + */ + static GridLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~GridLayout(); + + /** + * Set the number of columns in the layout. + * @param[in] columns The number of columns. + */ + void SetNumberOfColumns(unsigned int columns); + + /** + * Get the number of columns in the layout. + * @return The number of columns. + */ + unsigned int GetNumberOfColumns() const; + + /** + * Set the spacing between rows. + * @param[in] spacing The row spacing. + */ + void SetRowSpacing(float spacing); + + /** + * Get the spacing between rows. + * @return The row spacing. + */ + float GetRowSpacing() const; + + /** + * Set the spacing between columns. + * @param[in] spacing The row spacing. + */ + void SetColumnSpacing(float spacing); + + /** + * Get the spacing between columns. + * @return The row spacing. + */ + float GetColumnSpacing() const; + + /** + * Set the margin in the top of the layout + * @param[in] margin The layout top margin. + */ + void SetTopMargin(float margin); + + /** + * Get the margin in the top of the layout + * @return The layout top margin. + */ + float GetTopMargin() const; + + /** + * Set the margin in the bottom of the layout + * @param[in] margin The layout bottom margin. + */ + void SetBottomMargin(float margin); + + /** + * Get the margin in the bottom of the layout + * @return The layout bottom margin. + */ + float GetBottomMargin() const; + + /** + * Set the margin in the left and right of the layout + * @param[in] margin The layout side margin. + */ + void SetSideMargin(float margin); + + /** + * Get the margin in the left and right of the layout + * @return The layout side margin. + */ + float GetSideMargin() const; + + /** + * Set the gap of items in the Z axis in different columns. + * @param[in] gap The gap of items. + */ + void SetZGap(float gap); + + /** + * Get the gap of items in the Z axis in different columns. + * @return The gap of items. + */ + float GetZGap() const; + + /** + * Set the function used to calculate the item-size, for a given layout-size. + * @param[in] function The item-size function. + */ + void SetItemSizeFunction(ItemSizeFunction function); + + /** + * Get the function used to calculate the item-size. + * @return The item-size function. + */ + ItemSizeFunction GetItemSizeFunction() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + + /** + * @copydoc ItemLayout::GetClosestOnScreenLayoutPosition() + */ + virtual float GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize); + + /** + * @copydoc ItemLayout::GetNextFocusItemID() + */ + virtual int GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also GridLayout::New() + */ + GridLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_GRID_LAYOUT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h new file mode 100644 index 0000000..def729d --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_ITEM_FACTORY_H__ +#define __DALI_TOOLKIT_ITEM_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * ItemFactory is an abstract interface for providing actors to ItemView. + * Each actor is identified by a unique ID, and has a linear order from 0 to GetNumberOfItems()-1. + */ +class ItemFactory +{ +public: + + /** + * Virtual destructor + */ + virtual ~ItemFactory() = 0; + + /** + * Query the number of items available from the factory. + * The maximum available item has an ID of GetNumberOfItems() - 1. + */ + virtual unsigned int GetNumberOfItems() = 0; + + /** + * Create an Actor to represent a visible item. + * @param[in] itemId The ID of the newly visible item. + * @return An actor, or an uninitialized pointer if the ID is out of range. + */ + virtual Actor NewItem(unsigned int itemId) = 0; +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_ITEM_FACTORY_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h new file mode 100644 index 0000000..760de32 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h @@ -0,0 +1,367 @@ +#ifndef __DALI_TOOLKIT_ITEM_LAYOUT_H__ +#define __DALI_TOOLKIT_ITEM_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class ItemLayout; + +typedef IntrusivePtr ItemLayoutPtr; + +typedef std::vector ItemLayoutContainer; +typedef ItemLayoutContainer::iterator ItemLayoutIter; +typedef ItemLayoutContainer::const_iterator ItemLayoutConstIter; + +struct ItemRange +{ + /** + * Create a range of item identifiers. + * @param[in] beginItem The first item within the range. + * @param[in] endItem The past-the-end item. + */ + ItemRange(unsigned int beginItem, unsigned int endItem) + : begin(beginItem), + end(endItem) + { + } + + /** + * Copy Constructor. + * @param[in] copy ItemRange we should copy from. + */ + ItemRange(const ItemRange& copy) + : begin(copy.begin), + end(copy.end) + { + } + + /** + * Assignment operator. + * @param[in] range The Range to assign from. + * @return The updated range. + */ + ItemRange& operator=(const ItemRange& range) + { + begin = range.begin; + end = range.end; + return *this; + } + + /** + * Test whether an item is within the range. + * @param[in] itemId The item identifier. + * @return True if the item is within the range. + */ + bool Within(unsigned int itemId) + { + return itemId >= begin && + itemId < end; + } + + /** + * Create the intersection of two ranges. + * @param[in] second The second range. + * @return The intersection. + */ + ItemRange Intersection(const ItemRange& second) + { + ItemRange intersection(0u, 0u); + + // If the ranges intersect + if ( (begin < second.end && end > second.begin) || + (second.begin < end && second.end > begin) ) + { + intersection.begin = std::max(begin, second.begin); + intersection.end = std::min(end, second.end); + } + + return intersection; + } + + unsigned int begin; + unsigned int end; +}; + +/** + * An ItemLayout describes the constraints, which are imposed on items in the layout. + * - Potentially visible items are represented by Actors, created for ItemView by the ItemFactory. + * - Constraints are applied after ItemView activates a layout. + * + * An ItemLayout also describes the direction of input gestures, used to scroll through the layout. + * Whilst scrolling, the layout provides a range of items that are within a layout-area (3D bounding volume). + */ +class ItemLayout : public RefObject +{ +public: + + typedef boost::function BoolFunction; + typedef boost::function Vector3Function; + typedef boost::function Vector4Function; + typedef boost::function QuaternionFunction; + + /** + * Virtual destructor. + */ + virtual ~ItemLayout(); + + /** + * Set the orientation of the layout. + * @param[in] orientation The orientation of the layout. + */ + void SetOrientation(ControlOrientation::Type orientation); + + /** + * Query the orientation of the layout. + * @return the orientation of the layout. + */ + ControlOrientation::Type GetOrientation() const; + + /** + * Query the minimum valid layout position; this is a negative value. + * When scrolling, the first item will move within the range 0 to GetMinimumLayoutPosition(). + * @param[in] numberOfItems The current number of items in the layout. + * @param[in] layoutSize The size of the layout area. + * @return The minimum layout position. + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const = 0; + + /** + * Query the closest anchor position for the given layout position. + * This anchor position is the position where all the items in the layout are aligned to + * their rounded layout positions in integer. + * @param[in] layoutPosition The layout position. + * @return The closest anchor position for the given layout position. + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const = 0; + + /** + * Query the layout position for the first item in the layout to move to when the layout + * needs to scroll to a particular item. + * @param[in] itemId The ID of an item in the layout. + * @return The layout position for the first item in the layout to move to. + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const = 0; + + /** + * Query the items within a given layout-area. + * @param[in] firstItemPosition The layout-position of the first item in the layout. + * @param[in] layoutSize The size of the layout area. + * @return The ID of the first & last visible item. + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const = 0; + + /** + * Get the closest layout position to bring an item onto the screen. If the item is already fully on the screen + * this function will return the current layout position. + * + * This function is used by systems such as KeyboardFocusManager to bring the next focusable item into view and all + * layout implementations should provide their own version of this function to ensure proper functionality of + * internal toolkit systems. + * + * @param[in] itemID id of the item to bring within the viewable screen area + * @param[in] currentLayoutPosition the current layout position of the item view instance + * @param[in] layoutSize the current size of the item view instance + */ + virtual float GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize); + + /** + * Query the number of items that should be reserved, for scrolling purposes. + * @param[in] layoutSize The size of the layout area. + * @return The number of extra items. ItemView will populate itself with actors within the layout-area + * (see GetItemsWithinArea), plus this number of additional items on either-side. + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const = 0; + + /** + * Retrieve the target size of an item in the layout. + * @note layout-position is not provided as a parameter, since applying size constraints is not recommended. + * Animating to target-sizes is preferable, since this allows controls to perform layouting without constraints. + * @param[in] itemId The ID of an item in the layout. + * @param[in] layoutSize The layout size + * @param[out] itemSize The target size of an item, or an uninitialized value. + * @return Whether the item size is available or not + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const = 0; + + /** + * Retrieve the resize animation in the layout. + * @note This allows the layout to provide its own resize animation. + * @param[in] animation The resize animation, not owned by the layout + * @param[in] actor The actor to animate + * @param [in] size The target size. + * @param [in] durationSeconds The duration of the resizing. + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const = 0; + + /** + * Retrieve the position constraint function of an item in the layout. + * The constraint will be applied when the item is created or the layout is activated. + * @param[in] itemId The ID of an item in the layout. + * @param[out] constraint The position constraint function of an item, or an uninitialized function pointer. + * @return Whether the position constraint function of an item is available or not + */ + virtual bool GetPositionConstraint(unsigned int itemId, Vector3Function& constraint) const = 0; + + /** + * Retrieve the rotation constraint function of an item in the layout. + * The constraint will be applied when the item is created or the layout is activated. + * @param[in] itemId The ID of an item in the layout. + * @param[out] constraint The rotation constraint function of an item, or an uninitialized function pointer. + * @return Whether the rotation constraint function of an item is available or not + */ + virtual bool GetRotationConstraint(unsigned int itemId, QuaternionFunction& constraint) const = 0; + + /** + * Retrieve the scale constraint function of an item in the layout. + * The constraint will be applied when the item is created or the layout is activated. + * @param[in] itemId The ID of an item in the layout. + * @param[out] constraint The scale constraint function of an item, or an uninitialized function pointer. + * @return Whether the scale constraint function of an item is available or not + */ + virtual bool GetScaleConstraint(unsigned int itemId, Vector3Function& constraint) const = 0; + + /** + * Retrieve the color constraint function of an item in the layout. + * The constraint will be applied when the item is created or the layout is activated. + * @param[in] itemId The ID of an item in the layout. + * @param[out] constraint The color constraint function of an item, or an uninitialized function pointer. + * @return Whether the color constraint function of an item is available or not + */ + virtual bool GetColorConstraint(unsigned int itemId, Vector4Function& constraint) const = 0; + + /** + * Retrieve the visibility constraint function of an item in the layout. + * The constraint will be applied when the item is created or the layout is activated. + * @param[in] itemId The ID of an item in the layout. + * @param[out] constraint The visibility constraint function of an item, or an uninitialized function pointer. + * @return Whether the visibility constraint function of an item is available or not + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, BoolFunction& constraint) const = 0; + + /** + * Query the scroll direction of the layout. + * When an input gesture follows this direction, the layout-position of items will be increased. + * If the input gesture points in the opposite direction, then the layout-positions will decrease. + * @return The scroll direction in degrees. + */ + virtual Degree GetScrollDirection() const = 0; + + /** + * Tells scroll components how to interpolate our logical scroll position as a screen x/y direction + * + * Application developer wants to use -ve y, +ve x as up direction and +ve y, -ve x as down direction scroll values in a + * vertical scroll type effect (SpiralLayout). This means that scroll bar/overshoot indicator should be affected by y-axis. + * Returning (0.0f, 0.0f) for x and (0.0f, -1.0f) for y tells us that we need to use the y scroll value to move the scroll bar + * along y axis with y scroll of 0 starting at bottom (due to -1.0f on y), a value of (0.0f, 1.0f) on x axis mask would mean using y scroll value to move scroll bar along x axis + * + * This function is used by numerous objects such as scroll indicators and scroll overshoot indicators and all + * layout implementations should provide their own version of this function to ensure proper functionality of + * internal toolkit systems. + * + * @param[out] scrollHint Vector2 describing how x and y scroll values should be used for x-axis scrolling + */ + virtual void GetXAxisScrollHint(Vector2& scrollHint) const; + + /** + * Tells scroll components how to interpolate our logical scroll position as a screen x/y direction + * + * Application developer wants to use -ve y, +ve x as up direction and +ve y, -ve x as down direction scroll values in a + * vertical scroll type effect (SpiralLayout). This means that scroll bar/overshoot indicator should be affected by y-axis. + * Returning (0.0f, 0.0f) for x and (0.0f, -1.0f) for y tells us that we need to use the y scroll value to move the scroll bar + * along y axis with y scroll of 0 starting at bottom (due to -1.0f on y), a value of (0.0f, 1.0f) on x axis mask would mean using y scroll value to move scroll bar along x axis + * + * This function is used by numerous objects such as scroll indicators and scroll overshoot indicators and all + * layout implementations should provide their own version of this function to ensure proper functionality of + * internal toolkit systems. + * + * @param[out] scrollHint Vector2 describing how x and y scroll values should be used for y-axis scrolling + */ + virtual void GetYAxisScrollHint(Vector2& scrollHint) const; + + /** + * Query the scroll speed factor of the layout. + * This factor is used by the layout to customise its scroll speed while dragging and swiping. + * The factor will be multiplied with the scroll distance of how many pixels in actor coordinate, + * and the layout position of the actors in ItemView will be moved by this result. + * For example, when the speed factor is 0.01, if the scroll distance is 100 pixels, the layout + * position of actors will be moved by 1. + * Therefore, the bigger the factor is, the faster the scroll speed will be. + * + * @return The scroll speed factor of the layout. + */ + virtual float GetScrollSpeedFactor() const = 0; + + /** + * Query the maximum swipe speed in pixels per second. + * Swipe gestures will be clamped when exceeding this speed limit. + * @return speed The maximum swipe speed. + */ + virtual float GetMaximumSwipeSpeed() const = 0; + + /** + * Get the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @return The duration of the flick animation. + */ + virtual float GetItemFlickAnimationDuration() const = 0; + + /** + * Gets the id of the next item for KeyboardFocusManager to focus on depending on the inputted item ID + * + * @param[in] itemID The current focused item + * @param[in] maxItems The maximum number of items in the list + * @param[in] direction The directional key pressed on the keyboard + * @param[in] loopEnabled Whether the KeyboardFocusManager is set to wrap around between first and last item + */ + virtual int GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + +protected: + + /** + * Create a new ItemLayout; Only derived versions are instantiatable. + */ + ItemLayout(); + +protected: + + ControlOrientation::Type mOrientation; +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_ITEM_LAYOUT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h new file mode 100644 index 0000000..8ea9690 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h @@ -0,0 +1,280 @@ +#ifndef __DALI_TOOLKIT_ITEM_VIEW_H__ +#define __DALI_TOOLKIT_ITEM_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ItemView; +} + +class ItemFactory; +class ItemLayout; + +typedef IntrusivePtr ItemLayoutPtr; + +/** + * ItemView is a scrollable layout container. + * Multiple ItemLayouts may be provided, to determine the logical position of each item a layout. + * Actors are provided from an external ItemFactory, to display the currently visible items. + */ +class ItemView : public Scrollable +{ +public: + + /** + * Create an uninitialized ItemView; this can be initialized with ItemView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + ItemView(); + + /** + * Copy constructor. + */ + ItemView( const ItemView& itemView ); + + /** + * Assignment operator. + */ + ItemView& operator=( const ItemView& itemView ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~ItemView(); + + /** + * Create an initialized ItemView. + * @param[in] factory The factory which provides ItemView with items. + * @return A handle to a newly allocated Dali resource. + */ + static ItemView New(ItemFactory& factory); + + /** + * Downcast an Object handle to ItemView. If handle points to a ItemView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ItemView or an uninitialized handle + */ + static ItemView DownCast( BaseHandle handle ); + + /** + * Query the number of layouts. + * @return The number of layouts. + */ + unsigned int GetLayoutCount() const; + + /** + * Add a layout. + * @param[in] layout The layout. + */ + void AddLayout(ItemLayout& layout); + + /** + * Remove a layout. + * @pre layoutIndex is less than GetLayoutCount(). + * @param[in] layoutIndex The index of one of the ItemView layouts. + */ + void RemoveLayout(unsigned int layoutIndex); + + /** + * Retrieve a layout. + * @pre layoutIndex is less than GetLayoutCount(). + * @return The layout + */ + ItemLayoutPtr GetLayout(unsigned int layoutIndex) const; + + /** + * Retrieve the currently active layout, if any. + * @return The layout, or an uninitialized pointer if no layout is active. + */ + ItemLayoutPtr GetActiveLayout() const; + + /** + * Retrieve the current layout-position of an item in the ItemView. + * @param[in] itemId The item identifier. + * @return The current layout-position. + */ + float GetCurrentLayoutPosition(unsigned int itemId) const; + + /** + * Activate one of the layouts; this will resize the ItemView & relayout actors within the ItemView. + * This is done by applying constraints from the new layout, and removing constraints from the previous layout. + * @pre layoutIndex is less than GetLayoutCount(). + * @pre durationSeconds is greater or equal to zero. + * @param[in] layoutIndex The index of one of the ItemView layouts. + * @param[in] targetSize The target ItemView & layout size. + * @param[in] durationSeconds The time taken to relayout in seconds (zero for immediate). + */ + void ActivateLayout(unsigned int layoutIndex, Vector3 targetSize, float durationSeconds); + + /** + * Deactivate the current layout, if any. + * The constraints applied by the layout will be removed. + */ + void DeactivateCurrentLayout(); + + /** + * Set the minimum swipe speed in pixels per second; A pan gesture must exceed this to trigger a swipe. + * @param[in] speed The minimum swipe speed + */ + void SetMinimumSwipeSpeed(float speed); + + /** + * Get the minimum swipe speed in pixels per second. + * @return The minimum swipe speed + */ + float GetMinimumSwipeSpeed() const; + + /** + * Set the minimum swipe distance in actor coordinates; A pan gesture must exceed this to trigger a swipe. + * @param[in] distance The minimum swipe distance. + */ + void SetMinimumSwipeDistance(float distance); + + /** + * Get the minimum swipe distance in actor coordinates. + * @return The minimum swipe distance + */ + float GetMinimumSwipeDistance() const; + + /** + * Set the step of scroll distance in actor coordinates for each mouse wheel event received. + * @param[in] step The step of scroll distance(pixel). + */ + void SetMouseWheelScrollDistanceStep(float step); + + /** + * Get the step of scroll distance in actor coordinates for each mouse wheel event received. + * @return The step of scroll distance(pixel) + */ + float GetMouseWheelScrollDistanceStep() const; + + /** + * Set whether to enable the animation for the layout to scroll to its anchor position after + * dragging or swiping. The anchor position is the position where all the items in the layout + * are aligned to their closest rounded layout positions in integer. + * @param[in] enabled Whether the anchor animation is enabled or not. + */ + void SetAnchoring(bool enabled); + + /** + * Get whether the anchor animation is enabled or not + * @return Whether the anchor animation is enabled or not. + */ + bool GetAnchoring() const; + + /** + * Set the duration of the anchor animation in seconds. This is the time taken to reach the nearest + * anchor position after a drag or swipe gesture ends. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of the anchor animation in seconds. + */ + void SetAnchoringDuration(float durationSeconds); + + /** + * Get the duration of the anchor animation in seconds + * @return The duration of the anchor animation + */ + float GetAnchoringDuration() const; + + /** + * Scroll the current layout to a particular item. + * @pre durationSeconds must be zero or greater; zero means the layout should scroll to the particular item instantly. + * If calling this with zero second of duration immediately after calling ActivateLayout, it might not work unless + * the duration of relayout animation for ActivateLayout is also set to be zero. + * @param[in] itemId The ID of an item in the layout. + * @param[in] durationSeconds How long the scrolling takes in seconds. + */ + void ScrollToItem(unsigned int itemId, float durationSeconds); + + /** + * Set the interval between refreshes, during which new items are requested from ItemFactory. + * @param[in] intervalMilliseconds The refresh interval in milliseconds. + */ + void SetRefreshInterval(unsigned int intervalMilliseconds); + + /** + * Get the interval between refreshes in milliseconds. + * @return The refresh interval + */ + unsigned int GetRefreshInterval() const; + + /** + * Given the Item ID, this returns the accompanying actor. + * @param[in] itemId The Item ID of the actor required. + * @return The Actor corresponding to the Item ID. + */ + Actor GetItem(unsigned int itemId) const; + + /** + * Returns the Item ID of the specified actor. + * @param[in] actor The actor whose Item ID is required. + * @return The Item ID of the item. + * @pre The actor should be an item of ItemView. + */ + unsigned int GetItemId(Actor actor) const; + + /** + * Removes an item with the given ID. + * @pre durationSeconds must be zero or greater; zero means when the item is deleted the remaining items + * will finish the relayout instantly. + * @param[in] itemId The Item ID of the item to remove. + * @param[in] durationSeconds How long the realyout takes in seconds after the item is deleted. + */ + void RemoveItem(unsigned int itemId, float durationSeconds); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ItemView(Internal::ItemView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ItemView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_ITEM_VIEW_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.h new file mode 100644 index 0000000..dc3bbaf --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.h @@ -0,0 +1,139 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_CUBE_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_CUBE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewCubeEffect; +} + +/** + * ScrollView Cube-Effect. + * + * This effect causes Actors to appear to rotate around a 3D cube. + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * | + * Child (1..m) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * upon adding children to pages, the ApplyToActor(...) method should be called. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewCubeEffect : public ScrollViewEffect +{ + +public: + + /** + * Create an initialized ScrollViewCubeEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewCubeEffect New(); + + /** + * Create an uninitialized ScrollViewCubeEffect; this can be initialized with ScrollViewCubeEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewCubeEffect is not allowed. + */ + ScrollViewCubeEffect(); + + /** + * Downcast an Object handle to ScrollViewCubeEffect. If handle points to a ScrollViewCubeEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewCubeEffect or an uninitialized handle + */ + static ScrollViewCubeEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] anchor The anchor point that the child actor should + * rotate around when scrolling + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) as the page is scrolled. + * @param[in] positionSwing The maximum amount the child actor should + * move for each axis (X and Y) as the page is scrolled. + */ + void ApplyToActor(Actor child, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] parentPage The parent page Actor to be used by this effect. + * @param[in] anchor The anchor point that the child actor should + * rotate around when scrolling + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) as the page is scrolled. + * @param[in] positionSwing The maximum amount the child actor should + * move for each axis (X and Y) as the page is scrolled. + */ + void ApplyToActor(Actor child, + Actor parentPage, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewCubeEffect(Internal::ScrollViewCubeEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_CUBE_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.h new file mode 100644 index 0000000..9201fa7 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.h @@ -0,0 +1,433 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_CUSTOM_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_CUSTOM_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollViewCustomEffect; +} + +/** + * ScrollView Inner Cube-Effect. + * + * This effect cause each page in a scroll-view to rotate round an inner 3D cube. + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * ApplyToPage(...) method should be called on every page. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewCustomEffect : public ScrollViewEffect +{ + static const std::string ANCHOR_POINT_PROPERTY_NAME; + static const std::string SCROLL_AMOUNT_PROPERTY_NAME; ///< Property, name "scroll-amount", type VECTOR3 + +public: + enum EFlag + { + FlagTranslate = 0x0001, ///< indicates that translation is wanted + FlagTranslateIn = 0x0002, ///< translating onto the screen is a separate value + FlagTranslateOut = 0x0004, ///< translating off the screen is a separate value + FlagTranslateMask = FlagTranslate | FlagTranslateIn | FlagTranslateOut, + FlagRotate = 0x0008, ///< indicates that a positional rotation is wanted (rotate all pages around a single point like inner cube effect) + FlagRotateIn = 0x0010, ///< rotating onto the screen is a separate value + FlagRotateOut = 0x0020, ///< rotating off the screen is a separate value + FlagRotateAngleForcedOrigin = 0x0040, + FlagRotateMask = FlagRotate | FlagRotateIn | FlagRotateOut | FlagRotateAngleForcedOrigin, + FlagRotateOrigin = 0x0080, ///< indicates to use a global origin to rotate all pages around + FlagRotateOriginIn = 0x0100, ///< + FlagRotateOriginOut = 0x0200, ///< + FlagRotateOriginMask = FlagRotateOrigin | FlagRotateOriginIn | FlagRotateOriginOut, + FlagSwingAngle = 0x0400, ///< indicates that a SwingAngle is wanted (rotate all pages around a single point like inner cube effect) + FlagSwingAngleIn = 0x0800, ///< SwingAngle onto the screen is a separate value + FlagSwingAngleOut = 0x1000, ///< SwingAngle off the screen is a separate value + FlagSwingAngleMask = FlagSwingAngle | FlagSwingAngleIn | FlagSwingAngleOut, + FlagSwingAnchor = 0x2000, ///< indicates that a swing requires a specified anchor point, otherwise swings around centre of actor (rotate all pages around a single point like inner cube effect) + FlagSwingAnchorIn = 0x4000, ///< Swing anchor onto the screen is a separate value + FlagSwingAnchorOut = 0x8000, ///< Swing anchor off the screen is a separate value + FlagSwingAnchorMask = FlagSwingAnchor | FlagSwingAnchorIn | FlagSwingAnchorOut, + FlagOpacityThreshold = 0x00010000, ///< + FlagOpacityThresholdIn = 0x00020000, ///< + FlagOpacityThresholdOut = 0x00040000, ///< + FlagOpacityThresholdMask = FlagOpacityThreshold | FlagOpacityThresholdIn | FlagOpacityThresholdOut, + FlagTranslationAlphaFunctionIn = 0x00080000, + FlagTranslationAlphaFunctionOut = 0x00100000, + FlagTranslationAlphaFunctionMask = FlagTranslationAlphaFunctionIn | FlagTranslationAlphaFunctionOut, + FlagRotateAlphaFunctionIn = 0x00200000, + FlagRotateAlphaFunctionOut = 0x00400000, + FlagRotateAlphaFunctionMask = FlagRotateAlphaFunctionIn | FlagRotateAlphaFunctionOut, + FlagRotateOriginAlphaFunctionIn = 0x00800000, + FlagRotateOriginAlphaFunctionOut = 0x01000000, + FlagRotateOriginAlphaFunctionMask = FlagRotateOriginAlphaFunctionIn | FlagRotateOriginAlphaFunctionOut, + FlagSwingAngleAlphaFunctionIn = 0x02000000, + FlagSwingAngleAlphaFunctionOut = 0x04000000, + FlagSwingAngleAlphaFunctionMask = FlagSwingAngleAlphaFunctionIn | FlagSwingAngleAlphaFunctionOut, + FlagSwingAnchorAlphaFunctionIn = 0x08000000, + FlagSwingAnchorAlphaFunctionOut = 0x10000000, + FlagSwingAnchorAlphaFunctionMask = FlagSwingAnchorAlphaFunctionIn | FlagSwingAnchorAlphaFunctionOut, + FlagOpacityAlphaFunctionIn = 0x20000000, + FlagOpacityAlphaFunctionOut = 0x40000000, + FlagOpacityAlphaFunctionMask = FlagOpacityAlphaFunctionIn | FlagOpacityAlphaFunctionOut + }; + + /** + * Create an initialized ScrollViewPageCubeEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewCustomEffect New(); + + /** + * Create an uninitialized ScrollViewPageCubeEffect; this can be initialized with ScrollViewPageCubeEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewPageCubeEffect is not allowed. + */ + ScrollViewCustomEffect(); + + /** + * Downcast an Object handle to ScrollViewCustomEffect. If handle points to a ScrollViewCustomEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewCustomEffect or an uninitialized handle + */ + static ScrollViewCustomEffect DownCast( BaseHandle handle ); + + /** + * @brief SetPageSpacing + * @param spacing + */ + void SetPageSpacing(const Vector2& spacing); + + /** + * @brief SetPageTranslation sets a simple translate on/off value + * @param translation + */ + void SetPageTranslation(const Vector3& translation); + + /** + * @brief SetPageTranslation + * @param translationIn + * @param translationOut + */ + void SetPageTranslation(const Vector3& translationIn, const Vector3& translationOut); + + /** + * @brief SetPageTranslationIn + * @param translation + */ + void SetPageTranslationIn(const Vector3& translation); + + /** + * @brief SetPageTranslationOut + * @param translation + */ + void SetPageTranslationOut(const Vector3& translation); + + /** + * @brief SetPageTranslateAlphaFunction + * @param func + */ + void SetPageTranslateAlphaFunction(AlphaFunction func); + + /** + * @brief SetPageTranslateAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetPageTranslateAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetPageTranslateAlphaFunctionIn + * @param func + */ + void SetPageTranslateAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetPageTranslateAlphaFunctionOut + * @param func + */ + void SetPageTranslateAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetGlobalPageRotation + * @param angle + * @param axis + */ + void SetGlobalPageRotation(float angle, const Vector3& axis); + + /** + * @brief SetAnglePageRotation uses the angle and page size passed in on creation to create a faked origin (inner cube needs this method) + * @param angle + */ + void SetAngledOriginPageRotation(const Vector3& angle); + + /** + * @brief SetGlobalPageRotation + * @param angleIn + * @param axisIn + * @param angleOut + * @param axisOut + */ + void SetGlobalPageRotation(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut); + + /** + * @brief SetGlobalPageRotationIn + * @param angle + * @param axis + */ + void SetGlobalPageRotationIn(float angle, const Vector3& axis); + + /** + * @brief SetGlobalPageRotationOut + * @param angle + * @param axis + */ + void SetGlobalPageRotationOut(float angle, const Vector3& axis); + + /** + * @brief SetPageRotationOrigin Set the origin to rotate all the pages around + * - The default value is (0,0,0) + * @param origin + */ + void SetGlobalPageRotationOrigin(const Vector3& origin); + + /** + * @brief SetGlobalPageRotationOrigin + * @param originIn + * @param originOut + */ + void SetGlobalPageRotationOrigin(const Vector3& originIn, const Vector3& originOut); + + /** + * @brief SetGlobalPageRotationOriginIn + * @param origin + */ + void SetGlobalPageRotationOriginIn(const Vector3& origin); + + /** + * @brief SetGlobalPageRotationOriginOut + * @param origin + */ + void SetGlobalPageRotationOriginOut(const Vector3& origin); + + /** + * @brief SetSwingAngle + * @param angle + * @param axis + */ + void SetSwingAngle(float angle, const Vector3& axis); + + /** + * @brief SetSwingAngle + * @param angleIn + * @param axisIn + * @param angleOut + * @param axisOut + */ + void SetSwingAngle(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut); + + /** + * @brief SetSwingAngleIn + * @param angle + * @param axis + */ + void SetSwingAngleIn(float angle, const Vector3& axis); + + /** + * @brief SetSwingAngleOut + * @param angle + * @param axis + */ + void SetSwingAngleOut(float angle, const Vector3& axis); + + /** + * @brief SetSwingAngleAlphaFunction + * @param func + */ + void SetSwingAngleAlphaFunction(AlphaFunction func); + + /** + * @brief SetSwingAngleAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetSwingAngleAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetSwingAngleAlphaFunctionIn + * @param func + */ + void SetSwingAngleAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetSwingAngleAlphaFunctionOut + * @param func + */ + void SetSwingAngleAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetPageRotationOrigin Set the origin to rotate all the pages around + * - The default value is (0,0,0) + * @param anchor + */ + void SetSwingAnchor(const Vector3& anchor); + + /** + * @brief SetSwingAnchor + * @param anchorIn + * @param anchorOut + */ + void SetSwingAnchor(const Vector3& anchorIn, const Vector3& anchorOut); + + /** + * @brief SetSwingAnchorIn + * @param anchor + */ + void SetSwingAnchorIn(const Vector3& anchor); + + /** + * @brief SetSwingAnchorOut + * @param anchor + */ + void SetSwingAnchorOut(const Vector3& anchor); + + /** + * @brief SetSwingAnchorAlphaFunction + * @param func + */ + void SetSwingAnchorAlphaFunction(AlphaFunction func); + + /** + * @brief SetSwingAnchorAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetSwingAnchorAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetSwingAnchorAlphaFunctionIn + * @param func + */ + void SetSwingAnchorAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetSwingAnchorAlphaFunctionOut + * @param func + */ + void SetSwingAnchorAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetOpacityThreshold + * @param thresh + */ + void SetOpacityThreshold(float thresh); + + /** + * @brief SetOpacityThreshold + * @param threshIn + * @param threshOut + */ + void SetOpacityThreshold(float threshIn, float threshOut); + + /** + * @brief SetOpacityThresholdIn + * @param thresh + */ + void SetOpacityThresholdIn(float thresh); + + /** + * @brief SetOpacityThresholdOut + * @param thresh + */ + void SetOpacityThresholdOut(float thresh); + + /** + * @brief SetOpacityAlphaFunction + * @param func + */ + void SetOpacityAlphaFunction(AlphaFunction func); + + /** + * @brief SetOpacityAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetOpacityAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetOpacityAlphaFunctionIn + * @param func + */ + void SetOpacityAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetOpacityAlphaFunctionOut + * @param func + */ + void SetOpacityAlphaFunctionOut(AlphaFunction func); + + /** + * Applies the effect to a page + * @param page the page to apply this effect to + * @param pageSize not needed, page size is determined by scroll view + + */ + void ApplyToPage(Actor page, Vector3 pageSize); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewCustomEffect( Internal::ScrollViewCustomEffect *impl ); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_CUSTOM_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.h new file mode 100644 index 0000000..7c862c9 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.h @@ -0,0 +1,88 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +struct Vector2; +struct Vector3; +struct Vector4; +class PropertyInput; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollViewEffect; +class ScrollViewWobbleEffect; +} + +class ScrollView; +class ScrollViewEffect; + +typedef std::vector ScrollViewEffectContainer; +typedef ScrollViewEffectContainer::iterator ScrollViewEffectIter; +typedef ScrollViewEffectContainer::const_iterator ScrollViewEffectConstIter; + +/** + * ScrollView Effect base class, used to apply custom + * effects to a ScrollView instance. Such effects are + * purely logical (i.e. physics), and may produce + * properties that can be used with visual effects. + * Such as creating constraints that are applied to ShaderEffects + * or Actors using these properties as inputs. + */ +class ScrollViewEffect : public Dali::BaseHandle +{ + +public: + + /** + * Create an uninitialized ScrollViewEffect; this can only be initialized with derived classes + * Calling member functions with an uninitialized Toolkit::BaseObject is not allowed. + */ + ScrollViewEffect(); + +public: // Not intended for application developers + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewEffect(Internal::ScrollViewEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.h new file mode 100644 index 0000000..9de2d54 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollViewPageSpiralEffect; +} + +/** + * ScrollView Page Spiral Effect. + * + * This effect cause each page in a scroll-view to move along a spiral. + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * ApplyToPage(...) method should be called on every page. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewPageSpiralEffect : public ScrollViewEffect +{ + +public: + + /** + * Create an initialized ScrollViewPageSpiralEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewPageSpiralEffect New(); + + /** + * Create an uninitialized ScrollViewPageSpiralEffect; this can be initialized with ScrollViewPageSpiralEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewPageSpiralEffect is not allowed. + */ + ScrollViewPageSpiralEffect(); + + /** + * Downcast an Object handle to ScrollViewPageSpiralEffect. If handle points to a ScrollViewPageSpiralEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewPageSpiralEffect or an uninitialized handle + */ + static ScrollViewPageSpiralEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to a page in the scroll-view. + * @param[in] page The page to be affected by this effect. + * @param[in] spiralAngle The spirald angle (in radians). + * + * @note If the wrap mode of the scroll view is changed, then this needs to be called for every + * page again after removing the previous constraints. + */ + void ApplyToPage( Actor page, const Vector2& spiralAngle ); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewPageSpiralEffect( Internal::ScrollViewPageSpiralEffect *impl ); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.h new file mode 100644 index 0000000..08a240e --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.h @@ -0,0 +1,159 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_SLIDE_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_SLIDE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewSlideEffect; +} + +/** + * ScrollView Twist-Effect. + */ +class ScrollViewSlideEffect : public ScrollViewEffect +{ + +public: + + static const std::string EFFECT_TIME; + static const std::string EFFECT_REFERENCE; + static const std::string EFFECT_ACTIVE; + +public: + + /** + * Create an initialized ScrollViewSlideEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewSlideEffect New(); + + /** + * Create an uninitialized ScrollViewSlideEffect; this can be initialized with ScrollViewSlideEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewSlideEffect is not allowed. + */ + ScrollViewSlideEffect(); + + /** + * Downcast an Object handle to ScrollViewSlideEffect. If handle points to a ScrollViewSlideEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewSlideEffect or an uninitialized handle + */ + static ScrollViewSlideEffect DownCast( BaseHandle handle ); + + /** + * Gets the slide direction for this effect. + * @return The slide direction (true = vertical, false = horizontal) + */ + bool GetSlideDirection() const; + + /** + * Sets the slide direction for this effect. + * If the direction has been set to horizontal (false), then + * the user will see the Actors have a delay in horizontal movement + * based on the vertical distance the actor is away from the initial drag point. + * If the direction has been set to vertical (true), then the + * user will experience the opposite effect (i.e. delay in the vertical movement). + * @param[in] vertical The slide direction (true = vertical, false = horizontal) + * (default is horizontal i.e. false) + */ + void SetSlideDirection(bool vertical); + + /** + * Gets the delay reference offset for this effect. + * @return The delay reference offset (Vector3::ZERO - indicates no offset) + */ + Vector3 GetDelayReferenceOffset() const; + + /** + * Sets an offset for where the central delay point on the scroll-view should be + * when dragging. + * By default the offset is 0. Which means that the point where the user drags + * the scroll-view content should have no delay, and the further away from this + * point, the delay should increase. Adjusting this offset to for example + * 0.0f, -stageSize.height * 0.5f, will mean that dragging the center of the stage + * will result in the content at the top of the stage moving with no delay, and + * the further away from this point (top of stage), the delay should increase. + * @param[in] offset The offset in local coordinates, relative to the ScrollView. + */ + void SetDelayReferenceOffset(const Vector3& offset); + + /** + * Gets the maximum duration of the effect after scrolling completes + * @return The duration in seconds + */ + float GetMaxDelayDuration() const; + + /** + * Sets the maximum duration of the effect after scrolling completes + * @param[in] duration The duration in seconds (>= 0.0f, default is 0.25 seconds) + */ + void SetMaxDelayDuration(float duration); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] delayMin The minimum delay coefficient for Actors at the + * scroll-view touch point. Set to 0 for instantaneous, and 1 for infinite delay. + * Default is 0.5f + * @param[in] delayMax The maximum delay coefficient for Actors at the + * scroll-view approx 1 ScrollView size from the touch point. Set to 0 for + * instantaneous, and 1 for infinite delay. Default is 0.99f (a noticable delay) + */ + void ApplyToActor( Actor child, + float delayMin = 0.5f, + float delayMax = 0.95f ); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewSlideEffect(Internal::ScrollViewSlideEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_SLIDE_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.h new file mode 100644 index 0000000..0767a16 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.h @@ -0,0 +1,163 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_TWIST_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_TWIST_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewTwistEffect; +} + +/** + * ScrollView Twist-Effect. + */ +class ScrollViewTwistEffect : public ScrollViewEffect +{ + +public: + + static const std::string EFFECT_TIME; + static const std::string EFFECT_REFERENCE; + static const std::string EFFECT_DEPTH; + static const std::string EFFECT_ACTIVATE; + static const float DEFAULT_MINIMUM_DISTANCE_FOR_SHRINK; + +public: + + /** + * Create an initialized ScrollViewTwistEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewTwistEffect New(); + + /** + * Create an uninitialized ScrollViewTwistEffect; this can be initialized with ScrollViewTwistEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewTwistEffect is not allowed. + */ + ScrollViewTwistEffect(); + + /** + * Downcast an Object handle to ScrollViewTwistEffect. If handle points to a ScrollViewTwistEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewTwistEffect or an uninitialized handle + */ + static ScrollViewTwistEffect DownCast( BaseHandle handle ); + + /** + * Gets the minimum animation distance for the shrink effect to + * occur + * @return The minimum distance in seconds is returned. + */ + float GetMinimumDistanceForShrink() const; + + /** + * Sets the minimum animation distance for the shrink effect + * to occur. + * @param[in] distance The minimum distance in pixels (default = 0.0) + * i.e. any flick will result in shrinking. + */ + void SetMinimumDistanceForShrink(float distance); + + /** + * Enable or disable this effect. + * @param[in] enableFlag Set to true if the effect should be enabled. + */ + void EnableEffect(bool enableFlag); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] additionalEffects Whether just the basic effect (delay) + * should be applied. Or all effects (delay, rotation, scaling). + * For all effects set to true. Default is true i.e. apply all effects. + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) if the scrollview reaches + * overshoot. Default is PI/2 i.e. 90 degrees rotation. + * @param[in] scaleAmount The relative amount to shrink Actors as they + * are panned fast (flick animation). default is 0.125 (12.5% shrinkage) + * @param[in] delayMin The minimum delay coefficient for Actors at the + * scroll-view center. Set to 0 for instantaneous, and 1 for infinite delay. + * Default is 0.0f + * @param[in] delayMax The maximum delay coefficient for Actors at the + * scroll-view approx 1 ScrollView size from the center. Set to 0 for + * instantaneous, and 1 for infinite delay. Default is 0.9f (a slight delay) + */ + void ApplyToActor( Actor child, + bool additionalEffects = true, + const Vector2& angleSwing = Vector2( Math::PI_4, Math::PI_4 ), + float scaleAmount = 0.125f, + float delayMin = 0.0f, + float delayMax = 0.9f ); + + /** + * Set the maximum swing angle when at zero drop off + * + * @param[in] maxSwingAngle maximum swing angle for x and y axes + */ + void SetMaxSwingAngle(const Vector2& maxSwingAngle); + + /** + * Set the drop off values to affect the amount of swing angle applied to an actor the further it is from + * the scroll position. A drop off of 0.0f means no angle drop off while 1.0f will reduce the angle to zero + * over the distance supplied for that axis. + * + * Example maxSwingAngle.x is Pi, dropOff.x is 0.5f and distance.x is 100.0f: + * The angle on the x axis will reduce to (0.5f * Pi) over 100 pixels + * + * @param[in] dropOff amount to reduce swing angle by in the range [0.0f, 1.0f]. 0.0f + * @param[in] distance distance to apply dropOff in pixels + * @param[in] function Alpha Function to affect how drop off is applied over distance, NULL for linear application + */ + void SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function = NULL); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewTwistEffect(Internal::ScrollViewTwistEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_TWIST_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h new file mode 100644 index 0000000..cf614e3 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.h @@ -0,0 +1,1055 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollView; +} + +enum SnapType +{ + Snap, + Flick +}; + +/** + * DirectionBias types + */ +enum DirectionBias +{ + DirectionBiasLeft = -1, ///< Bias scroll snap to Left + DirectionBiasNone = 0, ///< Don't bias scroll snap + DirectionBiasRight = 1 ///< Bias scroll snap to Right +}; + +/** + * RulerDomain class + * + * Used for specifying minimum/maximum extents of a ruler. + */ +class RulerDomain +{ +public: + + /** + * Creates Ruler domain allowing a point to traverse between min and max extents + * @param[in] min Minimum extent (point cannot traverse less than this) + * @param[in] max Maximum extent (point cannot traverse greater than this) + * @param[in] enabled Whether domain has been enabled or not. + */ + explicit RulerDomain(float min, float max, bool enabled = true); + +public: + + float min; + float max; + bool enabled; + + /** + * Clamps value (x) from (min) to (max), an optional length parameter can be + * specifies to suggest that the subject is not a point but a line to that + * should be clamped. + * + * @param[in] x X point to be clamped between (min) and (max) extents. + * @param[in] length (optional) The Length of the line from (x) to (x + length) to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + */ + float Clamp(float x, float length = 0.0f, float scale = 1.0f) const; + + /** + * Clamps value (x) from (min) to (max), an optional length parameter can be + * specifies to suggest that the subject is not a point but a line to that + * should be clamped. + * + * @param[in] x X point to be clamped between (min) and (max) extents. + * @param[in] length (optional) The Length of the line from (x) to (x + length) to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + * @param[out] clamped Whether clamping occured and which size (None, Min or Max) + */ + float Clamp(float x, float length, float scale, ClampState &clamped) const; + + /** + * Returns (max-min) size of ruler. + * + * @return The size of the ruler from min to max. + */ + float GetSize() const; + +}; + +/** + * Ruler abstract class. + * + * Rulers are used to define axes, specifying whether they are traversable, + * where their snap points are, and their domain. + */ +class Ruler : public RefObject +{ +public: + + enum RulerType { + Fixed, + Free + }; + +public: + + /** + * Constructs ruler, defaulty enabled, with limitless domain. + */ + Ruler(); + + /** + * Destructor - A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Ruler(); + + /** + * Snaps (x) in accordance to the ruler settings. + * + * @param[in] x The input value on the ruler to be snapped. + * @param[in] bias (optional) The biasing employed for snapping + * 0 floor input (floor x) "Used for Flick Left" + * 0.5 round input (floor x + 0.5) "Used for Release" + * 1 ceil input (floor x + 1.0) "Used for Flick Right" + * @return The position of the one dimensional point passed in once snapped. + */ + virtual float Snap(float x, float bias = 0.5f) const = 0; + + /** + * Returns position from page, based on whatever the ruler + * defines as a page. + * + * If (wrap) is true, then will set volume to the number of + * times page has exceeded the domain's volume (volume being the + * number of pages within the domain), while wrapping the position + * within the domain. + * + * @param[in] page The page index + * @param[out] volume The overflow volume when the page exceeds the domain (wrap must be enabled) + * @param[in] wrap Enable wrap mode + * @return The position representing this page point. + */ + virtual float GetPositionFromPage(unsigned int page, unsigned int &volume, bool wrap) const = 0; + + /** + * Returns page from position, based on whatever the ruler + * defines as a page. + * + * If (wrap) is true, then will return a page wrapped within the domain. + * + * @param[in] position The position on the domain + * @param[in] wrap Enable wrap mode + * @return The page where this position resides. + */ + virtual unsigned int GetPageFromPosition(float position, bool wrap) const = 0; + + /** + * Returns the total number of pages within this Ruler + * + * @return The number of pages in the Ruler. + */ + virtual unsigned int GetTotalPages() const = 0; + +public: + + Ruler::RulerType GetType() const; + /** + * Returns whether this axis has been enabled or not. + * @return true if axis is enabled + */ + bool IsEnabled() const; + + /** + * Enables ruler (ruler must be enabled in order to traverse along it) + */ + void Enable(); + + /** + * Disables ruler + */ + void Disable(); + + /** + * Sets Domain + * @param[in] domain Ruler domain object. + */ + void SetDomain(RulerDomain domain); + + /** + * Gets Domain + * @return The domain + */ + const RulerDomain &GetDomain() const; + + /** + * Disables Domain (minimum/maximum extents for this axis) + */ + void DisableDomain(); + + /** + * Clamps value (x) from (min) to (max), an optional length parameter can be + * specifies to suggest that the subject is not a point but a line to that + * should be clamped. + * + * @param[in] x X point to be clamped between (min) and (max) extents. + * @param[in] length (optional) The Length of the line from (x) to (x + length) to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + */ + float Clamp(float x, float length = 0.0f, float scale = 1.0f) const; + + + /** + * Clamps value (x) from (min) to (max), an optional length parameter can be + * specifies to suggest that the subject is not a point but a line to that + * should be clamped. + * + * @param[in] x X point to be clamped between (min) and (max) extents. + * @param[in] length (optional) The Length of the line from (x) to (x + length) to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + * @param[out] clamped Whether clamping occured and which size (None, Min or Max) + */ + float Clamp(float x, float length, float scale, ClampState &clamped) const; + + /** + * Snaps and Clamps (x) in accordance to ruler settings. + * + * @param[in] x value to be snapped in accordance to ruler snap value, + * and clamped in accordance to the ruler's domain (if set). + * @param[in] bias (optional) The biasing employed for snapping + * 0 floor input (floor x) "Used for Flick Left" + * 0.5 round input (floor x + 0.5) "Used for Release" + * 1 ceil input (floor x + 1.0) "Used for Flick Right" + * @param[in] length (optional) The Length of the line from (x) to (x + length) + * to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + */ + float SnapAndClamp(float x, float bias = 0.5f, float length = 0.0f, float scale = 1.0f) const; + + /** + * Snaps and Clamps (x) in accordance to ruler settings. + * + * @param[in] x value to be snapped in accordance to ruler snap value, + * and clamped in accordance to the ruler's domain (if set). + * @param[in] bias (optional) The biasing employed for snapping + * 0 floor input (floor x) "Used for Flick Left" + * 0.5 round input (floor x + 0.5) "Used for Release" + * 1 ceil input (floor x + 1.0) "Used for Flick Right" + * @param[in] length (optional) The Length of the line from (x) to (x + length) + * to be clamped. + * @param[in] scale Scaling parameter which treats domain as scaled in calculations. + * @param[out] clamped Whether clamping occured and which size (None, Min or Max) + */ + float SnapAndClamp(float x, float bias, float length, float scale, ClampState &clamped) const; + +protected: + + RulerType mType; ///< Type of Ruler (Fixed or Free) + bool mEnabled; + RulerDomain mDomain; + +}; + +typedef IntrusivePtr RulerPtr; + +/** + * DefaultRuler has no snapping, and has one single page. + */ +class DefaultRuler : public Ruler +{ +public: + /** + * DefaultRuler constructor + */ + DefaultRuler(); + + /** + * @copydoc Toolkit::Ruler::Snap + */ + virtual float Snap(float x, float bias) const; + + /** + * @copydoc Toolkit::Ruler::GetPositionFromPage + */ + virtual float GetPositionFromPage(unsigned int page, unsigned int &volume, bool wrap) const; + + /** + * @copydoc Toolkit::Ruler::GetPageFromPosition + */ + virtual unsigned int GetPageFromPosition(float position, bool wrap) const; + + /** + * @copydoc Toolkit::Ruler::GetTotalPages + */ + virtual unsigned int GetTotalPages() const; +}; + +/** + * FixedRuler has fixed snapping, and contains + */ +class FixedRuler : public Ruler +{ +public: + /** + *@param[in] spacing The spacing between each interval on this ruler + */ + FixedRuler(float spacing = 1.0f); + + /** + * @copydoc Toolkit::Ruler::Snap + */ + virtual float Snap(float x, float bias) const; + + /** + * @copydoc Toolkit::Ruler::GetPositionFromPage + */ + virtual float GetPositionFromPage(unsigned int page, unsigned int &volume, bool wrap) const; + + /** + * @copydoc Toolkit::Ruler::GetPageFromPosition + */ + virtual unsigned int GetPageFromPosition(float position, bool wrap) const; + + /** + * @copydoc Toolkit::Ruler::GetTotalPages + */ + virtual unsigned int GetTotalPages() const; + +private: + float mSpacing; +}; + +class ScrollViewEffect; +class ScrollView; + +/** + * ScrollView contains actors that can be scrolled manually (via touch) + * or automatically. + */ +class ScrollView : public Scrollable +{ +public: + enum PageEffect + { + PageEffectNone, ///< No Effect (Standard ScrollView) + PageEffectOuterCube, ///< 3D Rotating Cube Effect + PageEffectDepth, ///< Depth Effect + PageEffectInnerCube, ///< Page Cube Effect + PageEffectCarousel, ///< Page Carousel Effect + PageEffectSpiral, ///< Page Spiral Effect + + Total + }; + + // Custom properties + + static const std::string SCROLL_PAGE_CURRENT; ///< Property, name "scroll-page-current", type INT + static const std::string SCROLL_TIME_PROPERTY_NAME; ///< Property, name "scroll-time", type FLOAT + static const std::string SCROLL_POSITION_PROPERTY_NAME; ///< Property, name "scroll-position", type VECTOR3 + static const std::string SCROLL_PRE_POSITION_PROPERTY_NAME; ///< Property, name "scroll-pre-position", type VECTOR3 + static const std::string SCROLL_OVERSHOOT_X_PROPERTY_NAME; ///< Property, name "scroll-overshoot-x", type float + static const std::string SCROLL_OVERSHOOT_Y_PROPERTY_NAME; ///< Property, name "scroll-overshoot-y", type float + static const std::string SCROLL_FINAL_PROPERTY_NAME; ///< Property, name "scroll-final", type VECTOR3 + static const std::string SCROLL_X_PROPERTY_NAME; ///< Property, name "scroll-x", type FLOAT + static const std::string SCROLL_Y_PROPERTY_NAME; ///< Property, name "scroll-y", type FLOAT + static const std::string SCROLL_SCALE_PROPERTY_NAME; ///< Property, name "scroll-scale", type VECTOR3 + static const std::string SCROLL_WRAP_PROPERTY_NAME; ///< Property, name "scroll-wrap", type BOOLEAN + static const std::string SCROLL_PANNING_PROPERTY_NAME; ///< Property, name "scroll-panning", type BOOLEAN + static const std::string SCROLL_SCROLLING_PROPERTY_NAME; ///< Property, name "scroll-scrolling", type BOOLEAN + static const std::string SCROLL_POSITION_DELTA_PROPERTY_NAME; ///< Property, name "scroll-position-delta" type VECTOR3 + static const std::string SCROLL_START_PAGE_POSITION_PROPERTY_NAME; ///< Property, name "scroll-start-page-position" type VECTOR3 + + // Default settings + + static const float DEFAULT_SLOW_SNAP_ANIMATION_DURATION; ///< Default Drag-Release animation time. + static const float DEFAULT_FAST_SNAP_ANIMATION_DURATION; ///< Default Drag-Flick animation time. + static const float DEFAULT_SNAP_OVERSHOOT_DURATION; ///< Default Overshoot snapping animation time. + static const float DEFAULT_MAX_OVERSHOOT; ///< Default maximum allowed overshoot + + static const float DEFAULT_AXIS_AUTO_LOCK_GRADIENT; ///< Default Axis-AutoLock gradient threshold. default is 0.36:1 (20 degrees) + static const float DEFAULT_FRICTION_COEFFICIENT; ///< Default Friction Co-efficient. (in stage diagonals per second) + static const float DEFAULT_FLICK_SPEED_COEFFICIENT; ///< Default Flick speed coefficient (multiples input touch velocity) + static const float DEFAULT_MAX_FLICK_SPEED; ///< Default Maximum flick speed. (in stage diagonals per second) + + //Signal Names + static const char* const SIGNAL_SNAP_STARTED; + + enum EDirectionFlag + { + DirectionFlagLeft = 0x01, + DirectionFlagRight = 0x02, + DirectionFlagUp = 0x04, + DirectionFlagDown = 0x08, + DirectionFlagTransitionOn = 0x10, ///< doesnt mean a page is moving towards centre, it affects whether the current page is using values for moving onto screen or off screen, if the user changes scroll direction we dont want things to flip over when in view + DirectionFlagTransitionOff = 0x20, + DirectionFlagMask_Direction = DirectionFlagLeft | DirectionFlagRight | DirectionFlagUp | DirectionFlagDown, + DirectionFlagMask_Transition = DirectionFlagTransitionOn | DirectionFlagTransitionOff + }; + +public: + + /** + * Snap signal event's data. + */ + struct SnapEvent + { + SnapType type; ///< Current snap commencing + Vector3 position; ///< Target snap position + Vector3 scale; ///< Target snap scale + float rotation; ///< Target snap rotation + float duration; ///< Duration of snap animation. + }; + + typedef SignalV2< void ( const SnapEvent& ) > SnapStartedSignalV2; + + /** + * Signal emitted when the ScrollView has started to snap or flick (it tells the target + * position, scale, rotation for the snap or flick) + */ + SnapStartedSignalV2& SnapStartedSignal(); + +public: + + /** + * Creates an empty ScrollView handle + */ + ScrollView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + ScrollView( const ScrollView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + ScrollView& operator=( const ScrollView& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~ScrollView(); + + /** + * Create an initialized ScrollView. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollView New(); + + /** + * Downcast an Object handle to ScrollView. If handle points to a ScrollView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollView or an uninitialized handle + */ + static ScrollView DownCast( BaseHandle handle ); + +public: + + /** + * Get snap-animation's AlphaFunction + * + * @return Current easing alpha function of the snap animation. + */ + AlphaFunction GetScrollSnapAlphaFunction() const; + + /** + * Set snap-animation's AlphaFunction + * + * @param[in] alpha Easing alpha function of the snap animation. + */ + void SetScrollSnapAlphaFunction(AlphaFunction alpha); + + /** + * Get flick-animation's AlphaFunction + * + * @return Current easing alpha function of the flick animation. + */ + AlphaFunction GetScrollFlickAlphaFunction() const; + + /** + * Set flick-animation's AlphaFunction + * + * @param[in] alpha Easing alpha function of the flick animation. + */ + void SetScrollFlickAlphaFunction(AlphaFunction alpha); + + /** + * Gets the time for the scroll snap-animation + * This animation occurs when the user drags, and releases. + * + * @return The time in seconds for the animation to take. + */ + float GetScrollSnapDuration() const; + + /** + * Sets the time for the scroll snap-animation + * This animation occurs when the user drags, and releases. + * + * @param[in] time The time in seconds for the animation to take. + */ + void SetScrollSnapDuration(float time); + + /** + * Gets the time for the scroll flick-animation + * This animation occurs when the user flicks scroll view. + * + * @return The time in seconds for the animation to take. + */ + float GetScrollFlickDuration() const; + + /** + * Sets the time for the scroll flick-animation + * This animation occurs when the user flicks scroll view. + * + * @param[in] time The time in seconds for the animation to take. + */ + void SetScrollFlickDuration(float time); + + /** + * Set X axis ruler. Defines how scrolling horizontally is snapped, and + * the boundary (domain) in which the ScrollView can pan. + * + * @param[in] ruler The ruler to be used for the X axis + */ + void SetRulerX(RulerPtr ruler); + + /** + * Set Y axis ruler. Defines how scrolling vertically is snapped, and + * the boundary (domain) in which the ScrollView can pan. + * + * @param[in] ruler The ruler to be used for the Y axis + */ + void SetRulerY(RulerPtr ruler); + + /** + * Set Scale-X axis ruler. Defines how scaling horizontally is snapped, and + * the extent (domain) to which scaling can be performed e.g. 10% to 200% + * + * @param[in] ruler The ruler to be used for the Scale-X axis + */ + void SetRulerScaleX(RulerPtr ruler); + + /** + * Set Scale-Y axis ruler. Defines how scaling vertically is snapped, and + * the extent (domain) to which scaling can be performed e.g. 10% to 200% + * + * @param[in] ruler The ruler to be used for the Scale-Y axis + */ + void SetRulerScaleY(RulerPtr ruler); + + /** + * Set Scroll's touch sensitivity. + * + * @note Unlike SetSensitive(), this determines whether this ScrollView + * should react (e.g. pan), without disrupting the sensitivity of it's children. + * + * @param[in] sensitive true to enable scroll, false to disable scrolling + */ + void SetScrollSensitive(bool sensitive); + + /** + * Set maximum overshoot amount. The final overshoot value is within 0.0f to 1.0f, + * but the maximum overshoot is in pixels (e.g. if you scroll 75 pixels beyond the edge of a scrollable + * area and the maximum overshoot is 100 then the final overshoot value will be 0.75f) + * + * @param[in] overshootX the maximum number of horizontally scrolled pixels before overshoot X reaches 1.0f + * @param[in] overshootY the maximum number of vertically scrolled pixels before overshoot Y reaches 1.0f + */ + void SetMaxOvershoot(float overshootX, float overshootY); + + /** + * Set Snap Overshoot animation's AlphaFunction + * + * @param[in] alpha Easing alpha function of the overshoot snap animation. + */ + void SetSnapOvershootAlphaFunction(AlphaFunction alpha); + + /** + * Set Snap Overshoot animation's Duration + * + * @note Set duration to 0 seconds, to disable Animation. + * + * @param[in] duration The duration of the overshoot snap animation. + */ + void SetSnapOvershootDuration(float duration); + + /** + * Sets Touches required for pan gestures. + * + * Panning requires number of touches to be within (minTouches) and + * (maxTouches). + * + * If (endOutside) is true, then outside this range of touches, + * the pan gesture will end and thus will snap. + * + * If (endOutside) is false, then outside this range of touches, + * the pan gesture will pause. but will not end until touches = 0. + * + * @param[in] minTouches Minimum touches for panning to occur. + * @param[out] maxTouches Maxiumum touches for panning to occur. + * @param[in] endOutside Whether to end the panning gesture outside of touch range + */ + void SetTouchesRequiredForPanning(unsigned int minTouches = 1, unsigned int maxTouches = 1, bool endOutside = true); + + /** + * Enables or Disables Actor Auto-Snap mode. + * + * When Actor Auto-Snap mode has been enabled, ScrollView will automatically + * snap to the closest actor (The closest actor will appear in the center of + * the ScrollView). + * + * @param[in] enable Enables (true), or disables (false) Actor AutoSnap + */ + void SetActorAutoSnap(bool enable); + + /** + * Enables or Disables Wrap mode for ScrollView contents. + * + * When enabled, the ScrollView contents are wrapped over the X/Y Domain. + * + * @note You must apply a position constraint that causes Wrapping + * to all children. + * + * @param[in] enable Enables (true), or disables (false) Wrap Mode. + */ + void SetWrapMode(bool enable); + + /** + * Gets the current refresh interval in milliseconds. + * + * @return Current refresh interval in milliseconds + */ + int GetRefreshInterval() const; + + /** + * Sets the refresh interval in milliseconds. + * + * The refresh interval is a notification signal + * (SignalScrollUpdate), that is periodically fired + * when scrolling animation is occuring. + * + * When set to 0. No update signals are sent. + * + * @param[in] milliseconds The frequency of the event in milliseconds + */ + void SetRefreshInterval(int milliseconds); + + /** + * Returns state of Axis Auto Lock mode. + * + * @return Whether Axis Auto Lock mode has been enabled or not. + */ + bool GetAxisAutoLock() const; + + /** + * Enables or Disables Axis Auto Lock mode for panning within the ScrollView + * + * When enabled, any pan gesture that appears mostly horizontal or mostly + * vertical, will be automatically restricted to horizontal only or vertical + * only panning, until the pan gesture has completed. + * + * @param[in] enable Enables (true), or disables (false) AxisAutoLock mode. + */ + void SetAxisAutoLock(bool enable); + + /** + * Gets the gradient threshold at which a panning gesture should be locked to the + * Horizontal or Vertical axis. + * @return The gradient, a value between 0.0 and 1.0f. + */ + float GetAxisAutoLockGradient() const; + + /** + * Sets the gradient threshold at which a panning gesture should be locked to the + * Horizontal or Vertical axis. by default this is 0.36 (0.36:1) which means angles + * less than 20 degrees to an axis will lock to that axis. + * + * @note: Specifying a value of 1.0 (the maximum value accepted) indicates that + * all panning gestures will auto-lock. Either to the horizontal or vertical axis. + * + * @param[in] gradient A value between 0.0 and 1.0 (auto-lock for all angles) + */ + void SetAxisAutoLockGradient(float gradient); + + /** + * Gets the friction coefficient setting for ScrollView when + * flicking in free panning mode. + * This is a value in stage-diagonals per second^2. + * stage-diagonal = Length( stage.width, stage.height ) + * @return Friction coefficient is returned. + */ + float GetFrictionCoefficient() const; + + /** + * Sets the friction coefficient for ScrollView when + * flicking in free panning mode. + * This is a value in stage-diagonals per second^2. + * stage-diagonal = Length( stage.width, stage.height ). + * example: + * A stage 480x800 in size has a diagonal length of 933. + * Friction coefficient of 1.0 means the swipe velocity will + * reduce by 1.0 * 933 pixels/sec^2. + * @param[in] friction Friction coefficient, must be greater than 0.0 (default = 1.0) + */ + void SetFrictionCoefficient(float friction); + + /** + * Gets the flick speed coefficient for ScrollView when + * flicking in free panning mode. + * This is a constant which multiplies the input touch + * flick velocity to determine the actual velocity at + * which to move the scrolling area. + * @return The flick speed coefficient is returned. + */ + float GetFlickSpeedCoefficient() const; + + /** + * Sets the flick speed coefficient for ScrollView when + * flicking in free panning mode. + * This is a constant which multiplies the input touch + * flick velocity to determine the actual velocity at + * which to move the scrolling area. + * @param[in] speed The flick speed coefficient (default = 1.0). + */ + void SetFlickSpeedCoefficient(float speed); + + /** + * Gets the maximum flick speed setting for ScrollView when + * flicking in free panning mode. + * This is a value in stage-diagonals per second. + * stage-diagonal = Length( stage.width, stage.height ) + * @return Maximum flick speed is returned + */ + float GetMaxFlickSpeed() const; + + /** + * Sets the maximum flick speed for the ScrollView when + * flicking in free panning mode. + * This is a value in stage-diagonals per second. + * stage-diagonal = Length( stage.width, stage.height ) + * example: + * A stage 480x800 in size has a diagonal length of 933. + * Max Flick speed of 1.0 means the maximum velocity of + * a swipe can be 1.0 * 933 pixels/sec. + * @param[in] speed Maximum flick speed (default = 3.0) + */ + void SetMaxFlickSpeed(float speed); + + /** + * Gets the step of scroll distance in actor coordinates for + * each mouse wheel event received in free panning mode. + * @return The step of scroll distance(pixel) in X and Y axes. + */ + Vector2 GetMouseWheelScrollDistanceStep() const; + + /** + * Sets the step of scroll distance in actor coordinates for + * each mouse wheel event received in free panning mode. + * @param[in] step The step of scroll distance(pixel) in X and Y axes. + * + * @note: If snap points are defined in the rulers, it will always + * scroll to the next snap point towards the scroll direction while + * receiving the mouse wheel events. + * + */ + void SetMouseWheelScrollDistanceStep(Vector2 step); + + /** + * Retrieves current scroll position. + * + * @returns The current scroll position. + */ + Vector3 GetCurrentScrollPosition() const; + + /** + * Retrieves current scroll scale. + * + * @returns The current scroll scale. + */ + Vector3 GetCurrentScrollScale() const; + + /** + * Retrieves current scroll page based on ScrollView dimensions being + * the size of one page, and all pages laid out in a grid fashion, + * increasing from left to right until the end of the X-domain. + * + * @note: Pages start from 0 as the first page, not 1. + * + * @returns The Current page. + */ + unsigned int GetCurrentPage() const; + + /** + * Transforms View to position, scale and rotation specified + * + * @param[in] position The position to transform to. + * @param[in] scale The scale to transform to. + * @param[in] rotation The rotation to transform to. + */ + void TransformTo(const Vector3& position, const Vector3& scale, float rotation); + + /** + * Transforms View to position, scale and rotation specified + * + * @param[in] position The position to transform to. + * @param[in] scale The scale to transform to. + * @param[in] rotation The rotation to transform to. + * @param[in] duration The duration for this animation in seconds. + */ + void TransformTo(const Vector3& position, const Vector3& scale, float rotation, float duration); + + /** + * Scrolls View to position specified (contents will scroll to this position) + * Position 0,0 is the origin. Increasing X scrolls contents left, while + * increasing Y scrolls contents up. + * - If Rulers have been applied to the axes, then the contents will scroll until + * reaching the domain boundary. + * @note Contents will not snap to ruler snap points. + * + * @param[in] position The position to scroll to. + */ + void ScrollTo(const Vector3 &position); + + /** + * Scrolls View to position specified (contents will scroll to this position) + * Position 0,0 is the origin. Increasing X scrolls contents left, while + * increasing Y scrolls contents up. + * - If Rulers have been applied to the axes, then the contents will scroll until + * reaching the domain boundary. + * @note Contents will not snap to ruler snap points. + * + * @param[in] position The position to scroll to. + * @param[in] duration The duration of the animation in seconds + */ + void ScrollTo(const Vector3 &position, float duration); + + /** + * Scrolls View to position specified (contents will scroll to this position) + * Position 0,0 is the origin. Increasing X scrolls contents left, while + * increasing Y scrolls contents up. + * - If Rulers have been applied to the axes, then the contents will scroll until + * reaching the domain boundary. + * @note Contents will not snap to ruler snap points. + * Biasing parameters are provided such that in scenarios with 2 or 2x2 pages in + * wrap mode, the application developer can decide whether to scroll left or right + * to get to the target page + * + * @param[in] position The position to scroll to. + * @param[in] duration The duration of the animation in seconds + * @param[in] horizontalBias Whether to bias scrolling to left or right. + * @param[in] verticalBias Whether to bias scrolling to top or bottom. + */ + void ScrollTo(const Vector3 &position, float duration, + DirectionBias horizontalBias, DirectionBias verticalBias); + + /** + * Scrolls View to page currently based on assumption that each page is + * "(page) * ScrollViewSize.width, 0". + * @note Should probably be upgraded so that page is an abstract class, that can be + * a function of ScrollViewSize, ruler domain, ruler snap points etc. as pages may be + * orchestrated in a 2D grid fashion, or variable width. + * + * @param[in] page to scroll to + */ + void ScrollTo(unsigned int page); + + /** + * Scrolls View to page currently based on assumption that each page is + * "(page) * ScrollViewSize.width, 0". + * @note Should probably be upgraded so that page is an abstract class, that can be + * a function of ScrollViewSize, ruler domain, ruler snap points etc. as pages may be + * orchestrated in a 2D grid fashion, or variable width. + * + * @param[in] page to scroll to + * @param[in] duration The duration of the animation in seconds + */ + void ScrollTo(unsigned int page, float duration); + + /** + * Scrolls View to page currently based on assumption that each page is + * "(page) * ScrollViewSize.width, 0". + * @note Should probably be upgraded so that page is an abstract class, that can be + * a function of ScrollViewSize, ruler domain, ruler snap points etc. as pages may be + * orchestrated in a 2D grid fashion, or variable width. + * A biasing parameter is provided such that in scenarios with 2 pages in wrap mode, + * the application developer can decide whether to scroll left or right to get to + * the target page. + * + * @param[in] page to scroll to + * @param[in] duration The duration of the animation in seconds + * @param[in] bias Whether to bias scrolling to left or right. + */ + void ScrollTo(unsigned int page, float duration, DirectionBias bias); + + /** + * Scrolls View such that actor appears in the center of the ScrollView. + * + * @note Actor must be a direct child of ScrollView, otherwise will + * cause an assertion failure. + * @param[in] actor The actor to center in on (via Scrolling). + */ + void ScrollTo(Actor& actor); + + /** + * Scrolls View such that actor appears in the center of the ScrollView. + * + * @note Actor must be a direct child of ScrollView, otherwise will + * cause an assertion failure. + * @param[in] actor The actor to center in on (via Scrolling). + * @param[in] duration The duration of the animation in seconds + */ + void ScrollTo(Actor& actor, float duration); + + /** + * Scrolls View to the nearest snap points as specified by the Rulers. + * + * If already at snap points, then will return false, and not scroll. + * + * @return True if Snapping necessary. + */ + bool ScrollToSnapPoint(); + + /** + * Scales View to (scale) + * + * @param[in] scale The scale factor the animate to. + */ + void ScaleTo(const Vector3& scale); + + /** + * Scales View to (scale) + * + * @param[in] scale The scale factor the animate to. + * @param[in] duration The duration of the animation in seconds. + */ + void ScaleTo(const Vector3& scale, float duration); + + /** + * Applies a constraint that will affect the children of ScrollView + * + * @note this affects all existing, and future Actors that are added to + * scrollview. + */ + void ApplyConstraintToChildren(Constraint constraint); + + /** + * Removes all constraints that will affect the children of ScrollView + * + * @note this removes all constraints from actors that have been added + * to scrollview. + */ + void RemoveConstraintsFromChildren(); + + /** + * Apply Effect to ScrollView + * @param[in] effect The effect to apply to scroll view + */ + void ApplyEffect(ScrollViewEffect effect); + + /** + * ApplyEffect Applies a predefined effect + * @param[in] effect enum for the predefined effect + */ + ScrollViewEffect ApplyEffect(ScrollView::PageEffect effect); + + /** + * Remove Effect from ScrollView + * @param[in] effect The effect to remove. + */ + void RemoveEffect(ScrollViewEffect effect); + + /** + * Remove All Effects from ScrollView + */ + void RemoveAllEffects(); + + /** + * Binds actor to this ScrollView, once an actor is bound to a ScrollView, + * it'll be subject to that ScrollView's properties. + * + * @param[in] child The actor to add to this ScrollView. + */ + void BindActor(Actor child); + + /** + * Unbind Actor from this ScrollView + * Once Unbound, this ScrollView will not affect the actor. + * + * @note this does not remove the child from the ScrollView container + * + * @param[in] child The actor to be unbound. + */ + void UnbindActor(Actor child); + + /** + * Allows the user to constrain the scroll view in a particular direction. + * @param[in] direction The axis to constrain the scroll-view to. + * Usually set to PanGestureDetector::DIRECTION_VERTICAL or PanGestureDetector::DIRECTION_HORIZONTAL (but can be any other angle if desired). + * @param[in] threshold The threshold to apply around the axis. + * @note If no threshold is specified, then the default threshold of PI * 0.25 radians (or 45 degrees) is used. + */ + void SetScrollingDirection( Radian direction, Radian threshold = PanGestureDetector::DEFAULT_THRESHOLD ); + + /** + * Remove a direction constraint from the scroll view. + * @param[in] direction The axis to stop constraining to. + * Usually will be PanGestureDetector::DIRECTION_VERTICAL or PanGestureDetector::DIRECTION_HORIZONTAL (but can be any other angle if desired). + */ + void RemoveScrollingDirection( Radian direction ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ScrollView(Internal::ScrollView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ScrollView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLL_VIEW_H__ diff --git a/capi/dali-toolkit/public-api/controls/scrollable/scrollable.h b/capi/dali-toolkit/public-api/controls/scrollable/scrollable.h new file mode 100644 index 0000000..bdc9e29 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/scrollable/scrollable.h @@ -0,0 +1,205 @@ +#ifndef __DALI_TOOLKIT_SCROLLABLE_H__ +#define __DALI_TOOLKIT_SCROLLABLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Scrollable; +} + +enum ClampState +{ + NotClamped, + ClampedToMin, + ClampedToMax +}; + +struct ClampState2 +{ + ClampState x; + ClampState y; +}; + +struct ClampState3 +{ + ClampState x; + ClampState y; + ClampState z; +}; + +/** + * Base class for derived Scrollables that contains actors that can be scrolled manually + * (via touch) or automatically. Scrollables such as ScrollView and ItemView can be derived + * from this class. + */ +class Scrollable : public Control +{ +public: + + /** + * Clamp signal event's data + */ + struct ClampEvent + { + ClampState3 scale; ///< Clamp information for scale axes + ClampState3 position; ///< Clamp information for position axes + ClampState rotation; ///< Clamp information for rotation + }; + + /** + * Scroll component types + */ + enum ScrollComponentType + { + HorizontalScrollBar, + VerticalScrollBar, + OvershootIndicator, + }; + + // Custom properties + + static const std::string SCROLL_RELATIVE_POSITION_PROPERTY_NAME; ///< Property, name "scroll-relative-position", type VECTOR3 + static const std::string SCROLL_POSITION_MIN_PROPERTY_NAME; ///< Property, name "scroll-position-min", type VECTOR3 + static const std::string SCROLL_POSITION_MAX_PROPERTY_NAME; ///< Property, name "scroll-position-max", type VECTOR3 + static const std::string SCROLL_DIRECTION_PROPERTY_NAME; ///< Property, name "scroll-direction", type VECTOR2 + + //Signal Names + static const char* const SIGNAL_SCROLL_STARTED; + static const char* const SIGNAL_SCROLL_COMPLETED; + static const char* const SIGNAL_SCROLL_UPDATED; + static const char* const SIGNAL_SCROLL_CLAMPED; + +public: + + typedef SignalV2< void ( const Vector3& ) > ScrollStartedSignalV2; + + typedef SignalV2< void ( const Vector3& ) > ScrollUpdatedSignalV2; + + typedef SignalV2< void ( const Vector3& ) > ScrollCompletedSignalV2; + + typedef SignalV2< void ( const ClampEvent& ) > ScrollClampedSignalV2; + + /** + * Signal emitted when the Scrollable has moved (whether by touch or animation) + */ + ScrollStartedSignalV2& ScrollStartedSignal(); + + /** + * Signal emitted when the Scrollable has moved (whether by touch or animation) + */ + ScrollUpdatedSignalV2& ScrollUpdatedSignal(); + + /** + * Signal emitted when the Scrollable has completed movement (whether by touch or animation) + */ + ScrollCompletedSignalV2& ScrollCompletedSignal(); + + /** + * Signal emitted when the Scrollable is pushing against a domain boundary + * (in either position, scale, or rotation) + */ + ScrollClampedSignalV2& ScrollClampedSignal(); + +public: + + /** + * Creates an uninitialized Scrollable handle + */ + Scrollable(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + Scrollable( const Scrollable& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + Scrollable& operator=( const Scrollable& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Scrollable(); + + /** + * Downcast an Object handle to Scrollable. If handle points to a Scrollable the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Scrollable or an uninitialized handle + */ + static Scrollable DownCast( BaseHandle handle ); + + /** + * Checks if a ScrollComponent has been enabled or not. + * @param[in] type The Scroll Component Type to check + * @return True (if Enabled) + */ + bool IsScrollComponentEnabled(Scrollable::ScrollComponentType type) const; + + /** + * Enables a ScrollComponent + * @param[in] type The Scroll Component Type to enable + */ + void EnableScrollComponent(Scrollable::ScrollComponentType type); + + /** + * Disables a ScrollComponent + * @param[in] type The Scroll Component Type to disable + */ + void DisableScrollComponent(Scrollable::ScrollComponentType type); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Scrollable(Internal::Scrollable& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Scrollable( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SCROLLABLE_H__ diff --git a/capi/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.h b/capi/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.h new file mode 100644 index 0000000..c9be8f6 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.h @@ -0,0 +1,165 @@ +#ifndef __DALI_TOOLKIT_SUPER_BLUR_VIEW_H__ +#define __DALI_TOOLKIT_SUPER_BLUR_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class SuperBlurView; +} + +/** + * SuperBlurView accepts an image as input, and displays/animates it with various blur strength. + * Usage example:- + * + * // initialise\n + * SuperBlurView blurView = SuperBlurView::New( blurLevels );\n + * blurView.SetSize(); // it is important to set the display size before set the input image!! + * Stage::GetCurrent().Add(blurView);\n + * + * // Set the input image + * Image image = Image::New(...);\n + * blurView.SetImage(image);\n + * + * // animate the strength of the blur - this can fade between no blur and full blur. .\n + * Animation blurAnimation = Animation::New( ... );\n + * blurAnimation.AnimateTo( Property( blurView, blurView.GetBlurStrengthPropertyIndex() ), ... );\n + * blurAnimation.Play();\n + */ +class SuperBlurView : public Control +{ +public: + /** + * Signal type for notifications + */ + typedef SignalV2< void (SuperBlurView source) > SuperBlurViewSignal; + + /** + * Creates an empty SuperBlurView handle + */ + SuperBlurView(); + + /** + * Create an initialized SuperBlurView + * @param[in] blurLevels The final blur strength level. It decides how many filtering passes are used to create the group of blurred images. + * @return A handle to a newly allocated Dali resource + */ + static SuperBlurView New( unsigned int blurLevels ); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + SuperBlurView( const SuperBlurView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + SuperBlurView& operator=( const SuperBlurView& rhs ); + + /** + * Virtual destructor. + */ + virtual ~SuperBlurView(); + + /** + * Downcast an Object handle to SuperBlurView. + * If handle points to a SuperBlurView, the downcast produces valid handle. + * If not, the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a SuperBlurView or an uninitialized handle + */ + static SuperBlurView DownCast( BaseHandle handle ); + + /** + * Sets a custom image to be blurred + * @param[in] inputImage The image that the user wishes to blur + */ + void SetImage(Image inputImage); + + /** + * Get the index of the property that can be used to fade the blur in / out. This is the overall strength of the blur. + * User can use this to animate the blur. A value of 0.0 is zero blur and 1.0 is full blur. Default is 0.0. + * @return Index of the property that can be used to fade the blur in / out + */ + Property::Index GetBlurStrengthPropertyIndex() const; + + /** + * Set the blur strength to display the image + * @param[in] blurStrength The blur strength used to display the image. + */ + void SetBlurStrength( float blurStrength ); + + /** + * Get the current blur strength + * @return The current blur strength + */ + float GetCurrentBlurStrength() const; + + /** + * Connect to this signal to be notified when the all the blurs have completed. + * @return The BlurFinished signal + */ + SuperBlurViewSignal& BlurFinishedSignal(); + + /** + * Get the blurred image. Should wait for the BlurFinishedSignal before calling this method + * @param[in] level Indicate which blurred image to get, must be a value between 1 and blurLevels + * @return The level-th blurred image + */ + Image GetBlurredImage( unsigned int level ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + DALI_INTERNAL SuperBlurView(Internal::SuperBlurView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + DALI_INTERNAL SuperBlurView(Dali::Internal::CustomActor* internal); + +}; + +} // namespace Toolkit + +} // namespace Dali + + +/** + * @} + */ +#endif /* __DALI_TOOLKIT_SUPER_BLUR_VIEW_H__ */ diff --git a/capi/dali-toolkit/public-api/controls/text-input/text-input.h b/capi/dali-toolkit/public-api/controls/text-input/text-input.h new file mode 100644 index 0000000..da7c8cc --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/text-input/text-input.h @@ -0,0 +1,584 @@ +#ifndef __DALI_TOOLKIT_TEXT_INPUT_H__ +#define __DALI_TOOLKIT_TEXT_INPUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class TextInput; +} + +/** + * TextInput Actor takes input one character at a time and displays it as a string within an input box. + * Characters can be removed from the end of the string until it is empty. A maximum length of displayed string + * can be set. + */ +class TextInput : public Control +{ + +public: + + //Signal Names + static const char* const SIGNAL_START_INPUT; + static const char* const SIGNAL_END_INPUT; + static const char* const SIGNAL_STYLE_CHANGED; + static const char* const SIGNAL_MAX_INPUT_CHARACTERS_REACHED; + static const char* const SIGNAL_TOOLBAR_DISPLAYED; + static const char* const SIGNAL_TEXT_EXCEED_BOUNDARIES; + +public: + + /** + * Create an uninitialized TextInput; this can be initialized with TextView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + TextInput(); + + /** + * Copy constructor. + * @param handle to be copied + */ + TextInput( const TextInput& handle ); + + /** + * Assignment operator. + * @param handle to object we want to point to + * @return handle to the TextInput + */ + TextInput& operator=( const TextInput& handle ); + + /** + * Create an initialised TextInput. + * @return A handle to a newly allocated Dali resource. + */ + static TextInput New(); + + /** + * Downcast an Object handle to TextInput. If handle points to a TextInput the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a TextInput or an uninitialized handle + */ + static TextInput DownCast( BaseHandle handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~TextInput(); + + /** + * Get the inputed text currently being displayed. + * @return string, the currently displayed string. + */ + std::string GetText() const; + + /** + * Get the inputed text currently being displayed together with mark-up tags. + * @return string, the currently displayed string with mark-up. + */ + std::string GetMarkupText() const; + + /** + * Set the maximum number of characters for the Text Input + * @param [in] maxChars the max number of characters + */ + void SetMaxCharacterLength(std::size_t maxChars); + + /** + * Limits the number of lines of text Text Input will display + * @param [in] maxLines the max number of lines to display, must be greater than 0. + * Currently the only valid limit is 1. Which turns TextInput into Single line mode. Any number higher than 1 results in no limit. + */ + void SetNumberOfLinesLimit(std::size_t maxLines); + + /** + * Returns the limit of lines Text Input is allowed to display. + * @return max line number limit + */ + std::size_t GetNumberOfLinesLimit() const; + + /** + * Returns the number of characters TextInput is displaying. + * @return number of characters + */ + std::size_t GetNumberOfCharacters() const; + + /** + * Sets a place holder text to be displayed when the text-input is empty. + * If not set or set to an empty string then no place holder will be shown. + * @param [in] placeHolderText text to be used as place holder. + */ + void SetPlaceholderText( const std::string& placeHolderText ); + + /** + * @return the current set place holder text, empty string returned if not set. + */ + std::string GetPlaceholderText(); + + /** + * set initial text to be displayed in text-input + * can be used to edit a pre-existing string + * @param [in] initialText text to be initially displayed + */ + void SetInitialText(const std::string& initialText); + + /** + * Manual method to set the focus on the TextInput so it starts or stops edit state + * @pre The text input actor has been initialised. + * @param[in] editMode true or false to indicate editMode on or off + */ + void SetEditable(bool editMode); + + /** + * @see SetEditable(bool editMode). + * + * It sets the cursor in the closest character to the given touch point. + * + * @param[in] editMode true or false to indicate editMode on or off + * @param[in] touchPoint A position in actor coordinates within the text-input. + */ + void SetEditable(bool editMode, const Vector2& touchPoint); + + /** + * Check if TextInput is in edit state + * @pre The text input actor has been initialised. + * @return True or False to indicate editMode on or off + */ + bool IsEditable() const; + + /** + * Method to enable or disable edit on touch/tap. + * If not enabled (set to false) then SetEditable(true) will be used to start edit mode. + * @pre The text input actor has been initialised. + * @param[in] editOnTouch true or false to indicate if editing should start on touch + * default is for editing to start on touching textinput + */ + void SetEditOnTouch(bool editOnTouch = true); + + /** + * Check if TextInput starts edit mode on touch + * @pre The text input actor has been initialised. + * @return True or False to indicate editOnTouch on or off + */ + bool IsEditOnTouch() const; + + /** + * Check if Text Selection is enabled so required text can be highlighted + * @pre The text input actor has been initialised. + * @param[in] textSelectable true or false to indicate if text can be selected or not + * default is for text to be select-able when in edit mode + */ + void SetTextSelectable(bool textSelectable = true); + + /** + * Check if Text can be selected + * @pre The text input actor has been initialised. + * @return True or False to indicate if text can be selected or not + */ + bool IsTextSelectable() const; + + /** + * Check if any text is currently selected, can be used to determine if ApplyStyle or SetActiveStyle should be used. + * @pre The text input actor has been initialised. + * @return True if text selected else False + */ + bool IsTextSelected() const; + + /** + * Selects text between the given positions + * @pre TextInput should be in edit mode. + * @param start position to start selection + * @param end position to end selection, inclusive of this character. + * Providing 0 and result from GetNumberOfCharacters() will select all text. + */ + void SelectText(std::size_t start, std::size_t end); + + /** + * If any text is selected then de-select it and hide highlight. + * @pre The text input actor has been initialised. + */ + void DeSelectText(); + + /** + * Set the image to be used as the cursor grab hander + * @pre The text input actor has been initialised. + * @param[in] image The image to be used. + */ + void SetGrabHandleImage( Image image ); + + /** + * Set the image to be used for the regular left to right cursor + * @pre The text input actor has been initialised. + * @param[in] image The image to be used. + * @param[in] border The nine patch border for the image. + */ + void SetCursorImage(Dali::Image image, const Vector4& border ); + + /** + * Retrieve the selection handle size. Both handles have same size. + * @return Vector3 the selection handle size. + */ + Vector3 GetSelectionHandleSize(); + + /** + * Set the image to be used for the Right to Left cursor + * @pre The text input actor has been initialised. + * @param[in] image The image to be used. + * @param[in] border The nine patch border for the image. + */ + void SetRTLCursorImage(Dali::Image image, const Vector4& border ); + + /** + * Toggle to enable the grab handle, used to position cursor when magnifier not being used. + * Default behaviour is to use the magnifier to position the cursor, enabling this prevents the magnifier from being shown. + * @param[in] toggle true to enable, false to disable grab handle + */ + void EnableGrabHandle(bool toggle); + + /** + * Method to check if grab handle is enabled, if false then the magnifier will be used to position cursor. + * @return bool returns true is grab handle enabled. + */ + bool IsGrabHandleEnabled(); + + /** + * Toggle to enable flipping the selection handle, when it is reached the selection handle flip border. + * @param[in] toggle true to enable, false to disable flipping the selection handle. DEFAULT = true. + */ + /* @deprecated, handles always flip. Use SetBoundingRectangle */ + void EnableSelectionHandleFlip( bool toggle ); + + /** + * Method to check if the selection handle flip is enabled, if true then the selection cursor will be fliped when this exceed border. + * @return bool return true is that the selection handle flip enable. + */ + /* @deprecated, handles always flip. Use SetBoundingRectangle */ + bool IsSelectionHandleFlipEnabled(); + + /** + * Set the selection handle filp margin. + * The default value is Vector4(0, 0, 0, 0) + * ------------------------------------------ + * | y | + * | ---------------------------------- | + * | | | | + * | x | Text Input | z | + * | | | | + * | ---------------------------------- | + * | w | + * ------------------------------------------ + */ + /* @deprecated, use SetBoundingRectangle instead. */ + void SetSelectionHandleFlipMargin( const Vector4& margin ); + + /** + * Set the bounding rectangle which handles, popup and similar decorations will not exceed + * The default value is the width and height of the stage from the top left origin. + * If a title bar for example is on the top of the screen then the y should be the title's height and + * the boundary height the stage height minus the title's height. + * Restrictions - The boundary box should be set up with a fixed z position for the text-input and the default camera. + * @param[in] boundingOriginAndSize Rect( x coordinate, y coordinate, width, height ) + * ------------------------------------------ + * |(x,y) | + * |o---------------------------------------| + * || || + * || Bounding Box || boundary height + * || || + * |----------------------------------------| + * ------------------------------------------ + * boundary width + */ + void SetBoundingRectangle( const Rect& boundingOriginAndSize ); + + /** + * Retrieve the bounding box origin and dimensions + * default is set once control is added to stage, before this the return vector will be Vector4:ZERO + * @return Rect the bounding rectangle + */ + const Rect GetBoundingRectangle() const; + + /** + * Retrieve the selection handle filp margin. + * @return Vector4 the selection handle flip margin. + */ + /* @deprecated, use GetBoundingRectangle instead. */ + const Vector4& GetSelectionHandleFlipMargin(); + + /** + * Sets the style for new text being typed. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * @pre The text input actor has been initialised. + * @param[in] style The style for the new text. + * @param[in] mask The bit mask. + */ + void SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask = TextStyle::ALL ); + + /** + * Applies the given style to the selected text. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * Introduced text after this call uses the new style. + * @param[in] style The given style. + * @param[in] mask The bit mask. + */ + void ApplyStyle( const TextStyle& style, const TextStyle::Mask mask = TextStyle::ALL ); + + /** + * Applies the given style to all text, selected or not selected. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * @param[in] style The given style. + * @param[in] mask The bit mask. + */ + void ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask = TextStyle::ALL ); + + /** + * Get the style of the Text character before the cursor + * If no character before then return the InputStyle. + * @return TextStyle, the style of the character before the cursor + */ + TextStyle GetStyleAtCursor() const; + + /** + * Set the current text alignment (overrides default setting) + * + * The default alignment is dependent on the current text in the text field. + * If the text begins using LTR characters (e.g. European text) then the + * alignment is HorizontalLeft. If the text begins using RTL characters + * (e.g. Hebrew/Arabic text) then the alignment is HorizontalRight. + * If there is no text, then the alignment defaults to: + * (HorizontalLeft | VerticalCenter) + * @param[in] align The new alignment option. + */ + void SetTextAlignment( Toolkit::Alignment::Type align ); + + /** + * Set the current line justification. (overrides default setting) + * The default justification is dependent on the current text in the text field. + * If the text begins using LTR characters (e.g. European text) then the + * justification is HorizontalLeft. If the text begins using RTL characters + * (e.g. Hebrew/Arabic text) then the justification is HorizontalRight. + * If there is no text, then the justification defaults to: + * (HorizontalLeft | VerticalCenter) + * @param[in] justification The new line justification. + */ + void SetTextLineJustification( Toolkit::TextView::LineJustification justification ); + + /** + * Sets a fade boundary. + * + * @see TextView::FadeBoundary. + * + * @param[in] fadeBoundary The given fade boundary. + */ + void SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ); + + /** + * Retrieves the fade boundary. + * + * @see TextView::FadeBoundary. + * + * @return The fade boundary. + */ + const Toolkit::TextView::FadeBoundary& GetFadeBoundary() const; + + /** + * Get the current text alignment combined into a single value. + * The values can be tested by using the & operator + * and the desired flag. e.g. if (GetTextAlignment() & HorizontalCentre) ... + */ + Toolkit::Alignment::Type GetTextAlignment() const; + + /** + * Sets how to split the text in lines policy. + * @param policy The multi-line policy. + */ + void SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy ); + + /** + * Gets the split in lines policy. + * @return The multi-line policy. + */ + Toolkit::TextView::MultilinePolicy GetMultilinePolicy() const; + + /** + * Sets how to display the text inside the TextView when it exceeds the text-view's width. + * @param policy The exceed policy. + */ + void SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * Gets the width exceed policy. + * @return The exceed policy. + */ + TextView::ExceedPolicy GetWidthExceedPolicy() const; + + /** + * Sets how to display the text inside the TextView when it exceeds the text-view's height. + * @param policy The exceed policy. + */ + void SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * Gets the height exceed policy. + * @return The exceed policy. + */ + TextView::ExceedPolicy GetHeightExceedPolicy() const; + + /** + * Sets if the inputed text can exceed the text-input boundary. + * + * By default is enabled. + * + * @param[in] enable Whether the inputed text can exceed its boundary. + */ + void SetExceedEnabled( bool enable ); + + /** + * Retrieves whether inputed text can exceed the text-input boundary. + * + * @return \e true if text inputed can exceed the boundary, otherwise \e false. + */ + bool GetExceedEnabled() const; + + /** + * Allows modification of text-actor's position in the depth sort algorithm. + * + * @see Dali::RenderableActor::SetSortModifier() + * @param [in] depthOffset the offset to be given to the internal text-actors. Positive values pushing it further back. + */ + void SetSortModifier( float depthOffset ); + + /** + * Sets whether text-view renders text using a previously generated snapshot. + * + * @see TextView::SetSnapshotModeEnabled() + * + * @param[in] enable Whether text-view is using or not a snapshot to render text. + */ + void SetSnapshotModeEnabled( bool enable ); + + /** + * Retrieves whether text-view is using a snapshot to render text + * + * @see TextView::IsSnapshotModeEnabled() + * + * @return \e true if text-view is using a snapshot to render text, otherwhise it returns \e false. + */ + bool IsSnapshotModeEnabled() const; + + /** + * @copydoc TextView::SetScrollEnabled() + */ + void SetScrollEnabled( bool enable ); + + /** + * @copydoc TextView::IsScrollEnabled() + */ + bool IsScrollEnabled() const; + + /** + * @copydoc TextView::SetScrollPosition() + */ + void SetScrollPosition( const Vector2& position ); + + /** + * @copydoc TextView::GetScrollPosition() + */ + Vector2 GetScrollPosition() const; + +public: /* Signals */ + + // Input Signal + typedef SignalV2< void ( TextInput textInput ) > InputSignalV2; + + // Input style changed signal + typedef SignalV2< void ( TextInput textInput, const TextStyle& style ) > StyleChangedSignalV2; + + // Max input characters reached signal + typedef SignalV2< void ( TextInput textInput ) > MaxInputCharactersReachedSignalV2; + + // Input text exceeds boundaries signal + typedef SignalV2< void ( TextInput textInput ) > InputTextExceedBoundariesSignalV2; + + /** + * Signal emitted when the Text-Input starts receiving input. + */ + InputSignalV2& InputStartedSignal(); + + /** + * Signal emitted when the Text-Input is finished receiving input. + * TextInput::GetText() can be called to get current text string. + */ + InputSignalV2& InputFinishedSignal(); + + /** + * Signal emitted when the cut and paste toolbar is displayed. + */ + InputSignalV2& CutAndPasteToolBarDisplayedSignal(); + + /** + * Signal emitted when style changes. + */ + StyleChangedSignalV2& StyleChangedSignal(); + + /** + * Signal emitted when max input characters are reached during text input. + */ + MaxInputCharactersReachedSignalV2& MaxInputCharactersReachedSignal(); + + /** + * Signal emitted when input text exceeds the boundaries of the text-input. + */ + InputTextExceedBoundariesSignalV2& InputTextExceedBoundariesSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + TextInput(Internal::TextInput& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + TextInput(Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_TEXT_INPUT_H__ diff --git a/capi/dali-toolkit/public-api/controls/text-view/text-view.h b/capi/dali-toolkit/public-api/controls/text-view/text-view.h new file mode 100644 index 0000000..8d9a156 --- /dev/null +++ b/capi/dali-toolkit/public-api/controls/text-view/text-view.h @@ -0,0 +1,641 @@ +#ifndef __DALI_TOOLKIT_TEXT_VIEW_H__ +#define __DALI_TOOLKIT_TEXT_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class TextView; +} + +/** + * TextView is a layout container with alignment, multi-line wrapping and formatting support. + * + * Different multi-line and exceed policies could be chosen to represent the given text. + * \see Toolkit::TextView::SetMultilinePolicy \see Toolkit::TextView::SetExceedPolicy + * + * Multi-line policies: + *
    + *
  • \e Split by new line character. + * Text will split when a '\\n' character is found. + * + *
  • \e Split by word. + * Text will split when a '\\n' character is found or if the text doesn't fit in the text view width. + * In that case, some words will be moved to a new line. + * + *
  • \e Split by character. + * Text will split when a '\\n' character is found or if the text doesn't fit in the text view width. + * In that case, words which doesn't fit will be split in two and the remaining text moved to a new line. + *
+ * + * Exceed policies work in combination with multi-line policies: + *
    + *
  • \e Original size. + * Text will be displayed with its original size. + * + *
  • \e Truncate. + * Text will be truncated. + * + *
  • \e Fade. + * Text will be faded out. + * + *
  • \e Split. + * Text will be split and moved to a new line. + * + *
  • \e Shrink to fit. + * Text will be shrink to fit in the text view's boundary. + * + *
  • \e EllipsizeEnd. + * Text will be ellipsized at the end. + * + *
+ * + * Text alignment could be set to align the whole text block inside the text view's boundary. \see Toolkit::TextView::SetTextAlignment. + * + * Line justification could be set to align lines inside a text block. \see Toolkit::TextView::SetLineJustification. + * + * A default font could be set through the \see Toolkit::TextView::SetFont method. If any font is set, text view automatically detects a suitable font for every character. + * + * Font priority: + * 1) Use the font specified in text decoration. + * 2) Use automatic font detection. + * + * Integration with other classes (GetSizeAndPositionTable ) + * + * Text decoration ( Color, Font, Size, italic and all features supported in TextActor ) + */ +class TextView : public Control +{ +public: + //Signal Names + static const char* const SIGNAL_TEXT_SCROLLED; ///< Signal emitted when the scroll position changes. @see SignalScrolled() + + /** + * The names of custom properties installed by this control. + */ + // Property Names + static const char* const PROPERTY_TEXT; ///< name "text", type std::string + static const char* const PROPERTY_MULTILINE_POLICY; ///< name "multiline-policy", type std::string + static const char* const PROPERTY_WIDTH_EXCEED_POLICY; ///< name "width-exceed-policy", type std::string + static const char* const PROPERTY_HEIGHT_EXCEED_POLICY; ///< name "height-exceed-policy", type std::string + static const char* const PROPERTY_LINE_JUSTIFICATION; ///< name "line-justification", type std::string + static const char* const PROPERTY_FADE_BOUNDARY_LEFT; ///< name "fade-boundary-left", type int + static const char* const PROPERTY_FADE_BOUNDARY_RIGHT; ///< name "fade-boundary-right", type int + static const char* const PROPERTY_FADE_BOUNDARY_TOP; ///< name "fade-boundary-top", type int + static const char* const PROPERTY_FADE_BOUNDARY_BOTTOM; ///< name "fade-boundary-bottom", type int + static const char* const PROPERTY_LINE_HEIGHT_OFFSET; ///< name "line-height-offset", type float + static const char* const PROPERTY_HORIZONTAL_ALIGNMENT; ///< name "horizontal-alignment", type std::string + static const char* const PROPERTY_VERTICAL_ALIGNMENT; ///< name "vertical-alignment", type std::string + + /** + * Structure used to retrieve Layout info per character. + */ + struct CharacterLayoutInfo + { + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + CharacterLayoutInfo(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~CharacterLayoutInfo(); + + /** + * Copy constructor. + */ + CharacterLayoutInfo( const CharacterLayoutInfo& characterLayoutInfo ); + + /** + * Assignment operator. + */ + CharacterLayoutInfo& operator=( const CharacterLayoutInfo& character ); + + /** + * Constructor. + * + * @param[in] size of the character. + * @param[in] position of the character. + * @param[in] isNewLineChar Whether the character is a new line character. + * @param[in] isRightToLeftCharacter Whether is a right to left character. + * @param[in] visible Whether is visible. + * @param[in] descender of the character (distance from the base line to the bottom of the character.) + */ + CharacterLayoutInfo( const Size& size, + const Vector3& position, + bool isNewLineChar, + bool isRightToLeftCharacter, + bool visible, + float descender ); + + Size mSize; ///< Size of the group of characters. + Vector3 mPosition; ///< Position of the group of characters within the text view. + bool mIsNewLineChar:1; ///< Whether this group of characters represent a new line. + bool mIsRightToLeftCharacter:1; ///< Whether it's a right-to-left character. + bool mIsVisible:1; ///< Whether this group of characters is visible or not. + float mDescender; ///< The character's descender which is the distance from the baseline to the bottom of the character + }; + + typedef std::vector CharacterLayoutInfoContainer; + + /** + * Stores some info about a laid-out line. + */ + struct LineLayoutInfo + { + std::size_t mCharacterGlobalIndex; ///< Global index within the whole text of the first character of current laid-out line. + Size mSize; ///< Size of the current laid-out line. + float mAscender; ///< The max ascender of the current laid-out line. + }; + typedef std::vector LineLayoutInfoContainer; + + struct TextLayoutInfo + { + /** + * Default constructor. + */ + TextLayoutInfo(); + + /** + * Empty destructor + * + * @note Added to increase coverage. + */ + ~TextLayoutInfo(); + + /** + * Copy constructor. + */ + TextLayoutInfo( const TextLayoutInfo& textLayoutInfo ); + + /** + * Assignment operator. + */ + TextLayoutInfo& operator=( const TextLayoutInfo& textLayoutInfo ); + + CharacterLayoutInfoContainer mCharacterLayoutInfoTable; ///< The table of character positions and sizes sorted by the characters' visual index. + LineLayoutInfoContainer mLines; ///< For each laid-out line, it stores an index to the first character of the line. + std::vector mCharacterLogicalToVisualMap; ///< The map to store the character's logical (input) index according to its visual (reordered) index. + std::vector mCharacterVisualToLogicalMap; ///< The map to store the character's visual (reordered) index according to its logical (input) index. + Size mTextSize; ///< Text size after relayout. + Vector2 mScrollOffset; ///< Scroll's position. + }; + + /** + * It represents a fade boundary. + * If Exceed policy is set to Fade all text which does not fit within the text-view fade boundary is faded out. Text which exceeds the text-view boundary becomes invisible. + * The \e left, \e right, \e top and \e bottom values are positive, in pixels and set the distances between the text-view and fade boundaries. + */ + struct FadeBoundary + { + /** + * Default constructor. + * Initializes all values to 0. It means no fade boundary. + */ + FadeBoundary(); + + /** + * Constructor. + * Initializes the fade boundary with the given values. + * + * @param[in] left value in pixels. + * @param[in] right value in pixels. + * @param[in] top value in pixels. + * @param[in] bottom value in pixels. + */ + FadeBoundary( PixelSize left, PixelSize right, PixelSize top, PixelSize bottom ); + + PixelSize mLeft; + PixelSize mRight; + PixelSize mTop; + PixelSize mBottom; + }; + + /** + * Define how to split the text in lines. + * SplitByNewLineChar will split the text in lines when a '\\n' character is found. + * SplitByWord has effect only when TextView size is assigned. + * It will split the text in lines when a '\\n' character is found or if a line exceeds the TextView's boundary. This option won't split a word in two. + * SplitByChar has effect only when TextView size is assigned. + * It will split the text in lines when a '\\n' character is found or if a line exceeds the TextView's boundary. This option might split a word in two. + * The default value is SplitByNewLineChar. + */ + enum MultilinePolicy + { + SplitByNewLineChar, ///< Text lines will split when '\\n' character is found. + SplitByWord, ///< Text lines will split by word or if '\\n' character is found. It has effect only when TextView size is assigned. + SplitByChar ///< Text lines will split by char or if '\\n' character is found. It has effect only when TextView size is assigned. + }; + + /** + * Define how to display the text when it doesn't fit inside the TextView. + * The default value is ShrinkToFit. + */ + enum ExceedPolicy + { + Original, ///< Will display the text in its original size. If a line, a word or a character is bigger than the TextView size it may exceed its boundary. + Truncate, ///< Will display the text in its original size. It won't display the text which exceeds the TextView boundary. + Fade, ///< Will display the text in its original size. It won't display the text which exceeds the TextView boundary. It fades the text out. + Split, ///< Will split the text in a new line. + ShrinkToFit, ///< Will shrink the text to fit the TextView boundary. + EllipsizeEnd ///< Will ellipsize the text by the end. + }; + + /** + * Define how to justify lines inside the text area. + * The default value is Left. + */ + enum LineJustification + { + Left, ///< Justify to the left. + Center, ///< Centered. + Right, ///< Justify to the right. + Justified ///< Line justified. + }; + +public: + /** + * Create an TextView handle; this can be initialised with TextView::New() + * Calling member functions with an uninitialised Dali::Object handle is not allowed. + */ + TextView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + TextView( const TextView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + TextView& operator=( const TextView& handle ); + + /** + * Create a TextView control with no text + * @return A handle the TextView control. + */ + static TextView New(); + + /** + * Create a TextView control. + * @param[in] text to display. + * @return A handle the TextView control. + */ + static TextView New( const std::string& text ); + + /** + * @copydoc TextView New( const std::string& text ) + */ + static TextView New( const MarkupProcessor::StyledTextArray& text ); + + /** + * Downcast an Object handle to TextView. If handle points to a TextView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a TextView or an uninitialized handle + */ + static TextView DownCast( BaseHandle handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~TextView(); + + /** + * Replace the current text with a new text string. + * @param[in] text to display. The string may contain style tags. + */ + void SetText( const std::string& text ); + + /** + * Replace the current text with a new text string with style. + * @param[in] text with style to display. + */ + void SetText( const MarkupProcessor::StyledTextArray& text ); + + /** + * Inserts the given text in the specified position + * + * @param[in] position Where the given text is going to be added. + * @param[in] text The text to be added. + */ + void InsertTextAt( std::size_t position, const std::string& text ); + + /** + * @copydoc InsertTextAt( std::size_t position, const std::string& text ) + */ + void InsertTextAt( std::size_t position, const MarkupProcessor::StyledTextArray& text ); + + /** + * Replaces part of the text. + * + * It removes the specified number of characters from the given position and inserts the given text in the same specified position. + * + * @param[in] position of the first character to be removed and Where the given text is going to be added. + * @param[in] numberOfCharacters number of characters to be removed. + * @param[in] text The text to be added. + */ + void ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const std::string& text ); + + /** + * @copydoc ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const std::string& text ) + */ + void ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text ); + + /** + * Removes the specified number of characters from the given position. + * + * @param[in] position of the first character to be removed. + * @param[in] numberOfCharacters number of characters to be removed. + */ + void RemoveTextFrom( std::size_t position, std::size_t numberOfCharacters ); + + /** + * Get the currently displayed text. + * @return The currently displayed text. + */ + std::string GetText() const; + + /** + * Sets a line height offset. + * The line height offset will be added to the font line height. + * @param [in] offset The height offset in PointSize units. + */ + void SetLineHeightOffset( PointSize offset ); + + /** + * Retrieves the line height offset. + * @return The line height offset in PointSize units. + */ + PointSize GetLineHeightOffset() const; + + /** + * Sets the given style to the current text. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * @note TextView doesn't store a copy of the given style, it applies the given style to the current text only. + * Subsequent calls to SetText() will override any style set by this method. + * @param[in] style The given style + * @param[in] mask The bit mask. + */ + void SetStyleToCurrentText( const TextStyle& style, TextStyle::Mask mask = TextStyle::ALL ); + + /** + * Set the current text alignment. + * Default alignment is (HorizontalCenter | VerticalCenter) + * @param[in] align The new alignment option. + */ + void SetTextAlignment( Alignment::Type align ); + + /** + * Get the current text alignment combined into a single value. + * The values can be tested by using the & operator + * and the desired flag. e.g. if (GetTextAlignment() & HorizontalCentre) ... + */ + Alignment::Type GetTextAlignment() const; + + /** + * Sets how to split the text in lines policy. + * @param policy The multi-line policy. SplitByNewLineChar is set by default. + */ + void SetMultilinePolicy( MultilinePolicy policy ); + + /** + * Gets the split in lines policy. + * @return The multi-line policy. + */ + MultilinePolicy GetMultilinePolicy() const; + + /** + * Sets how to display the text inside the TextView when it exceeds the text-view's width. + * @param policy The exceed policy. Original is set by default. + */ + void SetWidthExceedPolicy( ExceedPolicy policy ); + + /** + * Gets the width exceed policy. + * @return The exceed policy. + */ + ExceedPolicy GetWidthExceedPolicy() const; + + /** + * Sets how to display the text inside the TextView when it exceeds the text-view's height. + * @param policy The exceed policy. Original is set by default. + */ + void SetHeightExceedPolicy( ExceedPolicy policy ); + + /** + * Gets the height exceed policy. + * @return The exceed policy. + */ + ExceedPolicy GetHeightExceedPolicy() const; + + /** + * Sets how to justify lines inside the text area. + * @param justification The line justification. Left is set by default. + */ + void SetLineJustification( LineJustification justification ); + + /** + * Gets the line justification. + * @return The line justification. + */ + LineJustification GetLineJustification() const; + + /** + * Sets a fade boundary. + * + * @see FadeBoundary. + * + * @param[in] fadeBoundary The given fade boundary. + */ + void SetFadeBoundary( const FadeBoundary& fadeBoundary ); + + /** + * Retrieves the fade boundary. + * + * @see FadeBoundary. + * + * @return The fade boundary. + */ + const FadeBoundary& GetFadeBoundary() const; + + /** + * Sets the ellipsize text. + * + * @param[in] ellipsizeText The new text. The string may contain style tags. By default the ellipsize text is '...' + */ + void SetEllipsizeText( const std::string& ellipsizeText ); + + /** + * Sets the ellipsize text. + * + * @param[in] ellipsizeText The new text with its style. + */ + void SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText ); + + /** + * Retrieves the ellipsize text. + * + * @return The ellipsize text. + */ + std::string GetEllipsizeText() const; + + /** + * A mechanism to retrieve layout information from the TextView. + * + * It produces a vector of CharcterLayoutInfo structures which describe the size and position of each character, + * two vectors which maps the logical and visual positions of the characters in a bidirectional text, the size + * of the whole laid-out text and the scroll offset value. + * + * @see TextLayoutInfo. + * + * @param[out] textLayoutInfo A structure with text layout information. + */ + void GetTextLayoutInfo( TextLayoutInfo& textLayoutInfo ); + + /** + * Allows modification of text-actor's position in the depth sort algorithm. + * + * @see Dali::RenderableActor::SetSortModifier() + * @param [in] depthOffset the offset to be given to the internal text-actors. Positive values pushing it further back. + */ + void SetSortModifier( float depthOffset ); + + /** + * Sets whether text-view renders text using a previously generated snapshot. + * + * Rendering long text using a snapshot may increase performance. The default value is \e true (render using a snapshot). + * + * @param[in] enable Whether text-view is using a snapshot to render text. + */ + void SetSnapshotModeEnabled( bool enable ); + + /** + * Retrieves whether text-view is using a snapshot to render text + * + * @return \e true if text-view is using a snapshot to render text, otherwhise it returns \e false. + */ + bool IsSnapshotModeEnabled() const; + + /** + * Enables or disables the text scroll. + * + * When scroll is enabled, snapshot mode will be enabled automatically. Equally, if scroll is disabled + * the snapshot mode is restored to the previous value. + * + * @param[in] enable Whether to enable the text scroll. + */ + void SetScrollEnabled( bool enable ); + + /** + * Retrieves whether the text scroll is enabled. + * + * @return \e true if the scroll is enabled. + */ + bool IsScrollEnabled() const; + + /** + * Sets a new scroll position. + * + * The new scroll position set could be trimmed if the text doesn't cover the whole text-view. + * i.e. If a text-view is 100x100 and a text is 200x100 a scroll position beyond 50x0 will be trimmed to 50x0. + * + * Call IsScrollPositionTrimmed() to know if the last scroll position set has been trimmed. + * + * A signal is emitted. @see ScrolledSignal(). + * + * @param[in] position The new scroll position. + */ + void SetScrollPosition( const Vector2& position ); + + /** + * Recrieves current scroll position. + * + * @return The scroll position. + */ + const Vector2& GetScrollPosition() const; + + /** + * Whether the last scroll position set was trimmed. + * + * @return \e true if the last scroll position set was trimmed, otherwise \e false. + */ + bool IsScrollPositionTrimmed() const; + +public: + // Signals + typedef SignalV2< void ( TextView textView, Vector2 scrollDelta ) > ScrolledSignalV2; + + /** + * Signal emitted when the scroll position changes. + * + * A callback with the following prototype can be connected to this signal. + * + * Callback(TextView textView, Vector2 scrollDelta) + * + * \e textView is the handle of the text-view emitting the signal. + * \e scrollDelta is the differente of the current scroll position with the previous one. + */ + ScrolledSignalV2& ScrolledSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + TextView( Internal::TextView& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + TextView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_ITEM_VIEW_H__ diff --git a/capi/dali-toolkit/public-api/dali-toolkit-capi-internal.h b/capi/dali-toolkit/public-api/dali-toolkit-capi-internal.h new file mode 100644 index 0000000..001ab9c --- /dev/null +++ b/capi/dali-toolkit/public-api/dali-toolkit-capi-internal.h @@ -0,0 +1,65 @@ +#ifndef __DALI_TOOLKIT_CAPI_INTERNAL_H__ +#define __DALI_TOOLKIT_CAPI_INTERNAL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#endif // __DALI_TOOLKIT_CAPI_INTERNAL_H__ diff --git a/capi/dali-toolkit/public-api/enums.h b/capi/dali-toolkit/public-api/enums.h new file mode 100644 index 0000000..436751e --- /dev/null +++ b/capi/dali-toolkit/public-api/enums.h @@ -0,0 +1,71 @@ +#ifndef __DALI_TOOLKIT_ENUMS_H__ +#define __DALI_TOOLKIT_ENUMS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace ControlOrientation +{ + +/** + * The internal orientation a control. + */ +enum Type +{ + Up, ///< The contents of control are in a vertical layout, from top to bottom + Left, ///< The contents of control are in a horizontal layout, from left to right + Down, ///< The contents of control are in a vertical layout, from bottom to top + Right ///< The contents of control are in a horizontal layout, from right to left +}; + +} // namespace ControlOrientation + +/** + * Query whether an orientation is vertical. + * @param[in] orientation The orientation. + * @return True if the orientation is vertical. + */ +bool IsVertical(ControlOrientation::Type orientation); + +/** + * Query whether an orientation is horizontal. + * @param[in] orientation The orientation. + * @return True if the orientation is horizontal. + */ +bool IsHorizontal(ControlOrientation::Type orientation); + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_ENUMS_H__ diff --git a/capi/dali-toolkit/public-api/factory/localized-control-factory.h b/capi/dali-toolkit/public-api/factory/localized-control-factory.h new file mode 100644 index 0000000..fcb42c3 --- /dev/null +++ b/capi/dali-toolkit/public-api/factory/localized-control-factory.h @@ -0,0 +1,100 @@ +#ifndef __DALI_TOOLKIT_LOCALIZED_CONTROL_FACTORY_H__ +#define __DALI_TOOLKIT_LOCALIZED_CONTROL_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class LocalizedControlFactory; +} + +/** + * LocalizedControlFactory + * This class provides functionality for creating controls which have localized text. + * This class keeps track of objects created using its factory methods, and updates them + * when the system language changes. + * + * Warning: If the developer calls SetText on the object to overwrite the managed TextView, + * then it leads to an inconsistent state where the object will be overwritten with the + * localized text when language/locale changes. + */ + + class LocalizedControlFactory : public BaseHandle + { + public: + + /** + * Creates a localized TextView, which is automatically updated when the locale or language changes. + * + * @pre The LocalizedControlFactory has been initialized. + * + * @param textID The id of the localized text with which a platform request (gettext) for localized text can be made. + * @param textDomain The text domain for the localized text. Eg "sys_string" + * @param textViewTheme A string containing style info about various properties of TextView for different + * locale/language. + */ + static Dali::Toolkit::TextView CreateLocalizedTextView( const std::string& textID, const std::string& textDomain = "sys_string", const std::string& textViewTheme = "{}" ); + + +private: + + /** + * Create a LocalizedControlFactory handle; this can be initialised with LocalizedControlFactory::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + LocalizedControlFactory(); + + /** + * Virtual destructor. + */ + virtual ~LocalizedControlFactory(); + + /** + * Get the singleton of LocalizedControlFactory object. + * @return A handle to the LocalizedControlFactory control. + */ + static LocalizedControlFactory Get(); + + /** + * Allows the creation of this Control from an Internal::LocalizedControlFactory pointer. + * @param[in] impl A pointer to the internal LocalizedControlFactory. + */ + LocalizedControlFactory(Internal::LocalizedControlFactory *impl); +}; // class LocalizedControlFactory + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_LOCALIZED_CONTROL_FACTORY_H__ diff --git a/capi/dali-toolkit/public-api/file.list b/capi/dali-toolkit/public-api/file.list new file mode 100644 index 0000000..d90d79c --- /dev/null +++ b/capi/dali-toolkit/public-api/file.list @@ -0,0 +1,83 @@ +capi_devel_header_files = \ + $(capi_devel_src_dir)/enums.h \ + $(capi_devel_src_dir)/dali-toolkit-capi-internal.h + +capi_devel_controls_header_files = \ + $(capi_devel_src_dir)/controls/control.h \ + $(capi_devel_src_dir)/controls/control-impl.h + +capi_devel_alignment_header_files = \ + $(capi_devel_src_dir)/controls/alignment/alignment.h + +capi_devel_bubble_emitter_header_files = \ + $(capi_devel_src_dir)/controls/bubble-effect/bubble-emitter.h + +capi_devel_buttons_header_files = \ + $(capi_devel_src_dir)/controls/buttons/button.h \ + $(capi_devel_src_dir)/controls/buttons/push-button.h + +capi_devel_cluster_header_files = \ + $(capi_devel_src_dir)/controls/cluster/cluster-style.h + +capi_devel_default_controls_header_files = \ + $(capi_devel_src_dir)/controls/default-controls/solid-color-actor.h \ + $(capi_devel_src_dir)/controls/default-controls/push-button-factory.h + +capi_devel_image_view_header_files = \ + $(capi_devel_src_dir)/controls/image-view/masked-image-view.h + +capi_devel_popup_header_files = \ + $(capi_devel_src_dir)/controls/popup/popup.h + +capi_devel_scroll_component_header_files = \ + $(capi_devel_src_dir)/controls/scroll-component/scroll-bar.h \ + $(capi_devel_src_dir)/controls/scroll-component/scroll-component.h + +capi_devel_scrollable_header_files = \ + $(capi_devel_src_dir)/controls/scrollable/scrollable.h + +capi_devel_item_view_header_files = \ + $(capi_devel_src_dir)/controls/scrollable/item-view/grid-layout.h \ + $(capi_devel_src_dir)/controls/scrollable/item-view/item-factory.h \ + $(capi_devel_src_dir)/controls/scrollable/item-view/item-layout.h \ + $(capi_devel_src_dir)/controls/scrollable/item-view/item-view.h + +capi_devel_scroll_view_header_files = \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-effect.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-custom-effect.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-cube-effect.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-slide-effect.h \ + $(capi_devel_src_dir)/controls/scrollable/scroll-view/scroll-view-twist-effect.h + +capi_devel_super_blur_view_header_files = \ + $(capi_devel_src_dir)/controls/super-blur-view/super-blur-view.h + +capi_devel_text_input_header_files = \ + $(capi_devel_src_dir)/controls/text-input/text-input.h + +capi_devel_text_view_header_files = \ + $(capi_devel_src_dir)/controls/text-view/text-view.h + +capi_devel_factory_header_files = \ + $(capi_devel_src_dir)/factory/localized-control-factory.h + +capi_devel_focus_manager_header_files = \ + $(capi_devel_src_dir)/focus-manager/focus-manager.h \ + $(capi_devel_src_dir)/focus-manager/keyboard-focus-manager.h + +capi_devel_markup_processor_header_files = \ + $(capi_devel_src_dir)/markup-processor/markup-processor.h + +capi_devel_shader_effects_header_files = \ + $(capi_devel_src_dir)/shader-effects/dissolve-effect.h \ + $(capi_devel_src_dir)/shader-effects/image-region-effect.h \ + $(capi_devel_src_dir)/shader-effects/iris-effect.h \ + $(capi_devel_src_dir)/shader-effects/mask-effect.h \ + $(capi_devel_src_dir)/shader-effects/nine-patch-mask-effect.h \ + $(capi_devel_src_dir)/shader-effects/page-turn-book-spine-effect.h \ + $(capi_devel_src_dir)/shader-effects/page-turn-effect.h \ + $(capi_devel_src_dir)/shader-effects/ripple-effect.h \ + $(capi_devel_src_dir)/shader-effects/ripple2d-effect.h \ + $(capi_devel_src_dir)/shader-effects/swirl-effect.h diff --git a/capi/dali-toolkit/public-api/focus-manager/focus-manager.h b/capi/dali-toolkit/public-api/focus-manager/focus-manager.h new file mode 100644 index 0000000..d43dde7 --- /dev/null +++ b/capi/dali-toolkit/public-api/focus-manager/focus-manager.h @@ -0,0 +1,358 @@ +#ifndef __DALI_TOOLKIT_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class FocusManager; +} + +/** + * FocusManager + * This class provides the functionality of registering the focus order and description + * of actors and maintaining the focus chain. It provides functionality of setting the + * focus and moving the focus forward and backward. It also draws a highlight for the + * focused actor and emits a signal when the focus is changed. + */ + + class FocusManager : public BaseHandle + { + public: + //Signal Names + static const char* const SIGNAL_FOCUS_CHANGED; + static const char* const SIGNAL_FOCUS_OVERSHOT; + static const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED; + + // Property Names + static const std::string ACTOR_FOCUSABLE; ///< name "focusable", type bool + static const std::string IS_FOCUS_GROUP; ///< name "is-focus-group", type bool + + /** + * Accessibility needs four information which will be read by screen-reader. + * Reading order : Label -> Trait -> Optional (Value and Hint) + */ + enum AccessibilityAttribute + { + ACCESSIBILITY_LABEL = 0, ///< Simple text which contained in ui-control + ACCESSIBILITY_TRAIT, ///< Description of ui-control trait + ACCESSIBILITY_VALUE, ///< Current value of ui-control (Optional) + ACCESSIBILITY_HINT, ///< Hint for action (Optional) + ACCESSIBILITY_ATTRIBUTE_NUM + }; + + enum FocusOvershotDirection + { + OVERSHOT_PREVIOUS = -1, ///< Try to move previous of the first actor + OVERSHOT_NEXT = 1, ///< Try to move next of the last actor + }; + + public: + + //Focus changed signal + typedef SignalV2< void ( Actor, Actor ) > FocusChangedSignalV2; + + //Focus overshooted signal + typedef SignalV2< void ( Actor, FocusOvershotDirection ) > FocusOvershotSignalV2; + + //Focused actor activated signal + typedef SignalV2< void ( Actor ) > FocusedActorActivatedSignalV2; + + /** + * Create a FocusManager handle; this can be initialised with FocusManager::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + FocusManager(); + + /** + * Virtual destructor. + */ + virtual ~FocusManager(); + + /** + * Get the singleton of FocusManager object. + * @return A handle to the FocusManager control. + */ + static FocusManager Get(); + + /** + * Set the information of the specified actor's accessibility attribute. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor the text to be set with + * @param type The attribute type the text to be set with + * @param text The text for the actor's accessibility information + */ + void SetAccessibilityAttribute(Actor actor, AccessibilityAttribute type, const std::string& text); + + /** + * Get the text of the specified actor's accessibility attribute. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be queried + * @param type The attribute type to be queried + * @return The text of the actor's accessibility information + */ + std::string GetAccessibilityAttribute(Actor actor, AccessibilityAttribute type) const; + + /** + * Set the focus order of the actor. The focus order of each actor in the focus chain + * is unique. If there is another actor assigned with the same focus order already, + * the new actor will be inserted to the focus chain with that focus order, and the + * focus order of the original actor and all the actors followed in the focus chain + * will be increased accordingly. If the focus order assigned to the actor is 0, it + * means that actor's focus order is undefined (e.g. the actor has a description but + * with no focus order being set yet) and therefore that actor is not focusable. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor the focus order to be set with + * @param order The focus order of the actor + */ + void SetFocusOrder(Actor actor, const unsigned int order); + + /** + * Get the focus order of the actor. When the focus order is 0, it means the focus order + * of the actor is undefined. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be queried + * @return The focus order of the actor + */ + unsigned int GetFocusOrder(Actor actor) const; + + /** + * Generates a new focus order number which can be used to assign to actors which need to be + * appended to the end of the current focus order chain. The new number will be an increment + * over the very last focus order number in the focus chain. If the focus chain is empty then + * the function returns 1, else the number returned will be FOLast + 1 where FOLast is the focus + * order of the very last control in the focus chain. + * @pre The FocusManager has been initialized. + * @return The focus order of the actor + */ + unsigned int GenerateNewFocusOrder() const; + + /** + * Get the actor that has the specified focus order. It will return an empty handle if the + * actor is not in the stage or has a focus order of 0. + * @pre The FocusManager has been initialized. + * @param order The focus order of the actor + * @return The actor that has the specified focus order or an empty handle if no actor in the stage has the specified focus order. + */ + Actor GetActorByFocusOrder(const unsigned int order); + + /** + * Move the focus to the specified actor. Only one actor can be focused at the same time. + * The actor must have a defined focus order and must be focusable, visible and in the stage. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be focused + * @return Whether the focus is successful or not + */ + bool SetCurrentFocusActor(Actor actor); + + /** + * Get the current focused actor. + * @pre The FocusManager has been initialized. + * @return A handle to the current focused actor or an empty handle if no actor is focused. + */ + Actor GetCurrentFocusActor(); + + /** + * Get the focus group of current focused actor. + * @pre The FocusManager has been initialized. + * @return A handle to the immediate parent of the current focused actor which is also a focus group, + * or an empty handle if no actor is focused. + */ + Actor GetCurrentFocusGroup(); + + /** + * Get the focus order of currently focused actor. + * @pre The FocusManager has been initialized. + * @return The focus order of the currently focused actor or 0 if no actor is in focus. + */ + unsigned int GetCurrentFocusOrder(); + + /** + * Move the focus to the next focusable actor in the focus chain (according to the focus + * traversal order). When the focus movement is wrapped around, the focus will be moved + * to the first focusable actor when it reaches the end of the focus chain. + * @pre The FocusManager has been initialized. + * @return true if the moving was successful + */ + bool MoveFocusForward(); + + /** + * Move the focus to the previous focusable actor in the focus chain (according to the + * focus traversal order). When the focus movement is wrapped around, the focus will be + * moved to the last focusable actor when it reaches the beginning of the focus chain. + * @pre The FocusManager has been initialized. + * @return true if the moving was successful + */ + bool MoveFocusBackward(); + + /** + * Clear the focus from the current focused actor if any, so that no actor is focused + * in the focus chain. + * It will emit focus changed signal without current focused actor + * @pre The FocusManager has been initialized. + */ + void ClearFocus(); + + /** + * Clear the every registered focusable actor from focus-manager. + * @pre The FocusManager has been initialized. + */ + void Reset(); + + /** + * Set whether an actor is a focus group that can limit the scope of focus movement to + * its child actors in the focus chain. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be set as a focus group. + * @param isFocusGroup Whether to set the actor to be a focus group or not. + */ + void SetFocusGroup(Actor actor, bool isFocusGroup); + + /** + * Check whether the actor is set as a focus group or not. + * @pre The FocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be checked. + * @return Whether the actor is set as a focus group. + */ + bool IsFocusGroup(Actor actor) const; + + /** + * Set whether the group mode is enabled or not. + * When the group mode is enabled, the focus movement will be limited to the child actors + * of the current focus group including the current focus group itself. The current focus + * group is the closest ancestor of the current focused actor that set as a focus group. + * @pre The FocusManager has been initialized. + * @param enabled Whether the group mode is enabled or not + */ + void SetGroupMode(bool enabled); + + /** + * Get whether the group mode is enabled or not. + * @pre The FocusManager has been initialized. + * @return Whether the group mode is enabled or not. + */ + bool GetGroupMode() const; + + /** + * Set whether focus will be moved to the beginning of the focus chain when it reaches the + * end or vice versa. When both the wrap mode and the group mode are enabled, focus will be + * wrapped within the current focus group. Focus will not be wrapped in default. + * @pre The FocusManager has been initialized. + * @param wrapped Whether the focus movement is wrapped around or not + */ + void SetWrapMode(bool wrapped); + + /** + * Get whether the wrap mode is enabled or not. + * @pre The FocusManager has been initialized. + * @return Whether the wrap mode is enabled or not. + */ + bool GetWrapMode() const; + + /** + * Set the focus indicator actor. This will replace the default focus indicator actor + * in FocusManager and will be added to the focused actor as a highlight. + * @pre The FocusManager has been initialized. + * @pre The indicator actor has been initialized. + * @param indicator The indicator actor to be added + */ + void SetFocusIndicatorActor(Actor indicator); + + /** + * Get the focus indicator actor. + * @pre The FocusManager has been initialized. + * @return A handle to the focus indicator actor + */ + Actor GetFocusIndicatorActor(); + + /** + * Returns the closest ancestor of the given actor that is a focus group. + * @param actor The actor to be checked for its focus group + * @return The focus group the given actor belongs to or an empty handle if the given actor doesn't belong to any focus group + */ + Actor GetFocusGroup(Actor actor); + + public: // Signals + + /** + * This signal is emitted when the current focused actor is changed. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor originalFocusedActor, Actor currentFocusedActor); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusChangedSignalV2& FocusChangedSignal(); + + /** + * This signal is emitted when there is no way to move focus further. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor currentFocusedActor, FocusOvershotDirection direction); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusOvershotSignalV2& FocusOvershotSignal(); + + /** + * This signal is emitted when the current focused actor is activated. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor activatedActor); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusedActorActivatedSignalV2& FocusedActorActivatedSignal(); + +private: + + FocusManager(Internal::FocusManager *impl); + +}; // class FocusManager + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_FOCUS_MANAGER_H__ diff --git a/capi/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h b/capi/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h new file mode 100644 index 0000000..f86d78b --- /dev/null +++ b/capi/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h @@ -0,0 +1,245 @@ +#ifndef __DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class KeyboardFocusManager; +} + +/** + * KeyboardFocusManager + * This class provides the functionality of handling keyboard navigation and maintaining + * the two dimensional keyboard focus chain. It provides functionality of setting the + * focus and moving the focus in four directions (i.e. Left, Right, Up and Down). It also + * draws a highlight for the focused actor and emits a signal when the focus is changed. + */ + + class KeyboardFocusManager : public BaseHandle + { + public: + //Signal Names + static const char* const SIGNAL_PRE_FOCUS_CHANGE; + static const char* const SIGNAL_FOCUS_CHANGED; + static const char* const SIGNAL_FOCUS_GROUP_CHANGED; + static const char* const SIGNAL_FOCUSED_ACTOR_ACTIVATED; + + public: + + //Pre focus change signal + typedef SignalV2< Actor ( Actor, Actor, Control::KeyboardFocusNavigationDirection ) > PreFocusChangeSignalV2; + + //Focus changed signal + typedef SignalV2< void ( Actor, Actor ) > FocusChangedSignalV2; + + //Focus group changed signal + typedef SignalV2< void ( Actor, bool ) > FocusGroupChangedSignalV2; + + //Focused actor activated signal + typedef SignalV2< void ( Actor ) > FocusedActorActivatedSignalV2; + + /** + * Create a KeyboardFocusManager handle; this can be initialised with KeyboardFocusManager::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + KeyboardFocusManager(); + + /** + * Virtual destructor. + */ + virtual ~KeyboardFocusManager(); + + /** + * Get the singleton of KeyboardFocusManager object. + * @return A handle to the KeyboardFocusManager control. + */ + static KeyboardFocusManager Get(); + + /** + * Move the keyboard focus to the given actor. Only one actor can be focused at the same time. + * The actor must be in the stage already and keyboard focusable. + * @pre The KeyboardFocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be focused + * @return Whether the focus is successful or not + */ + bool SetCurrentFocusActor(Actor actor); + + /** + * Get the current focused actor. + * @pre The KeyboardFocusManager has been initialized. + * @return A handle to the current focused actor or an empty handle if no actor is focused. + */ + Actor GetCurrentFocusActor(); + + /** + * Move the focus to the next focusable actor in the focus chain in the given direction + * (according to the focus traversal order). + * @pre The KeyboardFocusManager has been initialized. + * @param direction The direction of focus movement + * @return true if the movement was successful + */ + bool MoveFocus(Control::KeyboardFocusNavigationDirection direction); + + /** + * Clear the focus from the current focused actor if any, so that no actor is focused + * in the focus chain. + * It will emit focus changed signal without current focused actor + * @pre The KeyboardFocusManager has been initialized. + */ + void ClearFocus(); + + /** + * Set whether the focus movement should be looped within the same focus group. + * The focus movement is not looped by default. + * @pre The KeyboardFocusManager has been initialized. + * @param enabled Whether the focus movement should be looped + */ + void SetFocusGroupLoop(bool enabled); + + /** + * Get whether the focus movement should be looped within the same focus group. + * @pre The KeyboardFocusManager has been initialized. + * @return Whether the focus movement should be looped + */ + bool GetFocusGroupLoop() const; + + /** + * Set whether an actor is a focus group that can limit the scope of focus movement to + * its child actors in the focus chain. Layout controls set themselves as focus groups + * by default. + * @pre The KeyboardFocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be set as a focus group. + * @param isFocusGroup Whether to set the actor as a focus group or not. + */ + void SetAsFocusGroup(Actor actor, bool isFocusGroup); + + /** + * Check whether the actor is set as a focus group or not. + * @pre The KeyboardFocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be checked. + * @return Whether the actor is set as a focus group. + */ + bool IsFocusGroup(Actor actor) const; + + /** + * Returns the closest ancestor of the given actor that is a focus group. + * @param actor The actor to be checked for its focus group + * @return The focus group the given actor belongs to or an empty handle if the given actor + * doesn't belong to any focus group + */ + Actor GetFocusGroup(Actor actor); + + /** + * Set the focus indicator actor. This will replace the default focus indicator actor + * in KeyboardFocusManager and will be added to the focused actor as a highlight. + * @pre The KeyboardFocusManager has been initialized. + * @pre The indicator actor has been initialized. + * @param indicator The indicator actor to be added + */ + void SetFocusIndicatorActor(Actor indicator); + + /** + * Get the focus indicator actor. + * @pre The KeyboardFocusManager has been initialized. + * @return A handle to the focus indicator actor + */ + Actor GetFocusIndicatorActor(); + + public: // Signals + + /** + * This signal is emitted before the focus is going to be changed. + * KeyboardFocusManager makes the best guess for which actor to focus towards the given direction, + * but applications might want to change that. By connecting with this signal, they can check the + * proposed actor to focus and return a different actor if they wish. + * This signal is only emitted when the navigation key is pressed and KeyboardFocusManager tries to + * move the focus automatically. It won't be emitted for focus movement by calling SetCurrentFocusActor + * directly. + * A callback of the following type may be connected: + * @code + * Actor YourCallbackName(Actor currentFocusedActor, Actor proposedActorToFocus, Control::KeyboardFocusNavigationDirection direction); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + PreFocusChangeSignalV2& PreFocusChangeSignal(); + + /** + * This signal is emitted after the current focused actor has been changed. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor originalFocusedActor, Actor currentFocusedActor); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusChangedSignalV2& FocusChangedSignal(); + + /** + * This signal is emitted when the focus group has been changed. + * If the current focus group has a parent layout control, KeyboardFocusManager will make the best guess + * for the next focus group to move the focus to in the given direction (forward or backward). If not, + * the application has to set the new focus. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor currentFocusedActor, bool forward); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusGroupChangedSignalV2& FocusGroupChangedSignal(); + + /** + * This signal is emitted when the current focused actor is activated. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(Actor activatedActor); + * @endcode + * @pre The Object has been initialized. + * @return The signal to connect to. + */ + FocusedActorActivatedSignalV2& FocusedActorActivatedSignal(); + + // Not intended for application developers + + /** + * Creates a new handle from the implementation. + * @param[in] impl A pointer to the object. + */ + explicit DALI_INTERNAL KeyboardFocusManager(Internal::KeyboardFocusManager *impl); + +}; // class KeyboardFocusManager + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_KEYBOARD_FOCUS_MANAGER_H__ diff --git a/capi/dali-toolkit/public-api/markup-processor/markup-processor.h b/capi/dali-toolkit/public-api/markup-processor/markup-processor.h new file mode 100644 index 0000000..ecb76e1 --- /dev/null +++ b/capi/dali-toolkit/public-api/markup-processor/markup-processor.h @@ -0,0 +1,167 @@ +#ifndef __DALI_TOOLKIT_MARKUP_PROCESSOR_H__ +#define __DALI_TOOLKIT_MARKUP_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace MarkupProcessor +{ + +/** + * Holds text and its style. + * + * mText is a Dali::Text object which can store text in different languages. mStyle is a Dali::TextStyle object which can + * store all text styling features provided by Dali::TextActor. + */ +struct StyledText +{ + StyledText() + : mText(), + mStyle() + { + } + + StyledText( const Text& text, const TextStyle& style ) + : mText( text ), + mStyle( style ) + { + } + + Text mText; ///< Store text. Could be a mix of different languages. + TextStyle mStyle; ///< Store the style for the text. +}; + +/** + * This type defines a vector of StyledText. It's used to store a whole text with its style + * and set it to a Dali::Toolkit::TextView. It could be used in other UI Dali::Toolkit::Control classes + * which need text with style. + */ +typedef std::vector StyledTextArray; + +/** + * Creates a text array with its style from a markup string. + * The syntax of a markup string is html-ish. It contains open, close and empty tags and some of them can contain parameters. + *
    + *
  • \e \\ Bold text. + *
  • \e \\ Italic text. + *
  • \e \\ Underlined text. + *
  • \e \
    New line. + *
  • \e \\ Specifies font properties: + *
      + *
    • \e face Font face. + *
    • \e size Font size. + *
    • \e color Font color. + *
    + *
  • \e \\ Specifies shadow properties @see Dali::TextActor::SetShadow(). + *
      + *
    • \e paramx X offset. + *
    • \e paramy Y offset. + *
    • \e color Shadow color. + *
    + *
  • \e \\ Specifies glow properties @see Dali::TextActor::SetGlow(). + *
      + *
    • \e param Glow around the text. + *
    • \e color Glow color. + *
    + *
  • \e \\ Specifies outline properties @see Dali::TextActor::SetOutline(). + *
      + *
    • \e paramx X thickness. + *
    • \e paramy Y thickness. + *
    • \e color Outline color. + *
    + *
  • \e \\ Specify the smooth edge @see Dali::TextActor::SetSmoothEdge(). + *
      + *
    • \e paramx Distance field. + *
    + *
+ * + * It transform any pair CR+LF new line characters into a single LF new line character. + * @param [in] markupString A string with style. + * @param [out] styledTextArray A text array split in characters, each one with its style. + */ +void GetStyledTextArray( const std::string& markupString, StyledTextArray& styledTextArray ); + +/** + * Creates a plain string from a text array (thus stripping the style meta) + * @param [in] styledTextArray A text array split in characters, each one with its style. + * @param [out] plainString A string without style. + */ +void GetPlainString( const StyledTextArray& styledTextArray, std::string& plainString ); + +/** + * Creates a markup string from a text array with its style. + * @param [in] styledTextArray A text array split in characters, each one with its style. + * @param [out] markupString A string with style. + */ +void GetMarkupString( const StyledTextArray& styledTextArray, std::string& markupString ); + +/** + * Sets a text style to the given text. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * @param[in,out] styledTextArray The given text + * @param[in] style The given style + * @param[in] mask The bit mask. + */ +void SetTextStyle( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask = TextStyle::ALL ); + +/** + * @see SetTextStyle( StyledTextArray& styledTextArray, const TextStyle& style, StyleMask mask ) + * @param[in] text The input text. + * @param[out] styledTextArray The input text with the given style. + * @param[in] style The given style. + * @param[in] mask The bit mask. + */ +void SetTextStyle( const Text& text, StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask = TextStyle::ALL ); + +/** + * Sets a text style to a range of characters of the given text. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + * @param[in,out] styledTextArray The given text + * @param[in] style The given style + * @param[in] mask The bit mask. + * @param[in] begin The first character of the range. + * @param[in] end The last character of the range. + * @note It will assert if begin or end are out of range, or if begin > end. + */ +void SetTextStyleToRange( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end ); + +} // namespace MarkupProcessor + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_MARKUP_PROCESSOR_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/dissolve-effect.h b/capi/dali-toolkit/public-api/shader-effects/dissolve-effect.h new file mode 100644 index 0000000..634e4ab --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/dissolve-effect.h @@ -0,0 +1,94 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_DISSOLVE_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_DISSOLVE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * DissolveEffect is a custom shader effect to achieve Dissolve effects in Image actors + */ +class DissolveEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized DissolveEffect; this can be initialized with DissolveEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + DissolveEffect(); + + /** + * Virtual destructor. + */ + virtual ~DissolveEffect(); + + /** + * Create an initialized DissolveEffect. + * @param[in] useHighPrecision True if using high precision in fragment shader for fully random noise, false otherwise + * @return A handle to a newly allocated Dali resource. + */ + static DissolveEffect New( bool useHighPrecision = true); + + /** + * Set the dissolve central line + * Use one point (position) and one direction ( displacement ) vector to define this line + * As we use the texture coordinate as pixel position to calculate random offset, + * the line should passing through rectangle {(0,0),(0,1),(1,0),(1,1)}, + * so make the position parameter with two component values between 0.0 to 1.0 + * @param[in] position The point ( locates within rectangle {(0,0),(0,1),(1,0),(1,1)} ) passed through by the central line + * @param[in] displacement The direction of the central line + */ + void SetCentralLine( const Vector2& position, const Vector2& displacement ); + + /** + * Sets the distortion applied to the effect texture. + * This value is proportional to the distortion applied; a value of zero means no distortion. + * @param [in] distortion The distortion value. + */ + void SetDistortion( float distortion ); + + /** + * Get the name for the distortion property + * @return A std::string containing the property name + */ + const std::string& GetDistortionPropertyName() const; + +private: // Not intended for application developers + DissolveEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SHADER_EFFECT_DISSOLVE_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/image-region-effect.h b/capi/dali-toolkit/public-api/shader-effects/image-region-effect.h new file mode 100644 index 0000000..6c16097 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/image-region-effect.h @@ -0,0 +1,97 @@ +#ifndef __DALI_TOOLKIT_IMAGE_REGION_EFFECT_H__ +#define __DALI_TOOLKIT_IMAGE_REGION_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * ImageRegionEffect is a custom shader effect to show only a region of an Image actor + */ +class ImageRegionEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized ImageRegionEffect; this can be initialized with ImageRegionEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + ImageRegionEffect(); + + /** + * Virtual destructor. + */ + virtual ~ImageRegionEffect(); + + /** + * Create an initialized ~ImageRegionEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ImageRegionEffect New(); + + /** + * Set the top-left corner of the image region. + * The coordinates are in percentage, (0,0) being the top-left and (1,1) the bottom right of the original image. + * @param [in] point The top-left corner of the region. + */ + void SetTopLeft(const Vector2& point); + + /** + * Set the bottom-right corner of the image region. + * The coordinates are in percentage, (0,0) being the top-left and (1,1) the bottom right of the original image. + * @param [in] point The bottom-right corner of the region. + */ + void SetBottomRight(const Vector2& point); + + /** + * Get the name for the top-left point property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetTopLeftPropertyName() const; + + /** + * Get the name for the bottom-right point property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetBottomRightPropertyName() const; + +private: // Not intended for application developers + ImageRegionEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_IMAGE_REGION_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/iris-effect.h b/capi/dali-toolkit/public-api/shader-effects/iris-effect.h new file mode 100644 index 0000000..9f8b33a --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/iris-effect.h @@ -0,0 +1,120 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_IRIS_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_IRIS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * IrisEffect is a custom shader effect to achieve iris effects in Image actors + */ +class IrisEffect : public ShaderEffect +{ + +public: + + /** + * Create an uninitialized IrisEffect; this can be initialized with IrisEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + IrisEffect(); + + /** + * Virtual destructor. + */ + virtual ~IrisEffect(); + + /** + * Create an initialized IrisEffect + * @return A handle to a newly allocated Dali resource. + */ + static IrisEffect New(); + + /** + * Set the radius of the iris effect (in texture coordinate distance, + * i.e. 0.0 (no circle) to 1.0 (complete circle), to > 1.0 (extending + * outside of texture) + * + * @note For Atlas Textures results may be unpredictable. + * + * @param [in] radius The new radius. + */ + void SetRadius(float radius); + + /** + * Set the blend factor of the iris effect. + * + * The lower the value, the larger the blending portion + * (between Opaque & Transparent) + * + * Blending will account for 1 / blendFactor of the radius + * of the texture. + * + * @param [in] value The new blend Factor. + */ + void SetBlendFactor(float value); + + /** + * Sets the center point of the iris (in texture coordinates) + * + * @param[in] center The center point. + */ + void SetCenter( const Vector2& center ); + + /** + * Get the name for the radius property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetRadiusPropertyName() const; + + /** + * Get the name for the blend factor property + * @return A std::string containing the property name + */ + const std::string& GetBlendFactorPropertyName() const; + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + +private: // Not intended for application developers + IrisEffect(ShaderEffect handle); +}; + +} +} + +/** + * @} + */ +#endif diff --git a/capi/dali-toolkit/public-api/shader-effects/mask-effect.h b/capi/dali-toolkit/public-api/shader-effects/mask-effect.h new file mode 100644 index 0000000..de6c223 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/mask-effect.h @@ -0,0 +1,76 @@ +#ifndef __DALI_TOOLKIT_MASK_EFFECT_H__ +#define __DALI_TOOLKIT_MASK_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * MaskEffect is used to control which parts of an image are visible, using the alpha channel of a separate mask image. + * Typically mask images should be the same size as the main image being viewed, but this isn't essential. + * + * Usage example: + * + * ImageActor actor = ImageActor::New( Image( EXAMPLE_IMAGE_PATH ) ); + * MaskEffect maskEffect = MaskEffect::New( Image::New( MASK_IMAGE_PATH ) ); + * actor.SetShaderEffect( maskEffect ); + */ +class DALI_IMPORT_API MaskEffect : public ShaderEffect +{ +public: + + /** + * Create an empty MaskEffect handle. + */ + MaskEffect(); + + /** + * Virtual destructor. + */ + virtual ~MaskEffect(); + + /** + * Create a MaskEffect. + * @return A handle to a newly allocated MaskEffect. + */ + static MaskEffect New( Image maskImage ); + +private: // Not intended for application developers + + DALI_INTERNAL MaskEffect( ShaderEffect handle ); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_MASK_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.h b/capi/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.h new file mode 100644 index 0000000..43de3b2 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.h @@ -0,0 +1,77 @@ +#ifndef __DALI_TOOLKIT_NINE_PATCH_MASK_EFFECT_H__ +#define __DALI_TOOLKIT_NINE_PATCH_MASK_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * NinePatchMaskEffect is used to control which parts of an image are visible, using the alpha channel of a separate mask image. + * The mask image is expected to be smaller than the main image being viewed. + * Conceptually the mask image is divided into a 3x3 grid (9 patches). The middle patch is stretched whilst the outer border is not. + * + * Usage example: + * + * @code + * ImageActor actor = ImageActor::New( Image( EXAMPLE_IMAGE_PATH ) ); + * NinePatchMaskEffect::Apply( actor, MASK_IMAGE_PATH ); + * @endcode + * + * NinePatchMaskEffect is mutually exclusive with ImageActor::STYLE_NINE_PATCH i.e. the actor's main image should not be a nine-patch. + */ +namespace NinePatchMaskEffect +{ + +/** + * Apply the mask effect to an ImageActor. + * NinePatchMaskEffect is mutually exclusive with ImageActor::STYLE_NINE_PATCH i.e. the actor's main image should not be a nine-patch. + * @param [in] actor The actor which needs the effect. To remove the effect call actor.RemoveShaderEffect(). + * @param [in] maskImage The path to a file containing the mask. The center pixels of the mask will be stretched. + */ +void Apply( ImageActor actor, const std::string& maskImage ); + +/** + * Apply the mask effect to an ImageActor. + * NinePatchMaskEffect is mutually exclusive with ImageActor::STYLE_NINE_PATCH i.e. the actor's main image should not be a nine-patch. + * @param [in] actor The actor which needs the effect. To remove the effect call actor.RemoveShaderEffect(). + * @param [in] maskImage The path to a file containing the mask. + * @param [in] maskBorder Specifies the part of the mask image that will be stretched (left, top, right, bottom). + */ +void Apply( ImageActor actor, const std::string& maskImage, const Vector4& maskBorder ); + +} // namespace NinePatchMaskEffect + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_NINE_PATCH_MASK_EFFECT_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.h b/capi/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.h new file mode 100644 index 0000000..72330bf --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.h @@ -0,0 +1,99 @@ +#ifndef __DALI_PAGE_TURN_BOOK_SPINE_EFFECT_H__ +#define __DALI_PAGE_TURN_BOOK_SPINE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ +/** + * It is an assisting effect of PageTurnEffect to display book spine on STATIC pages, and also flip the image horizontally when needed. + * When the page is turned over in landscape, call SetIsBackImageVisible(true), this effect can display the back image correctly after the imageActor been rotated 180 degrees. + * To display the pages visually consistent with its turning state, please set the uniforms with the same values as the PageTurnEffect + **/ +class PageTurnBookSpineEffect : public ShaderEffect +{ +public: + /** + * Create an uninitialized PageTurnBookSpineEffect; this can be initialized with PageTurnBookSpineEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + PageTurnBookSpineEffect(); + + /** + * Virtual destructor. + */ + virtual ~PageTurnBookSpineEffect(); + + /** + * Create an initialized PageTurnBookSpineEffect. + * @return A handle to a newly allocated Dali resource. + */ + static PageTurnBookSpineEffect New(); + + /** + * Set whether the current page is with its backside visible + * Need to pass the parameter as true for the page which is turned over but still visible in Landscape + * @param [in] isBackVisible True for page with its backside upwards + */ + void SetIsBackImageVisible( bool isBackVisible ); + + /** + * Set the page width of the PageTurnBookSpineEffect. + * @param [in] pageWidth The width of the page size. + */ + void SetPageWidth( float pageWidth ); + + /** + * Set the width of shadow to be pageSize * shadowWidth + * this shadow appears at the edges of the actor which is not visible on static pages + * @param [in] shadowWidth The width for the simulated shadow + */ + void SetShadowWidth( float shadowWidth ); + + /** + * Set the spine shadow parameter + * The two parameters are the major&minor radius (in pixels) to form an ellipse shape + * The top-left quarter of this ellipse is used to calculate spine normal for simulating shadow + * @param [in] spineShadowParameter The major&minor ellipse radius for the simulated spine shadow + */ + void SetSpineShadowParameter( const Vector2& spineShadowParameter ); + + +private:// Helper for New() + PageTurnBookSpineEffect( ShaderEffect handle ); + +}; // End of PageTurnBookSpineEffect class + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif /* __DALI_PAGE_TURN_BOOK_SPINE_EFFECT_H__ */ diff --git a/capi/dali-toolkit/public-api/shader-effects/page-turn-effect.h b/capi/dali-toolkit/public-api/shader-effects/page-turn-effect.h new file mode 100644 index 0000000..97f5c38 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/page-turn-effect.h @@ -0,0 +1,167 @@ +#ifndef __DALI_PAGE_TURN_EFFECT_H_ +#define __DALI_PAGE_TURN_EFFECT_H_ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * PageTurnEffect implementation class + */ +class PageTurnEffect; + +} // namespace Internal + +/** + * PageTurnEffect is a custom shader effect to achieve page turn effect for Image actors + * + * Usage example:- + * + * // create shader used for doing page-turn effect\n + * PageTurnEffect pageTurnEffect = PageTurnEffect::New(); + * + * // set image actor shader to the page-turn one\n + * // for portrait view, one image actor for each page\n + * // for landscape view, the page turned over is still visible, so back image is needed \n + * // in this case, create another image Actor using the back image and added to the page actor \n + * ImageActor pageActor = ImageActor::New(....); \n + * ImageActor backImageActor = ImageActor::New(....); \n + * pageActor.Add(backPageActor);\n + * pageActor.SetShaderEffect ( pageTurnEffect ); \n + * + * //set initial values + * pageTurnEffect.SetPageSize();\n + * pageTurnEffect.SetOriginalCenter();\n + * pageTurnEffect.SetIsTurningBack();\n + * pageTurnEffect.SetCurrentCenter();\n + * + * //Animate it with the current center property\n + * Animation animation[mAnimationIndex] = Animation::New( ... );\n + * animation.AnimateTo(Property( pageTurnEffect, pageTurnEffect.PageTurnEffect::GetCurrentCenterPropertyName() ), + * currentCenter, + * AlphaFunctions::...);\n + * animation[mAnimationIndex].Play(); \n + */ + +class PageTurnEffect : public ShaderEffect +{ +public: + /** + * Create an uninitialized PageTurnEffect; this can be initialized with PageTurnEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + PageTurnEffect(); + + /** + * Virtual destructor. + */ + virtual ~PageTurnEffect(); + + /** + * Create an initialized PageTurnEffect. + * If fake shadow is used, need to apply the ShaderEffect::HINT_BLENDING + * @param[in] enableBlending If true, apply HINT_BLENDING when creating the shader object; If false, disable the HINT_BLENDING + * @return A handle to a newly allocated Dali resource. + */ + static PageTurnEffect New( bool enableBlending = true ); + + /** + * Set the page size of the PageTurnEffect. + * @param [in] pageSize The page size. + */ + void SetPageSize(const Vector2& pageSize); + + /** + * Set the origin point of the PageTurnEffect, the position where the mouse/finger is pushed from. + * @param [in] originalCenter The new origin point. + */ + void SetOriginalCenter(const Vector2& originalCenter); + + /** + * Set the center point of the PageTurnEffect, the current position of touch motion. + * @param [in] currentCenter The new center point. + */ + void SetCurrentCenter(const Vector2& currentCenter); + + /** + * Set whether the current page is turning forward or backward + * @param [in] isTurningBack True for turning backward or False for turning forward + */ + void SetIsTurningBack(bool isTurningBack); + + /** + * Set the width of shadow to be pageSize * shadowWidth + * @param [in] shadowWidth The width for the simulated shadow + */ + void SetShadowWidth(float shadowWidth); + + /** + * Set the spine shadow parameter + * The two parameters are the major&minor radius (in pixels) to form an ellipse shape + * The top-left quarter of this ellipse is used to calculate spine normal for simulating shadow + * @param [in] spineShadowParameter The major&minor ellipse radius for the simulated spine shadow + */ + void SetSpineShadowParameter(const Vector2& spineShadowParameter); + + /** + * Get the name for the page size property + * @return A std::string containing the property name + */ + const std::string& GetPageSizePropertyName() const; + + /** + * Get the name for the origin center property + * @return A std::string containing the property name + */ + const std::string& GetOriginalCenterPropertyName() const; + + /** + * Get the name for the current center property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetCurrentCenterPropertyName() const; + +public: // Not intended for application developers + + PageTurnEffect( ShaderEffect handle, Internal::PageTurnEffect* shaderExtension ); + +}; //end of PageTurnEffect class + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif /* __DALI_TOOLKIT_SC_CURVE_EFFECT_H_ */ diff --git a/capi/dali-toolkit/public-api/shader-effects/ripple-effect.h b/capi/dali-toolkit/public-api/shader-effects/ripple-effect.h new file mode 100644 index 0000000..5e8cc42 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/ripple-effect.h @@ -0,0 +1,108 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * RippleEffect is a custom shader effect to achieve ripple effects on Image actors + */ +class RippleEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized RippleEffect; this can be initialized with RippleEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + RippleEffect(); + + /** + * Virtual destructor. + */ + virtual ~RippleEffect(); + + /** + * Create an initialized RippleEffect. + * @return A handle to a newly allocated Dali resource. + */ + static RippleEffect New(); + + /** + * Set the amplitude of the effect. + * @param [in] amplitude The new amplitude. + */ + void SetAmplitude(float amplitude); + + /** + * Set the center point of the effect as screen coordinates. + * @param [in] center The new center point. + */ + void SetCenter(const Vector2& center); + + /** + * Set the time duration for the ripple. + * @param[in] time The time duration in float. + */ + void SetTime(float time); + + /** + * Get the name for the amplitude property + * @return A std::string containing the property name + */ + const std::string& GetAmplitudePropertyName() const; + + /** + * Get the name for the center property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the time property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetTimePropertyName() const; + +private: + RippleEffect(ShaderEffect handle); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/ripple2d-effect.h b/capi/dali-toolkit/public-api/shader-effects/ripple2d-effect.h new file mode 100644 index 0000000..45b9efb --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/ripple2d-effect.h @@ -0,0 +1,95 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE2D_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE2D_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * Ripple2DEffect is a custom shader effect to achieve 2d ripple effects on Image actors + */ +class Ripple2DEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized Ripple2DEffect; this can be initialized with Ripple2DEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + Ripple2DEffect(); + + /** + * Virtual destructor. + */ + virtual ~Ripple2DEffect(); + + /** + * Create an initialized Ripple2DEffect. + * @return A handle to a newly allocated Dali resource. + */ + static Ripple2DEffect New(); + + /** + * Set the amplitude of the 2d ripple. + * @param[in] amplitude The amplitude in float. + */ + void SetAmplitude(float amplitude); + + /** + * Set the time duration for the 2d ripple. + * @param[in] time The time duration in float. + */ + void SetTime(float time); + + /** + * Get the name for the amplitude property + * @return A std::string containing the property name + */ + const std::string& GetAmplitudePropertyName() const; + + /** + * Get the name for the time property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetTimePropertyName() const; + +private: + Ripple2DEffect(ShaderEffect handle); + +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SHADER_EFFECT_RIPPLE2D_H__ diff --git a/capi/dali-toolkit/public-api/shader-effects/swirl-effect.h b/capi/dali-toolkit/public-api/shader-effects/swirl-effect.h new file mode 100644 index 0000000..62c1538 --- /dev/null +++ b/capi/dali-toolkit/public-api/shader-effects/swirl-effect.h @@ -0,0 +1,105 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SWIRL_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_SWIRL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/** + * @addtogroup CAPI_DALI_FRAMEWORK + * @{ + */ + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * SwirlEffect is a custom shader effect to achieve swirl effects in Image actors + */ +class SwirlEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized SwirlEffect; this can be initialized with SwirlEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + SwirlEffect(); + + /** + * Virtual destructor. + */ + virtual ~SwirlEffect(); + + /** + * Create an initialized SwirlEffect. + * @return A handle to a newly allocated Dali resource. + */ + static SwirlEffect New(bool warp); + + /** + * Set the angle of the swirl. + * @param[in] angle The angle in float. + */ + void SetAngle(float angle); + + /** + * Set the center of the swirl. + * @param[in] center The center in Vector2. + */ + void SetCenter(const Vector2& center); + + /** + * Set the radius of the swirl. + * @param[in] radius The radius in float. + */ + void SetRadius(float radius); + + /** + * Get the name for the angle property + * @return A std::string containing the property name + */ + const std::string& GetAnglePropertyName() const; + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the radius property + * @return A std::string containing the property name + */ + const std::string& GetRadiusPropertyName() const; + +private: // Not intended for application developers + SwirlEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_TOOLKIT_SHADER_EFFECT_SWIRL_H__ diff --git a/dali-toolkit.manifest b/dali-toolkit.manifest new file mode 100644 index 0000000..08561d6 --- /dev/null +++ b/dali-toolkit.manifest @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dali-toolkit/dali-toolkit.h b/dali-toolkit/dali-toolkit.h new file mode 100644 index 0000000..00f7bad --- /dev/null +++ b/dali-toolkit/dali-toolkit.h @@ -0,0 +1,88 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_H__ +#define __DALI_TOOLKIT_INTERNAL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +// INTERNAL INCLUDES +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#endif // __DALI_TOOLKIT_EXT_H__ diff --git a/dali-toolkit/images/00_popup_bg.png b/dali-toolkit/images/00_popup_bg.png new file mode 100755 index 0000000000000000000000000000000000000000..18f9533989d8068a13ddfcfc3fa3dcc8548e061d GIT binary patch literal 4323 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 IM6N<$f=t~{Gynhq literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/00_popup_bubble_bg.png b/dali-toolkit/images/00_popup_bubble_bg.png new file mode 100755 index 0000000000000000000000000000000000000000..ad3740b9c820d39eea6b1d3a18eebe886d200358 GIT binary patch literal 4235 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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/00_popup_button_bg.png b/dali-toolkit/images/00_popup_button_bg.png new file mode 100755 index 0000000000000000000000000000000000000000..10c746648ca07f5df707b3d074060b28c33da428 GIT binary patch literal 2975 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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/00_popup_button_pressed.png b/dali-toolkit/images/00_popup_button_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..faa5cead3cfc338ce2c1ece0f39586329204036f GIT binary patch literal 1480 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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/copy_paste_icon_clipboard.png b/dali-toolkit/images/copy_paste_icon_clipboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d158872b68d9aebb59fc9de7b031ffc905be5613 GIT binary patch literal 1374 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK1|+Sd9?b$$k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDkcV&P$o& z6x?pHz^PXs=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6+Bm^Chk=3dlBbJfNX4zB zKmY&RGpj!M{{H?8r&A7hn2zWL{EJgq$NK2@jW3%wZJLy$t~^zCE#HyjjS&LAH>(x4 zF+GyLQeYy?TGvM9Vyeg3;$SUA?gG@}*l68W!bQ~m?NqW{; z3VdhyD7@mhp7YfQn}k)$#WS9}hXf|Lxg6VH_xBU~BYlTI^4xt+LE*j5yqY`YPVUi= z$=b(MD6mO-qiR2=!x>)}oy1Wdfj zwe^9>qzYMqc$TCxBiTQ~9oLmVI;JgW-YBr;#1pk$ZY* idO_xHpA^Ri24;rsE%`qGjeDvc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDkc;$&uNWNu_(Y~}_Gbys62OLHS$o& z6x?nx$EjBz=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6`fmB100sueLQfaRkcwMx zX72Z5b`)s8JE8f;45v>_O}=kjR$s|v@mx5yx3W9rpv~l|bEh_iDl{r5EM4-M@yPDx zuiF(HA6uNgckkcKX2~BrSl(@t-&}00Bj`Cvrlm)tOUwT9s*3--5zLw0CcdWwKJqPF z5IjXd)aWj={DTXh)AlqdmOZ(xa(OvpODNBQBU@~u7`^|m>pQ`}BPcbMJLflBjUAJ+ z!1OiMNl}-lKajB5`9`kdZ)1>@p`E z(z-RRrS;&gOKHnarp-7cFN2<>w>~Z+tmDpLyrgjayt_rYcN#m3>+L rfz8N&)6KHI8{aW6>ipmPpUZ&3uyKCE3)M|dpaRL$)z4*}Q$iB}y(`bF literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/copy_paste_icon_cut.png b/dali-toolkit/images/copy_paste_icon_cut.png new file mode 100644 index 0000000000000000000000000000000000000000..f819fc67588f5c6873dfc7080e26e342ed1d9fee GIT binary patch literal 3745 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 LNkvXXu0mjf05Bky literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/copy_paste_icon_paste.png b/dali-toolkit/images/copy_paste_icon_paste.png new file mode 100644 index 0000000000000000000000000000000000000000..139695fb846ffaa71af0d35232352584c43fe435 GIT binary patch literal 1329 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK1|+Sd9?b$$k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDkc;$~{A zUexa>j=9nEPons#quSI1!j@{5IZoBZ4)Qk|{xSFIICyhjG+f-L=;;%qcZARQ^tr~! z3;PU%28N+%R<}+XV(i#ruMqN$WiicztKMVr=&N^*hc@HQ~ZD)_s}B z7W{2~^@;_pubxOr_F0NfJeSY3E9+X=wuQS*+2S*&BnBRAz4NQ&@%&E{Cax*HFw5`4 z9wWI0vin%xL?oPAYlTc+c~AL>;0w3#EyFYa?`=t)Dk>gZSu^>s z;eu_qHwfH6@sD95)1MUzqU(KC`@M74*=~?=(ps6fEMGAA_<{Ky;klI$vYs+H9}!Io uys)Epn@f+u`}S=%F*Y9x?miB0U}TVtIk~6$FW*H_A?E4o=d#Wzp$Pzxh2)$7 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/copy_paste_icon_select.png b/dali-toolkit/images/copy_paste_icon_select.png new file mode 100644 index 0000000000000000000000000000000000000000..d4585d29d43b296a712ac298a849c99751a1405e GIT binary patch literal 1678 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 JbIUfS{{wc|knR8g literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/copypanelLine.png b/dali-toolkit/images/copypanelLine.png new file mode 100755 index 0000000000000000000000000000000000000000..4a9a10177ada87b052ee843c11f9d2067a19d733 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^OhD|w0V4Oi7>WQX#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmPaw+O$b-i=;|RG^TQr;B5V#O35G o*RGmgxSX7n00I(A+oV_-yl*g0-0uAT7*G|1r>mdKI;Vst0D^=mLI3~& literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/cursor.png b/dali-toolkit/images/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..14f288b1983e39fafb22e5bbc5e3710c5487d39b GIT binary patch literal 2821 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 KMNUMnLSTY!ArXB5 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/file.list b/dali-toolkit/images/file.list new file mode 100644 index 0000000..dbc5012 --- /dev/null +++ b/dali-toolkit/images/file.list @@ -0,0 +1,4 @@ +# Files to install here + +dali_toolkit_image_files =\ + $(toolkit_images_dir)/*.png diff --git a/dali-toolkit/images/insertpoint-icon.png b/dali-toolkit/images/insertpoint-icon.png new file mode 100755 index 0000000000000000000000000000000000000000..61ab8529bca54c499ab318067aa4ef730f1559fb GIT binary patch literal 5136 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 II;Vst0KDn_r2qf` literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/magnifier.png b/dali-toolkit/images/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..1846be2247a23b19418adcf5a380ebca7ccd6131 GIT binary patch literal 5191 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 B$;|)& literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/overshoot_ripple.png b/dali-toolkit/images/overshoot_ripple.png new file mode 100644 index 0000000000000000000000000000000000000000..ae3ccf6cea3e5acc816fe6d6ba6600ddf82db4c7 GIT binary patch literal 281234 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 HUYFWAZ)AN@0GtooaRYRJ%Ckxc6TJ$jLZFRrRKs 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

6Hb7~)PkoqLfcQ{N>n()!q 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

?;y;WK1GY{=6-*FidoL&=#XQbJ0ui$7 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%0Anl0dM`;@D(6)WQb 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

)s=M;!sXK6#i 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ෳ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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/popup_bg.png b/dali-toolkit/images/popup_bg.png new file mode 100755 index 0000000000000000000000000000000000000000..3b2ff97025f47c5b1a06d05b4d99d36db217970b GIT binary patch literal 3869 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 Y?@}lu)@5&S-G2a0RXvpon04s?0dKWJ*Z=?k literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/popup_scroll.png b/dali-toolkit/images/popup_scroll.png new file mode 100755 index 0000000000000000000000000000000000000000..1dc8fc095a770cc4550d056f75abf62eb04cfbc1 GIT binary patch literal 1304 zcmeAS@N?(olHy`uVBq!ia0vp^d_XM9!3HGDBJXzsDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49rTIArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPCb#*Z@HE=d} zaP^Az7Gs=x^?{Dj2SqGWM8kxDsRzV_CtDx~p72xifT_I*n5;kQE&Ip7z}VyI;uunK zYswUR@52rf?bV)J*5-zWbco2vSap5Tw)n}&!v6f3cLyI=UFW%XTzYevZnBt*x9eP5 z)QSZeUHJ%xDDFkvj9?V`5-ypn! zv+9A=9Mjon&%0dQv?g?qb%MQI!@uKYyK66GZT+>vZ$;-V(|7j1lhTyAH>S^eW*HaW z;OM)mV$L@C!v=jHZUm$@|1P`a5_^67M}wVG7bh)Ukt(Yse}iA5@xG3uu4({V<*W?B zLQb8}a^ganJFW7)b+pcPi>OOp+P`N?lKNS$rUmmg+7c@!tCj7(+x}yX@x{`+t2Mgj zO;Wl4-Ma7bosAMFYo=~r9I*Oo+3T;rQkMo0(p>)G5YG~i`^!&cx?1i$I&;zIPx~^(J{bP0l+XkKs9o20 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/popup_tail_down.png b/dali-toolkit/images/popup_tail_down.png new file mode 100755 index 0000000000000000000000000000000000000000..806ba0e88e5041f1147d3d3cc7e63159111be4cd GIT binary patch literal 3765 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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/popup_tail_left.png b/dali-toolkit/images/popup_tail_left.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ed0471472fca88c95d7dfafcf05e71db8b6a05 GIT binary patch literal 3685 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 literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/popup_tail_up.png b/dali-toolkit/images/popup_tail_up.png new file mode 100644 index 0000000000000000000000000000000000000000..599cf17af37b7886ae34830f6070ef86b89d58af GIT binary patch literal 3712 zcmV-`4uA29P)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RX1`-Y?3xeuv<^TW#vPnci zR7l6IR^Lk_R~S8aCYd;sAMLcXA}k7vR?##dY+-9_i3CANFbHYi3a*CgOIH_cA9VZJ zN0Z@Sw9Hf~)-AL&yL?jl<>tyL?{f^ZJL7U=jfpfS14L;d4Ra zj^mc7!#-3AMy`T@2EfOOXoQ5zgWpOP34*wfjHiP<5YPa05oqLn7r;9JZ^`a!QeL!G zMeV=_FNX;32Vem3cXxMxHZU;oWvNuUC2Q5&ka#aUTIGST!QdqP0J@f!m;cl>?UQ^y z|0`dM5oKx;k+!y|_t>EFg?#{YLLdkrkWQzQS65d*l*?s*EEfCRah&58l_CePlNgPm zJW!5aqJl0|7#tjY5{X29%H?vfEDOE8z0qhi8U_&HFt~LhiD_@aeI#TXgbKoj2IlAI zf3s~{xxBnYy7+hLf`ngmpjo7vg z#uyl5D3{AH48uD$HPyGhz5NfdM7E`6k}5515EbF$BpNo1jg1ZV_xDG$*=&P=Kqiwx zwOS3&%*=etpVtZ6*}@>nLv0W$7<9o@D)n+}Ym2$A`#%zlF%${~M5ECcrfGgksz{e? z@yM2_gn)-App!*{00L`kYcGq%;**1egO&ssW2jcE&@@d+Bobd+mSsz2dMC}BazZ64 z_(&dzTnJ82Pd^WZLh;Sb&9(#t&d$zYnr3KpbksdOJp7mBMUCV}QzMft@DU&V0Q{zD znz2}HzECKj(~-;N5Q#*R;c&Q@Y>1Yvj|w1@(1;p709`99E3fkTyth~^KBxk<#c>>E zc6Rnvn{SSnmaVWrld8c0V5HOO_|47D_d7c~9TN~Jl}ZQ(gZjk8M96WRJh$YoMN4n` zKB(l3;29ns{vr~I{8%Uy-kX3xHk(CXUte@`a`G#w2dU&>#1ttx8>gkYxw+T-`}^wk z_4T7k0Dxs#EFO>l9tZ?H(xw$^he68I3&69wy86rc`T4WGy}d^h5U5lt&~@FLNF<(G zmbJ+bK25gJ?o|Werc$Y=Jv}`$ySuyiSi`n$R4SEk78Vx1Bf%S;T9M9%Z^y^S|H@=C zZM))QaeRD?k&%%zk4k_shKq{}9335Li;IhY e^P`}!#QhH?q(Ko*vVi#j0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0000wNklAJn?{g9pd;pr^b hHmk2(Ac+88&H!QQBscb>&ldmy002ovPDHLkV1lFfL)QQR literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/slider-popup-arrow.png b/dali-toolkit/images/slider-popup-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..0675a5712a7b3ef01092638984a9e53899b67bc2 GIT binary patch literal 2916 zcmV-q3!C(bP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001wNkl zw*#CDD(chmDJlz;|A5YGXtb&<_iHGR*8D=KkCB=hlt*f65bAwzO$*9R&3Oo4Ya10UepTZ%vlcmuEA(suyjkbMv&z9Jj| O0000<>&kwYhed!>&79xqFi~6Pq?~RrrF5t3^v0qB-Wcn(k1XTA=@<=|9uC1?JiXYzH?pxeM6HS#G!}{(<#f XTxaSPeW3$DOBp;}{an^LB{Ts5KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000YwNkl&b=iWOrjye?rz6c$B@x?Y+Q)tCnC8j}0eW~~=zKRch zur0KN5(t!rmV%g&mRduTw#KArP}@|9i1aP^;>(a^GXLZJ-ubyd=Wp-5`f&EyXP-Nx zn3$yPf#Ke}@4n}(pKq=8t+j_l1pm)h_@p;@_wU!xXf*Z^(SBphUSrH25gFFT>@->r zDW#!}UNy$d8EwY2(IZ)=?#FTbVH_tTKl{m#pXmAYc|CCb#{0X%T6hd%j%JxY+>N?B zI-L%>-3W0Mp&Q4@G6kI}5Ho6FfLgtddcB4)2mzo8;I`80RusiIU;M>Sr@j&euHSfn zxL&WFG{&6hbi4ajR#vgNu!yB*6YJ|OL{W@1O^{mQ4Ft@DAPCV{ucO|mqtR$!V4xp^ zTL#c*^Z|r>Mw=^fl3f1P%fB4^(iHgDtq)F#$mv$AeQ<7m0W&kRm|s{#tF?|ePGLkt zi~s~c;9d!U5P?MUR}gApfO?~W!ND!qwsjDFef=P&yJ?zU{LQalzVwAEaQ((Vch~Fn zvvC}snVVk-C&ni+J2!{5wRNOviih5#SfbwSRwZ1>U>$k)>?4?yVTk@lAHsSF(dJI7)Jt#waqjHb05Rv!iDOKnM2EguqB0!d9d3-P**T$TEECsH-d-YVD#Al|ar!hV` zg?6WF-EV~>0vFb|HfACaSA}I?A@8;DTp>9TJCEiLv6@>?D6J3%A((LH=<`23^+*N& z{MO}Rm8sK2Lx2$Tu~uyutH z1Yu~jIsNQ&FAP6a0U|ot?M4S@A3VUy$|{U#57ShAu#bIbVStFMP~qO#u-|bn%t}L4 z;s$_-K^WE$gu%fq%T8{pz#lKYxl1d3qPenyrKKj4ECopse5J+9AkKwF4`j#_0>l+z z^8L;icAeoN%6`wzU=D`N2y1mf8aUv6X__9}pnx{!Xs6T0N^=z| z)Aoh{F_*4~k6(t3zY-RO#hI*Xe&*o{gz_inuSBK5RztUsQ9>!;5Vb}FV$9Ks0*W=dD}Cz~mI9N;dOigv)z6#gG@dT37=! zAO6mP@9%L9jZ$jA(t5|*S_@gG@|?^92prdO5$CWi`Kw>a{lqS1c@U5KR7g^V<>i&R zq$O0+6tdKV2w^P*aj+weqx}FzLIB8=+MC2Fq9}$DvCk*(p^So}ImX~+3!h{bdy1@7 z2w(D^9_~~j9lZpQJPN{Ai2wmJv{HMW2aGX$l*+92bK%&(T_J_oa=!^f_U%dV_#F8{ zk)uh4lS=adBt`z0N`CiAUl1^AwHidEgpko@I88HXttx3weCpt=E}$MrR4iXC@J1!c zSDeZWBCv>M-`8ZRcBy)fx&T2K!iX7m3TUl&W-5cxdEqK3aY-tvw3JXKZ~NXL>6Ox_ z*{J4wyWic8svh*;5h!mX00f*T2{E$M3t~N_Z2wAyX{6_lQnHB6U{hEfQvFE3 z%@N@#>H(s7Rw0LZuZto936;>`65B*&PzdEgS}0d|fjmVZ9+UQCg2iU1e6N8(s|;ey zoU^gfGR7c;VQ4F0$v=uttE9~(FJfDv3zFwk2QYc$t;%GXs%)0+n%_UL-`P<~&&3#| zSqdXE<`mGzjDP?R*jj>m{^D0_?*OWFbWB|7578LhMJ6_cJ`LKw5Aupct&o1U3mYS>G7L-cR z>9o;}x=jK;bP)ReYrh@QTHVg~v1=eULdj>LTxFUbr6!LKm(;t!F-k_uy$yfKfy<`J zwi8Uq(hTkOHDqaed+gJXN2>jRHn)s1WiZ-))$w-Mzn0m8Dlt&)bKwh4@JKUPE3f}M zFqre-GkNqK45by??RBiT)*z5u8ygzYHQt`#i0DPQ0s zE3kzGZy28xDUh8~Me20|Qu)1uq}NClMpN3LwZ4Mp@)C?TS0=_sr#78Rh>^=msk>Td z5Zf0Ng@}3tb9ouuGg>Lpn4d$dwSG4UgUb&ctNiuN zKaA;2T};zNDV4!!V{>!Qw1K@)bBH3Z$MyGb4Aklf#bSla(M`$UfjEh9vp@6k24n2TCDm&0paVgY z##mZdz|8a{wAL3M%uZc;tV2E`KA$G(*G1?!I6$LLU=AFKsSKeV3!_TDuhcUkP=0Vw zdQr&LgKCeQ7pyc*vAnc|>B%ufQRf8~tK` z|G=TG+lJ8J-;W>+y>l<`46Nr&sdvWk=;MxjuQ7ro>SAHx0mjEi(QdWw0C;J3X7aNi za1_^(XOA6FS$3|`KX828_U#xP+=?)4APBh1w=Hogj+b)YjvVx_>In2+`!f1ULz{Q#A$uIVp z)xY+C`@nDUOcVwt{AJ3C-mt0CEq&6#$oKrYFC=gKq!&#?#O2in^U+#+ai)7#yzE>O1=S`q60g zqtTZuLd+1Ok!BgvBtaZU=yp5kv|BJnHv!xRa0|fAnd!-|=3fnZuWvs647%O+o+L^3 zYpwPI*aKh~z)k=|cH1g|IRIk-MgZJL&gVyFrYHY9|LpOdE6! literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/slider-skin-progress.png b/dali-toolkit/images/slider-skin-progress.png new file mode 100644 index 0000000000000000000000000000000000000000..4da9e7ff23b98ee1eda625f016d5adf9608db8bb GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp@KrF$*1|$_fl!pK*#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmP)x1<0M@3-kPQ-G$hdb&7bkXRU`X~%oRMD;vCFYuH|z-lN9G#?}^`E+73e)BD{fgNeV}bqgdCS-H0vDe2x@^W*gO z_@nmqe<$v#{jPg>a=N^vUCp1q3oZOPcS{$0c}%z*9x}r{)@4R_|HL^;JPbds+Zq1+ SVt*PKMhu>=elF{r5}E*)wv3Yi literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/slider-skin.png b/dali-toolkit/images/slider-skin.png new file mode 100644 index 0000000000000000000000000000000000000000..b006bdd2bbd326d9dba4daef0c285e8e497e377d GIT binary patch literal 1718 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 TEK^|*00000NkvXXu0mjfh+Moe literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/text-input-selection-handle-left.png b/dali-toolkit/images/text-input-selection-handle-left.png new file mode 100755 index 0000000000000000000000000000000000000000..ba10440db42e0c48346fc19dd55f3cd06eb70e04 GIT binary patch literal 4637 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 TEK^|*00000NkvXXu0mjfh+Moe literal 0 HcmV?d00001 diff --git a/dali-toolkit/images/text-input-selection-handle-right-press.png b/dali-toolkit/images/text-input-selection-handle-right-press.png new file mode 100755 index 0000000000000000000000000000000000000000..3ab7a4306fc50a6e5819aada58441878b80d18ec GIT binary patch literal 4689 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 +#include +#include + +// INTERNAL INCLUDES +#include + +namespace // unnamed namespace +{ + +using namespace Dali; +using namespace Dali::Scripting; + +ColorMode GetColorMode( const std::string& value ) +{ + ColorMode v( USE_OWN_COLOR ); + + bool set = \ + SetIfEqual(value, "USE_OWN_COLOR" , v, USE_OWN_COLOR ) || \ + SetIfEqual(value, "USE_PARENT_COLOR" , v, USE_PARENT_COLOR ) || \ + SetIfEqual(value, "USE_OWN_MULTIPLY_PARENT_COLOR", v, USE_OWN_MULTIPLY_PARENT_COLOR); + + if( !set ) + { + DALI_ASSERT_ALWAYS( !"Unknown Color mode" ); + } + + return v; +} + +PositionInheritanceMode GetPositionInheritanceMode( const std::string& value ) +{ + PositionInheritanceMode v(INHERIT_PARENT_POSITION); + + bool set = \ + SetIfEqual(value, "INHERIT_PARENT_POSITION", v, INHERIT_PARENT_POSITION ) || \ + SetIfEqual(value, "USE_PARENT_POSITION" , v, USE_PARENT_POSITION ) || \ + SetIfEqual(value, "USE_PARENT_POSITION_PLUS_LOCAL_POSITION", v, USE_PARENT_POSITION_PLUS_LOCAL_POSITION ) || \ + SetIfEqual(value, "DONT_INHERIT_POSITION" , v, DONT_INHERIT_POSITION); + + if( !set ) + { + DALI_ASSERT_ALWAYS( !"Unknown position inheritance mode" ); + } + + return v; +} + +DrawMode::Type GetDrawMode( const std::string& value ) +{ + DrawMode::Type e(DrawMode::NORMAL); + + bool set = \ + SetIfEqual(value, "NORMAL", e, DrawMode::NORMAL ) || \ + SetIfEqual(value, "OVERLAY",e, DrawMode::OVERLAY ) || \ + SetIfEqual(value, "STENCIL",e, DrawMode::STENCIL); + + if(!set) + { + DALI_ASSERT_ALWAYS( !"Unknown DrawMode type" ); + } + + return e; +} + + +Vector3 GetAnchorConstant( const std::string& value ) +{ + Vector3 v = ParentOrigin::CENTER; // a Vector3x + + bool set = + SetIfEqual( value, "BACK_TOP_LEFT", v, ParentOrigin::BACK_TOP_LEFT ) || + SetIfEqual( value, "BACK_TOP_CENTER", v, ParentOrigin::BACK_TOP_CENTER ) || + SetIfEqual( value, "BACK_TOP_RIGHT", v, ParentOrigin::BACK_TOP_RIGHT ) || + SetIfEqual( value, "BACK_CENTER_LEFT", v, ParentOrigin::BACK_CENTER_LEFT ) || + SetIfEqual( value, "BACK_CENTER", v, ParentOrigin::BACK_CENTER ) || + SetIfEqual( value, "BACK_CENTER_RIGHT", v, ParentOrigin::BACK_CENTER_RIGHT ) || + SetIfEqual( value, "BACK_BOTTOM_LEFT", v, ParentOrigin::BACK_BOTTOM_LEFT ) || + SetIfEqual( value, "BACK_BOTTOM_CENTER", v, ParentOrigin::BACK_BOTTOM_CENTER ) || + SetIfEqual( value, "BACK_BOTTOM_RIGHT", v, ParentOrigin::BACK_BOTTOM_RIGHT ) || + SetIfEqual( value, "TOP_LEFT", v, ParentOrigin::TOP_LEFT ) || + SetIfEqual( value, "TOP_CENTER", v, ParentOrigin::TOP_CENTER ) || + SetIfEqual( value, "TOP_RIGHT", v, ParentOrigin::TOP_RIGHT ) || + SetIfEqual( value, "CENTER_LEFT", v, ParentOrigin::CENTER_LEFT ) || + SetIfEqual( value, "CENTER", v, ParentOrigin::CENTER ) || + SetIfEqual( value, "CENTER_RIGHT", v, ParentOrigin::CENTER_RIGHT ) || + SetIfEqual( value, "BOTTOM_LEFT", v, ParentOrigin::BOTTOM_LEFT ) || + SetIfEqual( value, "BOTTOM_CENTER", v, ParentOrigin::BOTTOM_CENTER ) || + SetIfEqual( value, "BOTTOM_RIGHT", v, ParentOrigin::BOTTOM_RIGHT ) || + SetIfEqual( value, "FRONT_TOP_LEFT", v, ParentOrigin::FRONT_TOP_LEFT ) || + SetIfEqual( value, "FRONT_TOP_CENTER", v, ParentOrigin::FRONT_TOP_CENTER ) || + SetIfEqual( value, "FRONT_TOP_RIGHT", v, ParentOrigin::FRONT_TOP_RIGHT ) || + SetIfEqual( value, "FRONT_CENTER_LEFT", v, ParentOrigin::FRONT_CENTER_LEFT ) || + SetIfEqual( value, "FRONT_CENTER", v, ParentOrigin::FRONT_CENTER ) || + SetIfEqual( value, "FRONT_CENTER_RIGHT", v, ParentOrigin::FRONT_CENTER_RIGHT ) || + SetIfEqual( value, "FRONT_BOTTOM_LEFT", v, ParentOrigin::FRONT_BOTTOM_LEFT ) || + SetIfEqual( value, "FRONT_BOTTOM_CENTER", v, ParentOrigin::FRONT_BOTTOM_CENTER) || + SetIfEqual( value, "FRONT_BOTTOM_RIGHT", v, ParentOrigin::FRONT_BOTTOM_RIGHT ); + + if(!set) + { + DALI_ASSERT_ALWAYS(!"Unknown Parent origin Constant" ); + } + + return v; +} + + +}; // unnamed namespace + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/* + * Handles special case actor configuration (anything thats not already a property) + * + */ +Actor SetupActor( const TreeNode& child, Actor& actor ) +{ + DALI_ASSERT_ALWAYS( actor && "Empty actor handle" ); + + // we allow enums strings for parent-origin and anchor-point but as with the current json + // strings always succeed if they exist then check its not vector. If they are Vec3s then + // this has already been set as a generic property. + if( !IsVector3( child, "parent-origin") ) + { + if( OptionalVector3 v = IsVector3(child, "parent-origin") ) + { + actor.SetParentOrigin( *v ); + } + else if( OptionalString origin = IsString(child, "parent-origin") ) + { + actor.SetParentOrigin( GetAnchorConstant(*origin) ); + } + } + + if( !IsVector3(child, "anchor-point") ) + { + if( OptionalVector3 v = IsVector3(child, "anchor-point") ) + { + actor.SetParentOrigin( *v ); + } + else if( OptionalString anchor = IsString(child, "anchor-point") ) + { + actor.SetAnchorPoint( GetAnchorConstant(*anchor) ); + } + } + + if( OptionalFloat opacity = IsFloat(child, "opacity") ) + { + actor.SetOpacity( *opacity ); + } + + if( OptionalFloat opacity = IsFloat(child, "opacity-by") ) + { + actor.OpacityBy( *opacity ); + } + + if( OptionalString colorMode = IsString(child, "color-mode") ) + { + actor.SetColorMode( GetColorMode(*colorMode) ); + } + + if( OptionalBoolean inherit = IsBoolean(child, "inherit-shader-effect") ) + { + actor.SetInheritShaderEffect( *inherit ); + } + + if( OptionalBoolean sensitive = IsBoolean(child, "sensitive") ) + { + actor.SetSensitive( *sensitive ); + } + + if( OptionalBoolean leaveRequired = IsBoolean(child, "leave-required") ) + { + actor.SetLeaveRequired( *leaveRequired ); + } + + if( OptionalString v = IsString(child, "position-inheritance") ) + { + actor.SetPositionInheritanceMode( GetPositionInheritanceMode(*v) ); + } + + if( OptionalString v = IsString(child, "draw-mode") ) + { + actor.SetDrawMode( GetDrawMode(*v) ); + } + + if( OptionalBoolean v = IsBoolean(child, "inherit-rotation") ) + { + actor.SetInheritRotation( *v ); + } + + if( OptionalBoolean v = IsBoolean(child, "inherit-scale") ) + { + actor.SetInheritScale( *v ); + } + + return actor; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/builder-animations.cpp b/dali-toolkit/internal/builder/builder-animations.cpp new file mode 100644 index 0000000..b8ea43e --- /dev/null +++ b/dali-toolkit/internal/builder/builder-animations.cpp @@ -0,0 +1,336 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace // unnamed namespace +{ + +using namespace Dali; + +TimePeriod GetTimePeriod( const TreeNode& child ) +{ + OptionalFloat delay = IsFloat( IsChild(child, "delay" ) ); + OptionalFloat duration = IsFloat( IsChild(child, "duration" ) ); + DALI_ASSERT_ALWAYS( duration && "Time period must have at least a duration" ); + + if( delay ) + { + return TimePeriod( *delay, *duration ); + } + else + { + return TimePeriod( *duration ); + } +} + +Property::Value GetPropertyValue( const Property::Type& propType, const TreeNode& child ) +{ + switch ( propType ) + { + case Property::BOOLEAN: + { + return Property::Value( GetBoolean( child ) ); + } + + case Property::FLOAT: + { + return Property::Value( GetFloat( child ) ); + } + + case Property::VECTOR2: + { + return Property::Value( GetVector2( child ) ); + } + + case Property::VECTOR3: + { + return Property::Value( GetVector3( child ) ); + } + + case Property::VECTOR4: + { + return Property::Value( GetVector4( child ) ); + } + + case Property::ROTATION: + { + if( 4 == child.Size() ) + { + Vector4 v(GetVector4(child)); + // angle, axis as per spec + return Property::Value( Quaternion(Radian(Degree(v[3])), + Vector3(v[0],v[1],v[2])) ); + } + else + { + // degrees as per spec + Vector3 rotation = GetVector3( child ); + return Property::Value( Quaternion(Radian(Degree(rotation.x)), + Radian(Degree(rotation.y)), + Radian(Degree(rotation.z))) ); + } + } + + case Property::NONE: // fall + default: + { + DALI_ASSERT_ALWAYS( !"Property type incorrect" ); + return Property::Value(); + } + } +} + +AlphaFunction GetAlphaFunction( const std::string& alphaFunction ) +{ + typedef std::map< const std::string, Dali::AlphaFunction > AlphaFunctionLut; + static AlphaFunctionLut alphaFunctionLut; + + if( 0 == alphaFunctionLut.size() ) + { + // coding convention is uppercase enums + alphaFunctionLut["DEFAULT"] = Dali::AlphaFunctions::Default; + alphaFunctionLut["LINEAR"] = Dali::AlphaFunctions::Linear; + alphaFunctionLut["REVERSE"] = Dali::AlphaFunctions::Reverse; + alphaFunctionLut["EASE_IN"] = Dali::AlphaFunctions::EaseIn; + alphaFunctionLut["EASE_OUT"] = Dali::AlphaFunctions::EaseOut; + alphaFunctionLut["EASE_IN_OUT"] = Dali::AlphaFunctions::EaseInOut; + alphaFunctionLut["EASE_IN_SINE"] = Dali::AlphaFunctions::EaseInSine; + alphaFunctionLut["EASE_OUT_SINE"] = Dali::AlphaFunctions::EaseOutSine; + alphaFunctionLut["EASE_IN_OUT_SINE"]= Dali::AlphaFunctions::EaseInOutSine; + alphaFunctionLut["BOUNCE"] = Dali::AlphaFunctions::Bounce; + alphaFunctionLut["BOUNCE_BACK"] = Dali::AlphaFunctions::BounceBack; + alphaFunctionLut["EASE_OUT_BACK"] = Dali::AlphaFunctions::EaseOutBack; + alphaFunctionLut["SIN"] = Dali::AlphaFunctions::Sin; + alphaFunctionLut["SIN2X"] = Dali::AlphaFunctions::Sin; + } + + const AlphaFunctionLut::const_iterator iter( alphaFunctionLut.find( alphaFunction ) ); + + if( iter != alphaFunctionLut.end() ) + { + return iter->second; + } + else + { + DALI_ASSERT_ALWAYS( iter != alphaFunctionLut.end() && "Unknown Anchor Constant" ); + return Dali::AlphaFunctions::Default; + } +} + +} // unnamed namespace + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +Animation CreateAnimation( const TreeNode& child ) +{ + float durationSum = 0.f; + + Animation animation( Animation::New( 0.f ) ); + + if( OptionalBoolean looping = IsBoolean( IsChild(child, "loop" ) ) ) + { + animation.SetLooping( *looping ); + } + + if( OptionalString endAction = IsString( IsChild(child, "end-action" ) ) ) + { + if("BAKE" == *endAction) + { + animation.SetEndAction( Animation::Bake ); + } + else if("DISCARD" == *endAction) + { + animation.SetEndAction( Animation::Discard ); + } + } + + if( OptionalString endAction = IsString( IsChild(child, "destroy-action" ) ) ) + { + if("BAKE" == *endAction) + { + animation.SetDestroyAction( Animation::Bake ); + } + else if("DISCARD" == *endAction) + { + animation.SetDestroyAction( Animation::Discard ); + } + } + + OptionalChild propertiesNode = IsChild(child, "properties" ); + if(propertiesNode) + { + const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd(); + for( TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter ) + { + const TreeNode::KeyNodePair& pKeyChild = *iter; + + OptionalString actorName( IsString( pKeyChild.second, "actor" ) ); + OptionalString property( IsString( pKeyChild.second, "property" ) ); + DALI_ASSERT_ALWAYS( actorName && "Animation must specify actor name" ); + DALI_ASSERT_ALWAYS( property && "Animation must specify a property name" ); + + Actor targetActor = Stage::GetCurrent().GetRootLayer().FindChildByName( *actorName ); + DALI_ASSERT_ALWAYS( targetActor && "Actor must exist for property" ); + + Property::Index idx( targetActor.GetPropertyIndex( *property ) ); + + // A limitation here is that its possible that between binding to the signal and + // the signal call that the ShaderEffect of the targetActor has been changed. + // However this is a unlikely use case especially when using scripts. + if( idx == Property::INVALID_INDEX ) + { + if( ShaderEffect effect = targetActor.GetShaderEffect() ) + { + idx = effect.GetPropertyIndex( *property ); + if(idx != Property::INVALID_INDEX) + { + targetActor = effect; + } + else + { + DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" ); + continue; + } + } + else + { + DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" ); + continue; + } + } + + if( idx == Property::INVALID_INDEX) + { + DALI_ASSERT_ALWAYS( idx != Property::INVALID_INDEX && "Animation must specify a valid property" ); + continue; + } + + Property prop( Property( targetActor, idx ) ); + Property::Value propValue; + + // these are the defaults + AlphaFunction alphaFunction( AlphaFunctions::Default ); + TimePeriod timePeriod( 0.f ); + + if( OptionalChild timeChild = IsChild( pKeyChild.second, "time-period" ) ) + { + timePeriod = GetTimePeriod( *timeChild ); + } + + durationSum = std::max( durationSum, timePeriod.delaySeconds + timePeriod.durationSeconds ); + + if( OptionalString alphaChild = IsString( pKeyChild.second, "alpha-function" ) ) + { + alphaFunction = GetAlphaFunction( *alphaChild ); + } + + if( OptionalChild keyFrameChild = IsChild(pKeyChild.second, "key-frames") ) + { + KeyFrames keyframes = KeyFrames::New(); + + const TreeNode::ConstIterator endIter = (*keyFrameChild).CEnd(); + for( TreeNode::ConstIterator iter = (*keyFrameChild).CBegin(); endIter != iter; ++iter ) + { + const TreeNode::KeyNodePair& kfKeyChild = *iter; + + OptionalFloat kfProgress = IsFloat( kfKeyChild.second, "progress" ); + DALI_ASSERT_ALWAYS( kfProgress && "Key frame entry must have 'progress'" ); + + OptionalChild kfValue = IsChild( kfKeyChild.second, "value" ); + DALI_ASSERT_ALWAYS( kfValue && "Key frame entry must have 'value'" ); + + try + { + propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *kfValue ); + } + catch(...) + { + DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n", + (*property).c_str(), + PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)) ); + + throw; + } + + AlphaFunction kfAlphaFunction( AlphaFunctions::Default ); + if( OptionalString alphaFuncStr = IsString( pKeyChild.second, "alpha-function") ) + { + kfAlphaFunction = GetAlphaFunction( *alphaFuncStr ); + } + + keyframes.Add( *kfProgress, propValue, kfAlphaFunction ); + } + + animation.AnimateBetween( prop, keyframes, alphaFunction, timePeriod ); + } + else + { + try + { + propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *IsChild(pKeyChild.second, "value") ); + } + catch(...) + { + DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n", (*property).c_str(), + PropertyTypes::GetName( prop.object.GetPropertyType(prop.propertyIndex) ) ); + + throw; + } + + if( OptionalBoolean relative = IsBoolean(pKeyChild.second, "relative") ) + { + animation.AnimateBy( prop, propValue, alphaFunction, timePeriod ); + } + else + { + animation.AnimateTo( prop, propValue, alphaFunction, timePeriod ); + } + } + } + } + + if( OptionalFloat duration = IsFloat( child, "duration" ) ) + { + animation.SetDuration( *duration ); + } + else + { + animation.SetDuration( durationSum ); + } + + return animation; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/internal/builder/builder-control.cpp b/dali-toolkit/internal/builder/builder-control.cpp new file mode 100644 index 0000000..005fde9 --- /dev/null +++ b/dali-toolkit/internal/builder/builder-control.cpp @@ -0,0 +1,112 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace // unnamed namespace +{ + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace Dali::Scripting; // SetIfEqual + +Control::SizePolicy GetSizePolicy( const std::string& value ) +{ + Control::SizePolicy v(Control::Flexible); + + bool set = \ + SetIfEqual(value, "FIXED", v, Control::Fixed ) || \ + SetIfEqual(value, "MINIMUM", v, Control::Minimum ) || \ + SetIfEqual(value, "MAXIMUM", v, Control::Maximum ) || \ + SetIfEqual(value, "RANGE", v, Control::Range ) || \ + SetIfEqual(value, "FLEXIBLE", v, Control::Flexible ); + + if( !set ) + { + DALI_ASSERT_ALWAYS( !"Unknown Color mode" ); + } + + return v; +} + +} // anon namespace + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/* + * Handles special case control configuration (anything thats not already a property) + * + */ +Dali::Toolkit::Control SetupControl( const TreeNode& child, Dali::Toolkit::Control& control ) +{ + DALI_ASSERT_ALWAYS( control && "Empty actor handle" ); + + Control::SizePolicy widthPolicy(Control::Flexible); + Control::SizePolicy heightPolicy(Control::Flexible); + + if( OptionalString v = IsString(child, "width-policy") ) + { + widthPolicy = GetSizePolicy(*v); + control.SetSizePolicy( widthPolicy, heightPolicy ); + } + + if( OptionalString v = IsString(child, "height-policy") ) + { + heightPolicy = GetSizePolicy(*v); + control.SetSizePolicy( widthPolicy, heightPolicy ); + } + + if( OptionalVector3 v = IsVector3(child, "minimum-size") ) + { + control.SetMinimumSize( *v ); + } + + if( OptionalVector3 v = IsVector3(child, "maximum-size") ) + { + control.SetMaximumSize( *v ); + } + + if( OptionalBoolean v = IsBoolean(child, "key-input-focus") ) + { + if( *v ) + { + control.SetKeyInputFocus(); + } + } + + return control; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/builder-declarations.h b/dali-toolkit/internal/builder/builder-declarations.h new file mode 100644 index 0000000..b0e28a2 --- /dev/null +++ b/dali-toolkit/internal/builder/builder-declarations.h @@ -0,0 +1,43 @@ +#ifndef __DALI_TOOLKIT_BUILDER_DECLARATIONS_H__ +#define __DALI_TOOLKIT_BUILDER_DECLARATIONS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +typedef Dali::Toolkit::TreeNode TreeNode; +typedef TreeNode::ConstIterator TreeConstIter; + +typedef OptionalValue OptionalChild; +typedef OptionalValue OptionalString; +typedef OptionalValue OptionalFloat; +typedef OptionalValue OptionalInteger; +typedef OptionalValue OptionalUnsignedInt; +typedef OptionalValue OptionalBoolean; +typedef OptionalValue OptionalVector2; +typedef OptionalValue OptionalVector3; +typedef OptionalValue OptionalVector4; +typedef OptionalValue OptionalString; +typedef OptionalValue OptionalMatrix; +typedef OptionalValue OptionalMatrix3; +typedef OptionalValue > OptionalRect; + +#endif // __DALI_TOOLKIT_BUILDER_DECLARATIONS_H__ diff --git a/dali-toolkit/internal/builder/builder-filesystem.h b/dali-toolkit/internal/builder/builder-filesystem.h new file mode 100644 index 0000000..b3fa958 --- /dev/null +++ b/dali-toolkit/internal/builder/builder-filesystem.h @@ -0,0 +1,48 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUILDER_FILESYSTEM_H__ +#define __DALI_TOOLKIT_INTERNAL_BUILDER_FILESYSTEM_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +inline std::string ExpandPath(const std::string &name) +{ + wordexp_t p; + char** w; + wordexp( name.c_str(), &p, 0 ); + w = p.we_wordv; + std::stringstream s; + for (size_t i=0; i + +inline OptionalChild IsChild(const TreeNode* node, const std::string& childName) +{ + if( node ) + { + const TreeNode* c = node->GetChild(childName); + if( NULL != c ) + { + return OptionalChild( *c ); + } + else + { + return OptionalChild(); + } + } + else + { + return OptionalChild(); + } +} + +inline OptionalChild IsChild(const TreeNode& node, const std::string& childName) +{ + return IsChild(&node, childName); +} + +inline OptionalString IsString(const OptionalChild& node) +{ + if( node && (*node).GetType() == TreeNode::STRING ) + { + return OptionalString((*node).GetString()); + } + else + { + return OptionalString(); + } +} +inline OptionalFloat IsFloat(const OptionalChild& node) +{ + OptionalFloat ret; + + if( node ) + { + if( (*node).GetType() == TreeNode::FLOAT ) + { + ret = (*node).GetFloat(); + } + else if( (*node).GetType() == TreeNode::INTEGER ) + { + // JSON has number not float/int but JsonParser discriminates. + // Here we don't care so we allow coercion + ret = static_cast( (*node).GetInteger() ); + } + } + + return ret; +} + +inline OptionalInteger IsInteger(const OptionalChild &node) +{ + OptionalInteger ret; + + if( node ) + { + if( (*node).GetType() == TreeNode::INTEGER ) + { + ret = (*node).GetInteger(); + } + else if( (*node).GetType() == TreeNode::FLOAT ) + { + ret = static_cast( (*node).GetFloat() ); + } + } + + return ret; +} + +inline OptionalBoolean IsBoolean(const OptionalChild& node) +{ + if( node && (*node).GetType() == TreeNode::BOOLEAN ) + { + return OptionalBoolean(1 == (*node).GetInteger()); + } + else + { + return OptionalBoolean(); + } +} + +// copy N Numbers +template +inline bool CopyNumbers(TreeNode::ConstIterator iter, int N, T& vector) +{ + for(int i = 0; i < N; ++i) + { + if( (*iter).second.GetType() == TreeNode::FLOAT) + { + vector[i] = (*iter).second.GetFloat(); + } + else if( (*iter).second.GetType() == TreeNode::INTEGER ) + { + vector[i] = static_cast((*iter).second.GetInteger()); + } + else + { + return false; + } + iter++; + } + + return true; +} + +inline OptionalVector4 IsVector4(const OptionalChild& node) +{ + OptionalVector4 ret; + + if( node && (*node).Size() >= 4 ) + { + Dali::Vector4 v; + if( CopyNumbers((*node).CBegin(), 4, v) ) + { + ret = OptionalVector4(v); + } + } + + return ret; +} + +inline OptionalVector3 IsVector3(const OptionalChild& node) +{ + OptionalVector3 ret; + + if( node && (*node).Size() >= 3 ) + { + Dali::Vector3 v; + if( CopyNumbers((*node).CBegin(), 3, v) ) + { + ret = OptionalVector3(v); + } + } + + return ret; +} + +inline OptionalVector2 IsVector2(const OptionalChild& node) +{ + OptionalVector2 ret; + + if( node && (*node).Size() >= 2 ) + { + Dali::Vector2 v; + if( CopyNumbers((*node).CBegin(), 2, v) ) + { + ret = OptionalVector2(v); + } + } + + return ret; +} + +inline OptionalMatrix IsMatrix(const OptionalChild &node) +{ + OptionalMatrix ret; + + if( node && (*node).Size() >= 16 ) + { + float v[16]; + if( CopyNumbers((*node).CBegin(), 16, v) ) + { + ret = OptionalMatrix(Dali::Matrix(v)); + } + } + + return ret; +} + +inline OptionalMatrix3 IsMatrix3(const OptionalChild& node) +{ + OptionalMatrix3 ret; + + if( node && (*node).Size() >= 9 ) + { + float v[9]; + if( CopyNumbers((*node).CBegin(), 9, v) ) + { + ret = OptionalMatrix3(Dali::Matrix3(v[0], v[1], v[2], + v[3], v[4], v[5], + v[6], v[7], v[8] )); + } + } + + return ret; +} + +inline OptionalRect IsRect(const OptionalChild& node) +{ + OptionalRect ret; + if(node && (*node).Size()) + { + if((*node).Size() >= 4) + { + TreeNode::ConstIterator iter((*node).CBegin()); + int v[4]; + if( CopyNumbers((*node).CBegin(), 4, v) ) + { + ret = OptionalRect(Dali::Rect(v[0], v[1], v[2], v[3])); + } + } + } + return ret; +} + +// +// +// +inline OptionalString IsString( const TreeNode& parent, const std::string& childName) +{ + return IsString( IsChild(&parent, childName) ); +} + +inline OptionalFloat IsFloat( const TreeNode& parent, const std::string& childName) +{ + return IsFloat( IsChild(&parent, childName) ); +} + +inline OptionalInteger IsInteger( const TreeNode& parent, const std::string& childName) +{ + return IsInteger( IsChild(&parent, childName) ); +} + +inline OptionalBoolean IsBoolean( const TreeNode& parent, const std::string& childName) +{ + return IsBoolean( IsChild(parent, childName) ); +} + +inline OptionalVector4 IsVector4(const TreeNode &parent, const std::string& childName) +{ + return IsVector4( IsChild(parent, childName) ); +} + +inline OptionalVector3 IsVector3(const TreeNode &parent, const std::string& childName) +{ + return IsVector3( IsChild(parent, childName) ); +} + +inline OptionalVector2 IsVector2(const TreeNode &parent, const std::string& childName) +{ + return IsVector2( IsChild(parent, childName) ); +} + +inline OptionalMatrix IsMatrix(const TreeNode &parent, const std::string& childName) +{ + return IsMatrix( IsChild(parent, childName) ); +} + +inline OptionalMatrix3 IsMatrix3(const TreeNode &parent, const std::string& childName) +{ + return IsMatrix3( IsChild(&parent, childName) ); +} + +inline OptionalRect IsRect(const TreeNode &parent, const std::string& childName) +{ + return IsRect( IsChild(&parent, childName) ); +} + +// +// +// +inline OptionalString IsString( const TreeNode& node ) +{ + return IsString( OptionalChild( node ) ); +} + +inline OptionalFloat IsFloat( const TreeNode& node ) +{ + return IsFloat( OptionalChild( node ) ); +} + +inline OptionalInteger IsInteger( const TreeNode& node ) +{ + return IsInteger( OptionalChild( node ) ); +} + +inline OptionalBoolean IsBoolean( const TreeNode& node ) +{ + return IsBoolean( OptionalChild( node ) ); +} + +inline OptionalVector4 IsVector4(const TreeNode &node ) +{ + return IsVector4( OptionalChild( node ) ); +} + +inline OptionalVector3 IsVector3(const TreeNode &node ) +{ + return IsVector3( OptionalChild( node ) ); +} + +inline OptionalVector2 IsVector2(const TreeNode &node ) +{ + return IsVector2( OptionalChild( node ) ); +} + +inline OptionalMatrix IsMatrix(const TreeNode &node ) +{ + return IsMatrix( OptionalChild( node ) ); +} + +inline OptionalMatrix3 IsMatrix3(const TreeNode &node ) +{ + return IsMatrix3( OptionalChild( node ) ); +} + +inline OptionalRect IsRect(const TreeNode &node ) +{ + return IsRect( OptionalChild( node ) ); +} + +// +// +// +inline Dali::Vector4 GetVector4(const TreeNode &child) +{ + OptionalVector4 v( IsVector4( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + +inline Dali::Vector3 GetVector3(const TreeNode &child) +{ + OptionalVector3 v( IsVector3( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + +inline Dali::Vector2 GetVector2(const TreeNode &child) +{ + OptionalVector2 v( IsVector2( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + +inline float GetFloat(const TreeNode &child) +{ + OptionalFloat v( IsFloat( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + +inline bool GetBoolean(const TreeNode &child) +{ + OptionalBoolean v( IsBoolean( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + +inline int GetInteger(const TreeNode &child) +{ + OptionalInteger v( IsInteger( OptionalChild( child ) ) ); + DALI_ASSERT_ALWAYS(v); + return *v; +} + + + +#endif // __DALI_TOOLKIT_INTERNAL_BUILDER_GET_IS_INL__ diff --git a/dali-toolkit/internal/builder/builder-impl.cpp b/dali-toolkit/internal/builder/builder-impl.cpp new file mode 100755 index 0000000..83572ec --- /dev/null +++ b/dali-toolkit/internal/builder/builder-impl.cpp @@ -0,0 +1,693 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +#include + +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +extern Animation CreateAnimation(const TreeNode& child); +extern bool SetPropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value ); +extern Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor); +extern Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor); +extern Actor SetupActor( const TreeNode& node, Actor& actor ); +extern Control SetupControl( const TreeNode& node, Control& actor ); + +#if defined(DEBUG_ENABLED) +Integration::Log::Filter* gFilterScript = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_SCRIPT"); +#endif + +namespace +{ + +typedef std::vector TreeNodeList; + +/* + * Sets the handle properties found in the tree node + */ +void SetProperties( const TreeNode& node, Handle& handle, Builder& builder ) +{ + if( handle ) + { + for( TreeNode::ConstIterator iter = node.CBegin(); iter != node.CEnd(); ++iter ) + { + const TreeNode::KeyNodePair& keyChild = *iter; + + std::string key( keyChild.first ); + + // ignore special fields; type,actors,signals + if(key == "type" || key == "actors" || key == "signals") + { + continue; + } + + // special field 'image' usually contains an json object description + // although sometimes refers to a framebuffer + if( 0 == keyChild.second.Size() ) + { + if(key == "image") + { + ImageActor imageActor = ImageActor::DownCast(handle); + if(imageActor) + { + if( OptionalString s = IsString( keyChild.second ) ) + { + FrameBufferImage fb = builder.GetFrameBufferImage(*s); + if(fb) + { + imageActor.SetImage( fb ); + } + } + } + } + } + + // special field 'effect' references the shader effect instances + if(key == "effect") + { + Actor actor = Actor::DownCast(handle); + OptionalString s = IsString( keyChild.second ); + if(actor && s) + { + ShaderEffect e = builder.GetShaderEffect(*s); + actor.SetShaderEffect(e); + } + else + { + DALI_SCRIPT_WARNING("Could not find or set shader effect\n"); + } + + continue; + } + + Property::Index index = handle.GetPropertyIndex(key); + + if( Property::INVALID_INDEX != index ) + { + Property::Type type = handle.GetPropertyType(index); + + Property::Value value; + if( !SetPropertyFromNode( keyChild.second, type, value ) ) + { + // verbose as this might not be a problem + // eg parent-origin can be a string which is picked up later + DALI_SCRIPT_VERBOSE("Could not convert property:%s\n", key.c_str()); + } + else + { + DALI_SCRIPT_VERBOSE("SetProperty '%s' Index=:%d Value Type=%d\n", key.c_str(), index, value.GetType()); + + handle.SetProperty( index, value ); + } + } + else + { + DALI_SCRIPT_VERBOSE("SetProperty INVALID '%s' Index=:%d\n", key.c_str(), index); + } + + } // for property nodes + } + else + { + DALI_SCRIPT_WARNING("Style applied to empty handle\n"); + } +} + +/* + * Split a string by delimiter + */ +std::vector SplitString(const std::string &s, char delim, std::vector &elems) +{ + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) + { + elems.push_back(item); + } + return elems; +} + +/* + * Recursively collects all styles listed in a json node 'type' field. (Could be a comma separated list). + * It also returns the first found base typeInfo from the TypeRegistry. This is the concrete object to instance. + * i.e. the type field can name a json style section or a TypeRegistry base type. + */ +void CollectAllStyles( const TreeNode& styles, const std::string& typeStyleString, TreeNodeList& additionalStyles, TypeInfo& typeInfo ) +{ + typedef std::vector StyleNames; + StyleNames styleNames; + + SplitString(typeStyleString, ',', styleNames); + + for(StyleNames::iterator iter = styleNames.begin(); iter != styleNames.end(); ++iter) + { + const std::string typeName(*iter); + + OptionalChild style = IsChild(styles, typeName); + if(style) + { + additionalStyles.push_back(&(*style)); + + OptionalString styleType = IsString( IsChild(*style, "type") ); + if( styleType ) + { + CollectAllStyles(styles, *styleType, additionalStyles, typeInfo); + } + else + { + if(!typeInfo) + { + // if its not a style then check for a type but only the first found + typeInfo = TypeRegistry::Get().GetTypeInfo( typeName ); + } + } + } + else + { + DALI_ASSERT_DEBUG(!typeInfo); + typeInfo = TypeRegistry::Get().GetTypeInfo( typeName ); + } + } +} + +/* + * Recursively collects all styles listed in a json node 'type' field. (Could be a comma separated list). + * Adds the given node to the end of the additional styles list. + * It also returns the first found base typeInfo from the TypeRegistry. This is the concrete object to instance. + * i.e. the type field can name a json style section or a TypeRegistry base type. + */ +void CollectAllStyles( const OptionalChild& optionalStyles, const TreeNode& node, TreeNodeList& additionalStyles, TypeInfo& typeInfo ) +{ + OptionalString type = IsString( IsChild(node, "type") ); + + if(!type) + { + DALI_SCRIPT_WARNING("Cannot create style as type section is missing\n"); + } + else + { + additionalStyles.push_back(&node); + + if( optionalStyles ) + { + CollectAllStyles( *optionalStyles, *type, additionalStyles, typeInfo ); + } + else + { + typeInfo = TypeRegistry::Get().GetTypeInfo( *type ); + } + + } +} + +/* + * Create a dali type from a node. + * If parent given and an actor type was created then add it to the parent and + * recursively add nodes children. + */ +BaseHandle Create( ConnectionTracker* tracker, const OptionalChild& optionalStyles, const TreeNode& node, const TreeNode& root, Actor parent, Builder& builder ) +{ + BaseHandle baseHandle; + TreeNodeList allStyles; + TypeInfo typeInfo; + + CollectAllStyles( optionalStyles, node, allStyles, typeInfo ); + + if(!typeInfo) + { + DALI_SCRIPT_WARNING("Unable to create Dali type from node\n"); + } + else + { + baseHandle = typeInfo.CreateInstance(); + Handle handle = Handle::DownCast(baseHandle); + Actor actor = Actor::DownCast(handle); + Control control = Control::DownCast(handle); + + if(handle) + { + + DALI_SCRIPT_VERBOSE("Create:%s\n", typeInfo.GetName().c_str()); + DALI_SCRIPT_VERBOSE(" %d style sections\n", allStyles.size()); + +#if defined(DEBUG_ENABLED) + if(handle) + { + DALI_SCRIPT_VERBOSE(" Is Handle Object=%d\n", (long*)handle.GetObjectPtr()); + DALI_SCRIPT_VERBOSE(" Is Handle Property Count=%d\n", handle.GetPropertyCount()); + } + + if(actor) + { + DALI_SCRIPT_VERBOSE(" Is Actor id=%d\n", actor.GetId()); + } + + if(control) + { + DALI_SCRIPT_VERBOSE(" Is Control id=%d\n", actor.GetId()); + } +#endif // DEBUG_ENABLED + + for(TreeNodeList::reverse_iterator iter = allStyles.rbegin(); iter != allStyles.rend(); ++iter) + { + SetProperties( *(*iter), handle, builder ); + + if( actor ) // if we created an actor + { + SetupActor( *(*iter), actor); + + if( control ) + { + SetupControl( *(*iter), control); + } + + // add children of all the styles + if( OptionalChild actors = IsChild( *(*iter), "actors" ) ) + { + for( TreeConstIter iter = (*actors).CBegin(); iter != (*actors).CEnd(); ++iter ) + { + Create( tracker, optionalStyles, (*iter).second, root, actor, builder ); + } + } + } + } + + if( actor ) + { + // add signals first + SetupSignalAction( tracker, root, node, actor ); + SetupPropertyNotification( tracker, root, node, actor ); + + // then add to parent + if( parent ) + { + parent.Add( actor ); + } + + } + } + else + { + DALI_SCRIPT_WARNING("Cannot create handle from type '%s'\n", typeInfo.GetName().c_str()); + } + } + + return baseHandle; +} + + +} // namespace anon + + +ActorContainer Builder::GetTopLevelActors() const +{ + // deprecated function. + return ActorContainer(); +} + +Animation Builder::GetAnimation( const std::string &name ) const +{ + // deprecated + return Animation(); +} + +void Builder::SetupTask( RenderTask& task, const TreeNode& node ) +{ + const Stage& stage = Stage::GetCurrent(); + Layer root = stage.GetRootLayer(); + + if( OptionalString s = IsString( IsChild(node, "source-actor") ) ) + { + Actor actor = root.FindChildByName(*s); + if(actor) + { + task.SetSourceActor( actor ); + } + else + { + DALI_SCRIPT_WARNING("Cannot find source actor on stage for render task called '%s'\n", (*s).c_str() ); + } + } + + if( OptionalString s = IsString( IsChild(node, "camera-actor") ) ) + { + CameraActor actor = CameraActor::DownCast( root.FindChildByName(*s) ); + if(actor) + { + task.SetCameraActor( actor ); + } + else + { + DALI_SCRIPT_WARNING("Cannot find camera actor on stage for render task called '%s'\n", (*s).c_str() ); + } + } + + if( OptionalString s = IsString( IsChild(node, "target-frame-buffer") ) ) + { + FrameBufferImage fb = GetFrameBufferImage( *s ); + if(fb) + { + task.SetTargetFrameBuffer( fb ); + } + else + { + DALI_SCRIPT_WARNING("Cannot find target frame buffer '%s'\n", (*s).c_str() ); + } + } + + if( OptionalString s = IsString( IsChild(node, "screen-to-frame-buffer-function") ) ) + { + if("DEFAULT_SCREEN_TO_FRAMEBUFFER_FUNCTION" == *s) + { + task.SetScreenToFrameBufferFunction( RenderTask::DEFAULT_SCREEN_TO_FRAMEBUFFER_FUNCTION ); + } + else if("FULLSCREEN_FRAMEBUFFER_FUNCTION" == *s) + { + task.SetScreenToFrameBufferFunction( RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION ); + } + else + { + DALI_SCRIPT_WARNING("todo"); + } + } + + // other setup is via the property system + SetProperties( node, task, *this ); // @ todo, remove 'source-actor', 'camera-actor'? + +} + +void Builder::CreateRenderTask( const std::string &name ) +{ + const Stage& stage = Stage::GetCurrent(); + + OptionalChild tasks = IsChild(*mParser.GetRoot(), "render-tasks"); + + if(tasks) + { + // + // Create the tasks from the current task as generally we want + // to setup task zero and onwards. Although this does overwrite + // the properties of the current task. + // + if( OptionalChild renderTask = IsChild(*tasks, name ) ) + { + RenderTaskList list = stage.GetRenderTaskList(); + unsigned int start = list.GetTaskCount(); + + RenderTask task; + if(0 == start) + { + // zero should have already been created by the stage so really + // this case should never happen + task = list.CreateTask(); + start++; + } + + TreeNode::ConstIterator iter = (*renderTask).CBegin(); + task = list.GetTask( start - 1 ); + + SetupTask( task, (*iter).second ); + + ++iter; + + for(; iter != (*renderTask).CEnd(); ++iter ) + { + task = list.CreateTask(); + SetupTask( task, (*iter).second ); + } + } + } +} + +ShaderEffect Builder::GetShaderEffect( const std::string &name ) +{ + ShaderEffect ret; + + ShaderEffectLut::const_iterator iter( mShaderEffectLut.find( name ) ); + if( iter != mShaderEffectLut.end() ) + { + ret = iter->second; + } + else + { + if( OptionalChild effects = IsChild( *mParser.GetRoot(), "shader-effects") ) + { + if( OptionalChild effect = IsChild( *effects, name ) ) + { + Dali::Property::Value propertyMap(Property::MAP); + if( SetPropertyFromNode( *effect, Property::MAP, propertyMap ) ) + { + ret = Dali::Scripting::NewShaderEffect( propertyMap ); + mShaderEffectLut[ name ] = ret; + } + } + } + } + + return ret; +} + +FrameBufferImage Builder::GetFrameBufferImage( const std::string &name ) +{ + FrameBufferImage ret; + + ImageLut::const_iterator iter( mFrameBufferImageLut.find( name ) ); + if( iter != mFrameBufferImageLut.end() ) + { + ret = iter->second; + } + else + { + if( OptionalChild images = IsChild( *mParser.GetRoot(), "frame-buffer-images") ) + { + if( OptionalChild image = IsChild( *images, name ) ) + { + Dali::Property::Value propertyMap(Property::MAP); + if( SetPropertyFromNode( *image, Property::MAP, propertyMap ) ) + { + propertyMap.SetValue("type", Property::Value(std::string("FrameBufferImage"))); + ret = Dali::Scripting::NewImage( propertyMap ); + mFrameBufferImageLut[ name ] = ret; + } + } + } + } + + return ret; +} + +Font Builder::GetFont( const std::string& name ) const +{ + // deprecated function. + Font font; + return font; +} + +TextStyle Builder::GetTextStyle( const std::string& name ) const +{ + // deprecated + return TextStyle(); +} + +Image Builder::GetImage( const std::string& name) const +{ + // deprecated function. + return Image(); +} + +Actor Builder::GetActor( const std::string &name ) const +{ + // deprecated function. + return Actor(); +} + +void Builder::AddActors( Actor toActor ) +{ + // 'stage' is the default/by convention section to add from + AddActors( "stage", toActor ); +} + +void Builder::AddActors( const std::string §ionName, Actor toActor ) +{ + OptionalChild addToStage = IsChild(*mParser.GetRoot(), sectionName); + + if( addToStage ) + { + OptionalChild styles = IsChild(*mParser.GetRoot(), "styles"); + + for( TreeNode::ConstIterator iter = (*addToStage).CBegin(); iter != (*addToStage).CEnd(); ++iter ) + { + // empty actor adds directly to the stage + BaseHandle baseHandle = Create( mSlotDelegate.GetConnectionTracker(), styles, (*iter).second, *mParser.GetRoot(), Actor(), *this); + Actor actor = Actor::DownCast(baseHandle); + if(actor) + { + toActor.Add( actor ); + } + } + + // if were adding the 'stage' section then also check for a render task called stage + // to add automatically + if( "stage" == sectionName ) + { + if( OptionalChild renderTasks = IsChild(*mParser.GetRoot(), "render-tasks") ) + { + if( OptionalChild tasks = IsChild(*renderTasks, "stage") ) + { + CreateRenderTask( "stage" ); + } + } + } + + } +} + +Animation Builder::CreateAnimation( const std::string& animationName ) +{ + Animation anim; + + if( OptionalChild animations = IsChild(*mParser.GetRoot(), "animations") ) + { + if( OptionalChild animation = IsChild(*animations, animationName) ) + { + anim = Dali::Toolkit::Internal::CreateAnimation( *animation ); + } + } + else + { + DALI_SCRIPT_WARNING( "Request for Animation called '%s' failed\n", animationName.c_str() ); + } + + return anim; +} + +void Builder::LoadFromString( std::string const& data, Dali::Toolkit::Builder::UIFormat format ) +{ + DALI_ASSERT_ALWAYS( format == Dali::Toolkit::Builder::JSON && "Currently only JSON is supported" ); + + if( !mParser.Parse(data) ) + { + DALI_LOG_WARNING( "JSON Parse Error:%d:%d:'%s'\n", + mParser.GetErrorLineNumber(), + mParser.GetErrorColumn(), + mParser.GetErrorDescription().c_str() ); + + DALI_ASSERT_ALWAYS(!"Cannot parse JSON"); + } + + DALI_ASSERT_ALWAYS(mParser.GetRoot() && "Cannot parse JSON"); + +} + +void Builder::ApplyStyle( const std::string& styleName, Handle& handle ) +{ + OptionalChild styles = IsChild(*mParser.GetRoot(), "styles"); + + if( styles ) + { + if( OptionalChild style = IsChild(*styles, styleName) ) + { + TreeNodeList allStyles; + TypeInfo typeInfo; + + CollectAllStyles( styles, *style, allStyles, typeInfo ); + + for(TreeNodeList::reverse_iterator iter = allStyles.rbegin(); iter != allStyles.rend(); ++iter) + { + SetProperties( *style, handle, *this ); + } + } + else + { + DALI_SCRIPT_WARNING("Could not find style:%s\n", styleName.c_str()); + } + } + else + { + DALI_SCRIPT_WARNING("No style section available for style:%s\n", styleName.c_str()); + } +} + +BaseHandle Builder::CreateFromStyle( const std::string& styleName ) +{ + BaseHandle baseHandle; + + OptionalChild styles = IsChild(*mParser.GetRoot(), "styles"); + + if( !styles ) + { + DALI_SCRIPT_WARNING("No style section found to CreateFromStyle\n"); + } + else + { + OptionalChild style = IsChild(*styles, styleName); + if(!style) + { + DALI_SCRIPT_WARNING("Style '%s' does not exist in style section\n", styleName.c_str()); + } + else + { + OptionalString type = IsString( IsChild(*style, "type") ); + + if(!type) + { + DALI_SCRIPT_WARNING("Cannot create style '%s' as style section is missing 'type'\n", styleName.c_str()); + } + else + { + baseHandle = Create( mSlotDelegate.GetConnectionTracker(), styles, *style, *mParser.GetRoot(), Actor(), *this ); + } + } + } + + return baseHandle; +} + +Builder::Builder() +: mSlotDelegate( this ) +{ + mParser = Dali::Toolkit::JsonParser::New(); +} + +Builder::~Builder() +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/builder-impl.h b/dali-toolkit/internal/builder/builder-impl.h new file mode 100644 index 0000000..ba52c08 --- /dev/null +++ b/dali-toolkit/internal/builder/builder-impl.h @@ -0,0 +1,196 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUILDER_H__ +#define __DALI_TOOLKIT_INTERNAL_BUILDER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 +#include +#include + +// INTERNAL INCLUDES +#include +#include + +// Warning messages usually displayed +#define DALI_SCRIPT_WARNING(format, args...) \ + DALI_LOG_WARNING("Script:" format, ## args) + +// Info messages are usually debug build +#define DALI_SCRIPT_INFO(format, args...) \ + DALI_LOG_INFO(Dali::Toolkit::Internal::gFilterScript, Debug::General, "Script:" format, ## args) + +// Info Verbose need to be swiched on in gFilterScript filter constructor (by default set to General) +#define DALI_SCRIPT_VERBOSE(format, args...) \ + DALI_LOG_INFO(Dali::Toolkit::Internal::gFilterScript, Debug::Verbose, "Script:" format, ## args) + +namespace Dali +{ + +namespace Toolkit +{ + class TreeNode; +} + +namespace Toolkit +{ + +namespace Internal +{ + +#if defined(DEBUG_ENABLED) +extern Dali::Integration::Log::Filter* gFilterScript; +#endif + +class Builder; + +/** + * @copydoc Toolkit::Builder + */ +class Builder : public Dali::BaseObject +{ +public: + + Builder(); + + /** + * @copydoc Toolkit::Builder::LoadFromString + */ + void LoadFromString( const std::string &data, + Dali::Toolkit::Builder::UIFormat rep = Dali::Toolkit::Builder::JSON ); + + /** + * @copydoc Toolkit::Builder::CreateAnimation + */ + Animation CreateAnimation( const std::string& animationName ); + + /** + * @copydoc Toolkit::Builder::CreateFromStyle + */ + BaseHandle CreateFromStyle( const std::string& styleName ); + + /** + * @copydoc Toolkit::Builder::GetFont + */ + Font GetFont(const std::string &name) const; + + /** + * @copydoc Toolkit::Builder::GetTextStyle + */ + TextStyle GetTextStyle(const std::string &name) const; + + /** + * @copydoc Toolkit::Builder::GetImage + */ + Image GetImage(const std::string &name) const; + + /** + * @copydoc Toolkit::Builder::GetActor + */ + Actor GetActor( const std::string &name ) const; + + /** + * @copydoc Toolkit::Builder::ApplyStyle + */ + void ApplyStyle( const std::string& styleName, Handle& handle ); + + /** + * @copydoc Toolkit::Builder::AddActors + */ + void AddActors( Actor toActor ); + + /** + * @copydoc Toolkit::Builder::AddActors + */ + void AddActors( const std::string §ionName, Actor toActor ); + + /** + * @copydoc Toolkit::Builder::GetAnimation + */ + Animation GetAnimation( const std::string &name ) const; + + /** + * @copydoc Toolkit::Builder::CreateRenderTask + */ + void CreateRenderTask( const std::string &name ); + + /** + * @copydoc Toolkit::Builder::GetShaderEffect + */ + ShaderEffect GetShaderEffect( const std::string &name ); + + /** + * @copydoc Toolkit::Builder::GetFrameBufferImage + */ + FrameBufferImage GetFrameBufferImage( const std::string &name ); + + /** + * @copydoc Toolkit::Builder::GetTopLevelActors + */ + ActorContainer GetTopLevelActors( void ) const; + +protected: + + virtual ~Builder(); + +private: + // Undefined + Builder(const Builder&); + Builder& operator=(const Builder& rhs); + + void SetupTask( RenderTask& task, const Toolkit::TreeNode& node ); + +private: + Toolkit::JsonParser mParser; + + typedef std::map ImageLut; + ImageLut mFrameBufferImageLut; + + typedef std::map ShaderEffectLut; + ShaderEffectLut mShaderEffectLut; + + SlotDelegate mSlotDelegate; +}; + +} // namespace Internal + +inline Internal::Builder& GetImpl(Dali::Toolkit::Builder& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::Builder& GetImpl(const Dali::Toolkit::Builder& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_BUILDER_H__ diff --git a/dali-toolkit/internal/builder/builder-set-property.cpp b/dali-toolkit/internal/builder/builder-set-property.cpp new file mode 100644 index 0000000..5b079bb --- /dev/null +++ b/dali-toolkit/internal/builder/builder-set-property.cpp @@ -0,0 +1,488 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/* + * Set a property value from a tree node. + * This function guesses the type of the property from the format of the string in the node. + * This is not always possible and could be surprising. + * @param node The node string to convert from + * @param value The property value to set + * @return true if the string could be converted. + */ +bool SetPropertyFromNode( const TreeNode& node, Property::Value& value ); + +/* + * Set a property value as the given type from a tree node. + * @param node The node string to convert from + * @param type The property type to convert to. + * @param value The property value to set + * @return true if the string could be converted to the correct type. + */ +bool SetPropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value ); + +/** + * A property value type can be forced when its unknown by a disambiguation convention in the json + * ie "myarray": [1,2,3,4] ; would be a vector but + * "myarray": {'type-cast':'array', 'value':[1,2,3,4]} would be an array + * @param child The node whos string to search for a disambiguated type + * @param value The value to set + * @return True if child contained a disambiguated string that could be converted. + */ +bool Disambiguated(const TreeNode& child, // ConstantLut& constantLut, + Dali::Property::Value& value) +{ + OptionalString childType = IsString( IsChild(child, "type-cast") ); + OptionalChild childValue = IsChild(child, "value"); + + if( childType && childValue && (2 == child.Size()) ) + { + // this case allows disambiguation but normally the type is guessed + // 2 == child.count() is an extra check as the user could have a user dictionary/map with + // type-cast and value keys. If they do then a work around is to add a bogus key to not run this case. + if(*childType == "boolean") + { + return SetPropertyFromNode( *childValue, Dali::Property::BOOLEAN, value); + } + else if(*childType == "float") + { + return SetPropertyFromNode( *childValue, Dali::Property::FLOAT, value); + } + else if(*childType == "vector2") + { + return SetPropertyFromNode( *childValue, Dali::Property::VECTOR2, value); + } + else if(*childType == "vector3") + { + return SetPropertyFromNode( *childValue, Dali::Property::VECTOR3, value); + } + else if(*childType == "vector4") + { + return SetPropertyFromNode( *childValue, Dali::Property::VECTOR4, value); + } + else if(*childType == "rotation") + { + return SetPropertyFromNode( *childValue, Dali::Property::ROTATION, value); + } + else if(*childType == "rect") + { + return SetPropertyFromNode( *childValue, Dali::Property::RECTANGLE, value); + } + else if(*childType == "string") + { + return SetPropertyFromNode( *childValue, Dali::Property::STRING, value); + } + else if(*childType == "map") + { + return SetPropertyFromNode( *childValue, Dali::Property::MAP, value); + } + else if(*childType == "array") + { + return SetPropertyFromNode( *childValue, Dali::Property::ARRAY, value); + } + } + + // else we failed to disambiguate + return false; +} + +bool SetPropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value ) +{ + bool done = false; + + switch(type) + { + case Property::BOOLEAN: + { + if( OptionalBoolean v = IsBoolean(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::FLOAT: + { + if( OptionalFloat v = IsFloat(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::INTEGER: + { + if( OptionalInteger v = IsInteger(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::UNSIGNED_INTEGER: + { + if( OptionalInteger v = IsInteger(node) ) + { + if( *v >= 0 ) // with a loss of resolution.... + { + value = *v; + done = true; + } + } + break; + } + case Property::VECTOR2: + { + if( OptionalVector2 v = IsVector2(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::VECTOR3: + { + if( OptionalVector3 v = IsVector3(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::VECTOR4: + { + if( OptionalVector4 v = IsVector4(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::MATRIX3: + { + if( OptionalMatrix3 v = IsMatrix3(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::MATRIX: + { + if( OptionalMatrix v = IsMatrix(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::RECTANGLE: + { + if( OptionalRect v = IsRect(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::ROTATION: + { + if(4 == node.Size()) + { + if( OptionalVector4 ov = IsVector4(node) ) + { + const Vector4& v = *ov; + // angle, axis as per spec + value = Quaternion(Radian(Degree(v[3])), + Vector3(v[0],v[1],v[2])); + done = true; + } + } + else + { + // degrees Euler as per spec + if( OptionalVector3 v = IsVector3(node) ) + { + value = Quaternion(Radian(Degree((*v).x)), + Radian(Degree((*v).y)), + Radian(Degree((*v).z))); + done = true; + } + } + break; + } + case Property::STRING: + { + if( OptionalString v = IsString(node) ) + { + value = *v; + done = true; + } + break; + } + case Property::ARRAY: + { + if(node.Size()) + { + value = Property::Value(Property::ARRAY); + unsigned int i = 0; + TreeNode::ConstIterator iter(node.CBegin()); + for( ; i < node.Size(); ++i, ++iter) + { + Property::Value v; + if( SetPropertyFromNode( (*iter).second, v) ) + { + value.AppendItem(v); + } + } + + if( value.GetSize() == static_cast(node.Size()) ) + { + done = true; + } + else + { + done = false; + } + } + break; + } + case Property::MAP: + { + if(node.Size()) + { + value = Property::Value(Property::MAP); + unsigned int i = 0; + TreeNode::ConstIterator iter(node.CBegin()); + for( ; i < node.Size(); ++i, ++iter) + { + Property::Value v; + if( SetPropertyFromNode( (*iter).second, v) ) + { + value.SetValue( (*iter).first, v ); + } + } + + if( value.GetSize() == static_cast(node.Size()) ) + { + done = true; + } + else + { + done = false; + } + } + break; + } + case Property::TYPE_COUNT: + case Property::NONE: + { + break; + } + } + + return done; +} + + +bool SetPropertyFromNode( const TreeNode& node, Property::Value& value ) +{ + bool done = false; + + // some values are ambiguous as we have no Property::Type but can be disambiguated in the json + + // Currently Rotations and Rectangle must always be disambiguated when a type isnt available + if( Disambiguated( node, value ) ) + { + done = true; + } + else + { + if( node.Size() ) + { + // our current heuristic for deciding an array is actually a vector and not say a map + // is to check if the values are all floats + bool allNumbers = true; + for(TreeConstIter iter = node.CBegin(); iter != node.CEnd(); ++iter) + { + OptionalFloat f = IsFloat((*iter).second); + if(!f) + { + allNumbers = false; + break; + } + } + + if( allNumbers ) + { + // prefer finding vectors over presuming composite Property::Array... + if( OptionalMatrix v = IsMatrix(node) ) + { + value = *v; + done = true; + } + else if( OptionalMatrix3 v = IsMatrix3(node) ) + { + value = *v; + done = true; + } + if( OptionalVector4 v = IsVector4(node) ) + { + value = *v; + done = true; + } + else if( OptionalVector3 v = IsVector3(node) ) + { + value = *v; + done = true; + } + else if( OptionalVector2 v = IsVector2(node) ) + { + value = *v; + done = true; + } + else if( 4 == node.Size() ) + { + if( OptionalVector4 v = IsVector4(node) ) + { + value = *v; + done = true; + } + } + else + { + value = Property::Value(Property::ARRAY); + Property::Value v; + + for(TreeConstIter iter = node.CBegin(); iter != node.CEnd(); ++iter) + { + if( SetPropertyFromNode( (*iter).second, v) ) + { + value.AppendItem(v); + done = true; + } + } + } + } + + if(!done) + { + // presume an array or map + // container of size 1 + TreeNode::ConstIterator iter = node.CBegin(); + + // its seems legal with current json parser for a map to have an empty key + // but here we take that to mean the structure is a list + if( ((*iter).first) == 0 ) + { + value = Property::Value(Property::ARRAY); + Property::Value v; + for(unsigned int i = 0; i < node.Size(); ++i, ++iter) + { + if( SetPropertyFromNode( (*iter).second, v) ) + { + value.AppendItem(v); + done = true; + } + } + } + else + { + value = Property::Value(Property::MAP); + Property::Value v; + for(unsigned int i = 0; i < node.Size(); ++i, ++iter) + { + if( SetPropertyFromNode( (*iter).second, v) ) + { + value.SetValue((*iter).first, v); + done = true; + } + } + } + } // if!done + } // if node.size() + else // if( 0 == node.size() ) + { + // no children so either one of bool, float, integer, string + OptionalBoolean aBool = IsBoolean(node); + OptionalInteger anInt = IsInteger(node); + OptionalFloat aFloat = IsFloat(node); + OptionalString aString = IsString(node); + + if(aBool) + { + // a bool is also an int but here we presume int + if(anInt) + { + value = *anInt; + done = true; + } + else + { + value = *aBool; + done = true; + } + } + else + { + // Note: These are both floats and strings + // {"value":"123"} + // {"value":123} + // This means we can't have a string with purely numeric content without disambiguation. + if(aFloat) + { + value = *aFloat; + done = true; + } + else if(anInt) + { + value = *anInt; + done = true; + } + else + { + // string always succeeds with the current json parser so its last + value = *aString; + done = true; + } + + } // if aBool + + } // if( node.size() ) + + } // if Disambiguated() + + return done; +} // bool SetPropertyFromNode( const TreeNode& node, Property::Value& value ) + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/builder-signals.cpp b/dali-toolkit/internal/builder/builder-signals.cpp new file mode 100644 index 0000000..a7ed4c2 --- /dev/null +++ b/dali-toolkit/internal/builder/builder-signals.cpp @@ -0,0 +1,467 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +extern Animation CreateAnimation( const TreeNode& child ); +extern bool SetPropertyFromNode( const TreeNode& node, Property::Value& value ); +} +} +} + +namespace +{ +using namespace Dali; + +// +// Signal Actions +// + +// Action quit; connected to signals +// TODO: MOVE TO BUILDER TEMPLATE +struct ActionQuit +{ + ActionQuit(void) {}; + + void operator()(void) { + // Dali::Application::Get().Quit(); + }; +}; + +// Action on child actor. The child is found by alias so can be 'previous' etc. +struct ChildActorAction +{ + std::string actorName; + std::string actionName; + std::string childAlias; + std::vector parameters; + + void operator()(void) + { + Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName); + + if(actor) + { + Actor child_actor = actor.FindChildByAlias(childAlias); + + if(child_actor) + { + child_actor.DoAction(actionName, parameters); + } + else + { + DALI_SCRIPT_WARNING("Could not find child by alias '%s'\n", childAlias.c_str()); + } + } + }; +}; + +// Action to set a property +struct PropertySetAction +{ + std::string actorName; + std::string propertyName; + Property::Value value; + + void operator()(void) + { + Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName); + + if(actor) + { + Property::Index idx = actor.GetPropertyIndex(propertyName); + + if( idx != Property::INVALID_INDEX ) + { + actor.SetProperty( idx, value ); + } + } + }; +}; + +// Generic action on a handle (Animation & Actor) +struct GenericAction +{ + std::string actorName; + std::string actionName; + std::vector parameters; + + void operator()(void) + { + Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName); + if(actor) + { + actor.DoAction(actionName, parameters); + } + + }; +}; + +// Delay an animation play; ie wait as its not on stage yet +struct DelayedAnimationPlay +{ + Toolkit::JsonParser memento; + + void operator()(void) + { + Animation anim = Toolkit::Internal::CreateAnimation(*memento.GetRoot()); + if(anim) + { + anim.Play(); + } + }; +}; + +/* + * Gets Property::Value from child + */ +Property::Value GetPropertyValue(const TreeNode &child) +{ + size_t nChildren = child.Size(); + + Property::Value ret; + + if(0 == nChildren) + { + // cast away unused return for static analyzers + static_cast( Dali::Toolkit::Internal::SetPropertyFromNode( child, ret ) ); + } + else if(1 == nChildren) + { + // {"property": {"quaternion":[1,2,3,4]} } + // {"property": {"angle":22, "axis": [1,2,3]} } + + OptionalChild quaternion = IsChild(&child, "quaternion"); + OptionalChild axis = IsChild(&child, "axis"); + OptionalChild angle = IsChild(&child, "angle"); + + if(quaternion) + { + ret = Property::Value(Quaternion(GetVector4(*quaternion))); + } + else if(axis && angle) + { + ret = Property::Value(AngleAxis(Degree(GetFloat(*angle)), GetVector3(*axis))); + } + } + else if(2 == nChildren) + { + // {"property": [1,2]} + ret = Property::Value(GetVector2(child)); + } + else if(3 == nChildren) + { + // {"property": [1,2,3]} + ret = Property::Value(GetVector3(child)); + } + else if(4 == nChildren) + { + // {"property": [1,2,3,4]} + ret = Property::Value(GetVector4(child)); + } + + return ret; +} + + +/* + * Gets Parmeter list from child + * params is be cleared before insertion + */ +void GetParameters(const TreeNode &child, std::vector ¶ms) +{ + if( OptionalChild c = IsChild(child, "parameters") ) + { + const TreeNode& node = *c; + + if(0 == node.Size()) + { + GetPropertyValue(node); + } + else + { + params.clear(); + params.reserve(node.Size()); + + for(TreeNode::ConstIterator iter(node.CBegin()); iter != node.CEnd(); ++iter) + { + params.push_back( GetPropertyValue( (*iter).second ) ); + } + } + } +} + +void DoNothing(void) {}; + +/** + * Get an action as boost function callback + */ +boost::function GetAction(const TreeNode &root, const TreeNode &child, Actor actor) +{ + OptionalString childActorName(IsString( IsChild(&child, "child-actor")) ); + OptionalString actorName(IsString( IsChild(&child, "actor")) ); + OptionalString propertyName(IsString( IsChild(&child, "property")) ); + OptionalString valueChild(IsString( IsChild(&child, "value")) ); + + OptionalString actionName = IsString( IsChild(&child, "action") ); + DALI_ASSERT_ALWAYS(actionName && "Signal must have an action"); + + boost::function callback = DoNothing; + + if(childActorName) + { + ChildActorAction action; + action.actorName = *actorName; + action.childAlias = *childActorName; + action.actionName = *actionName; + GetParameters(child, action.parameters); + callback = action; + } + else if(actorName) + { + if(propertyName && valueChild) + { + PropertySetAction action; + action.actorName = *actorName; + action.propertyName = *propertyName; + callback = action; + } + else + { + GenericAction action; + action.actorName = *actorName; + action.actionName = *actionName; + GetParameters(child, action.parameters); + callback = action; + } + } + else if("quit" == *actionName) + { + callback = ActionQuit(); + } + else if("play" == *actionName) + { + OptionalChild animations = IsChild( root, "animations" ); + OptionalString animationName = IsString( IsChild(child, "animation") ); + if( animations && animationName ) + { + if( OptionalChild animNode = IsChild(*animations, *animationName) ) + { + DelayedAnimationPlay action; + action.memento = Toolkit::JsonParser::New(*animNode); + callback = action; + } + else + { + DALI_SCRIPT_WARNING("Cannot find animation '%s'\n", (*animationName).c_str()); + } + } + else + { + DALI_SCRIPT_WARNING("Cannot find animations section\n"); + } + } + else + { + // no named actor; presume self + GenericAction action; + action.actorName = actor.GetName(); + action.actionName = *actionName; + GetParameters(child, action.parameters); + callback = action; + } + + return callback; +} + + +/** + * Get a notification condition argument0 as 'arg0' 'value' or 'min' + */ +float GetConditionArg0(const TreeNode &child) +{ + OptionalFloat f = IsFloat( IsChild(child, "arg0") ); + // allowing some human preferable alternatives + if(!f) + { + f = IsFloat( IsChild(child, "value") ); + } + if(!f) + { + f = IsFloat( IsChild(child, "min") ); + } + DALI_ASSERT_ALWAYS(f && "Notification condition for arg0 not specified"); + if(f) + { + return *f; + } + else + { + return 0.f; + } +} + +/** + * Get a notification condition argument1 as 'arg1' or 'max' + */ +float GetConditionArg1(const TreeNode &child) +{ + OptionalFloat f = IsFloat( IsChild(child, "arg1") ); + // allowing some human preferable alternatives + if(!f) + { + f = IsFloat( IsChild(child, "max") ); + } + DALI_ASSERT_ALWAYS(f && "Notification condition for arg1 not specified"); + if(f) + { + return *f; + } + else + { + return 0.f; + } +} + + + +}; // anon namespace + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +Actor SetupSignalAction(const TreeNode &child, Actor actor); +Actor SetupPropertyNotification(const TreeNode &child, Actor actor); + +/** + * Setup signals and actions on an actor + */ +Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor) +{ + DALI_ASSERT_ALWAYS(actor); + + if(OptionalChild signalsChild = IsChild(child, "signals")) + { + const TreeNode& signalsNode = *signalsChild; + const TreeConstIter endIter = signalsNode.CEnd(); + for( TreeConstIter iter = signalsNode.CBegin(); endIter != iter; ++iter ) + { + const TreeNode::KeyNodePair& key_child = *iter; + + DALI_SCRIPT_INFO(" Creating Signal for: %s\n", actor.GetName().c_str()); + + OptionalString name( IsString( IsChild( key_child.second, "name")) ); + DALI_ASSERT_ALWAYS(name && "Signal must have a name"); + + boost::function callback = GetAction(root, key_child.second, actor); + + actor.ConnectSignal(tracker, *name, callback); + } + } + + return actor; +} + +/** + * Setup Property notifications for an actor + */ +Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor) +{ + DALI_ASSERT_ALWAYS(actor); + + if(OptionalChild notificationsChild = IsChild(child,"notifications")) + { + const TreeNode& notificationsNode = *notificationsChild; + const TreeNode::ConstIterator endIter = notificationsNode.CEnd(); + for( TreeNode::ConstIterator iter = notificationsNode.CBegin(); endIter != iter; ++iter ) + { + const TreeNode::KeyNodePair& key_child = *iter; + + // Actor actions reference by pointer because of circular reference actor->signal + // So this callback should only go onto the actor maintained list. + boost::function callback = GetAction(root, key_child.second, actor); + + OptionalString prop(IsString( IsChild(key_child.second, "property")) ); + DALI_ASSERT_ALWAYS(prop && "Notification signal must specify a property"); + + Property::Index prop_index = actor.GetPropertyIndex(*prop); + DALI_ASSERT_ALWAYS(prop_index != Property::INVALID_INDEX && "Notification signal specifies an unknown property"); + + OptionalString cond(IsString( IsChild(key_child.second, "condition"))); + DALI_ASSERT_ALWAYS(cond && "Notification signal must specify a condition"); + + if("False" == *cond) + { + PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop), + LessThanCondition(1.f) ); + notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) ); + } + else if("LessThan" == *cond) + { + PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop), + LessThanCondition(GetConditionArg0(key_child.second)) ); + notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) ); + } + else if("GreaterThan" == *cond) + { + PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop), + GreaterThanCondition(GetConditionArg0(key_child.second)) ); + notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) ); + } + else if("Inside" == *cond) + { + PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop), + InsideCondition(GetConditionArg0(key_child.second), + GetConditionArg1(key_child.second)) ); + notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) ); + } + else if("Outside" == *cond) + { + PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop), + OutsideCondition(GetConditionArg0(key_child.second), + GetConditionArg1(key_child.second)) ); + notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) ); + } + else + { + DALI_ASSERT_ALWAYS(!"Unknown condition"); + } + } + } // if notifications + + return actor; + +} // AddPropertyNotification + + +} // namespace Internal +} // namespace Toolkit +} // namespace Dali diff --git a/dali-toolkit/internal/builder/json-parser-impl.cpp b/dali-toolkit/internal/builder/json-parser-impl.cpp new file mode 100644 index 0000000..7a2832c --- /dev/null +++ b/dali-toolkit/internal/builder/json-parser-impl.cpp @@ -0,0 +1,186 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 +// EXTERNAL +#include + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +const char ERROR_DESCRIPTION_NONE[] = "No Error"; + +template +inline IteratorType Advance(IteratorType& iter, EndIteratorType& end, int n) +{ + for(int i =0; i < n; ++i) + { + ++iter; + } + return iter; +} + +} // anon namespace + + +JsonParser::JsonParser() + : mRoot(NULL), + mErrorDescription(ERROR_DESCRIPTION_NONE), + mErrorPosition(0), + mErrorLine(0), + mErrorColumn(0), + mNumberOfChars(0), + mNumberOfNodes(0) +{ +} + +JsonParser::JsonParser(const TreeNode& tree) + : mRoot(NULL), + mErrorDescription(ERROR_DESCRIPTION_NONE), + mErrorPosition(0), + mErrorLine(0), + mErrorColumn(0), + mNumberOfChars(0), + mNumberOfNodes(0) +{ + mRoot = TreeNodeManipulator::Copy( tree, mNumberOfNodes, mNumberOfChars ); + + mSources.push_back( VectorChar( (sizeof(char) * mNumberOfChars) ) ); + + VectorChar& buffer = mSources.back(); + + VectorCharIter start = buffer.begin(); + + TreeNodeManipulator modify(mRoot); + + modify.MoveStrings(start, buffer.end()); +} + +JsonParser::~JsonParser() +{ + if(mRoot) + { + TreeNodeManipulator modify(mRoot); + modify.RemoveChildren(); + delete mRoot; + mRoot = NULL; + } +} + +int JsonParser::Parse(const std::string& source) +{ + mSources.push_back( VectorChar(source.begin(), source.end()) ); + + JsonParserState parserState(mRoot); + + if( parserState.ParseJson(mSources.back()) ) + { + mRoot = parserState.GetRoot(); + + mNumberOfChars += parserState.GetParsedStringSize(); + mNumberOfNodes += parserState.GetCreatedNodeCount(); + + mErrorDescription = ERROR_DESCRIPTION_NONE; + mErrorPosition = 0; + mErrorLine = 0; + mErrorColumn = 0; + } + else + { + mErrorDescription = parserState.GetErrorDescription(); + if(NULL == mErrorDescription) + { + mErrorDescription = ERROR_DESCRIPTION_NONE; + } + mErrorPosition = parserState.GetErrorPosition(); + mErrorLine = parserState.GetErrorLineNumber(); + mErrorColumn = parserState.GetErrorColumn(); + } + + return mRoot != NULL; +} + + +const TreeNode* JsonParser::GetRoot() const +{ + return mRoot; +} + +bool JsonParser::ParseError() const +{ + return mErrorDescription != ERROR_DESCRIPTION_NONE; +} + +int JsonParser::GetErrorPosition() const +{ + return mErrorPosition; +} + +std::string JsonParser::GetErrorDescription() const +{ + return std::string(mErrorDescription); +} + +int JsonParser::GetErrorLineNumber() const +{ + return mErrorLine; +} + +int JsonParser::GetErrorColumn() const +{ + return mErrorColumn; +} + +void JsonParser::Pack(void) +{ + mSources.push_back( VectorChar( (sizeof(char) * mNumberOfChars) ) ); + + VectorChar& buffer = mSources.back(); + + VectorCharIter start = buffer.begin(); + + TreeNodeManipulator modify(mRoot); + + modify.MoveStrings(start, buffer.end()); + + mSources.erase( mSources.begin(), --mSources.end() ); +} + +void JsonParser::Write(std::ostream& output, int indent) const +{ + TreeNodeManipulator modify(mRoot); + modify.Write(output, indent); +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/json-parser-impl.h b/dali-toolkit/internal/builder/json-parser-impl.h new file mode 100644 index 0000000..e5be9f3 --- /dev/null +++ b/dali-toolkit/internal/builder/json-parser-impl.h @@ -0,0 +1,156 @@ +#ifndef __DALI_JSON_PARSER_IMPL_H__ +#define __DALI_JSON_PARSER_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +// +#include +#include +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/* + * Parses JSON + */ +class JsonParser : public BaseObject +{ +public: + /* + * @copydoc Toolkit::JsonParser::JsonParser() + */ + JsonParser(); + + /* + * @copydoc Toolkit::JsonParser::JsonParser(const TreeNode& tree) + */ + explicit JsonParser(const TreeNode& tree); + + /* + */ + ~JsonParser(); + + /* + * @copydoc Toolkit::JsonParser::Parse() + */ + int Parse(const std::string& source); + + /* + * @copydoc Toolkit::JsonParser::Pack() + */ + void Pack(void); + + /* + * @copydoc Toolkit::JsonParser::GetRoot() + */ + const TreeNode* GetRoot() const; + + /* + * @copydoc Toolkit::JsonParser::ParseError() + */ + bool ParseError() const; + + /* + * @copydoc Toolkit::JsonParser::GetErrorPosition() + */ + int GetErrorPosition() const; + + /* + * @copydoc Toolkit::JsonParser::GetErrorDescription() + */ + std::string GetErrorDescription() const; + + /* + * @copydoc Toolkit::JsonParser::GetErrorLineNumber() + */ + int GetErrorLineNumber() const; + + /* + * @copydoc Toolkit::JsonParser::GetErrorColumn() + */ + int GetErrorColumn() const; + + /* + * @copydoc Toolkit::JsonParser::Write() + */ + void Write(std::ostream& output, int indent) const; + +private: + typedef std::vector VectorChar; + typedef VectorChar::iterator VectorCharIter; + + typedef std::list SourceContainer; + typedef std::list::iterator SourceContainerIter; + + JsonParser(JsonParser &); + JsonParser& operator=(const JsonParser&); + + SourceContainer mSources; ///< List of strings from Parse() merge operations + + TreeNode* mRoot; ///< Tree root + + const char *mErrorDescription; ///< Last parse error description + int mErrorPosition; ///< Last parse error position + int mErrorLine; ///< Last parse error line + int mErrorColumn; ///< Last parse error column + + int mNumberOfChars; ///< The size of string data for all nodes + int mNumberOfNodes; ///< Node count + +}; + +} // namespace Internal + + +inline const Internal::JsonParser& GetImplementation(const Toolkit::JsonParser& parser) +{ + DALI_ASSERT_ALWAYS( parser && "JsonParser handle is empty" ); + + const BaseObject& handle = parser.GetBaseObject(); + + return static_cast(handle); +} + + +inline Internal::JsonParser& GetImplementation(Toolkit::JsonParser& parser) +{ + DALI_ASSERT_ALWAYS( parser && "JsonParser handle is empty" ); + + BaseObject& handle = parser.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + + +#endif // __DALI_JSON_PARSER_IMPL_H__ diff --git a/dali-toolkit/internal/builder/json-parser-state.cpp b/dali-toolkit/internal/builder/json-parser-state.cpp new file mode 100644 index 0000000..ca7b139 --- /dev/null +++ b/dali-toolkit/internal/builder/json-parser-state.cpp @@ -0,0 +1,966 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +// true if character represent a digit +inline bool IsDigit(char c) +{ + return (c >= '0' && c <= '9'); +} + +// convert string to integer +bool StringToInteger(const char *first, const char *last, int& out) +{ + int sign = 1; + if (first != last) + { + if (*first == '-') + { + sign = -1; + ++first; + } + else if (*first == '+') + { + ++first; + } + } + + // json error for int starting with zero + if( 0 == (*first - '0') && (first+1 != last)) + { + return false; + } + + int result = 0; + for (; first != last && IsDigit(*first); ++first) + { + result = 10 * result + (*first - '0'); + } + out = result * sign; + + if(first != last) + { + return false; + } + else + { + return true; + } +} + +// convert hexadecimal string to unsigned integer +bool HexStringToUnsignedInteger(const char *first, const char *last, unsigned int& out) +{ + unsigned int result = 0; + for (; first != last; ++first) + { + int digit; + if (IsDigit(*first)) + { + digit = *first - '0'; + } + else if (*first >= 'a' && *first <= 'f') + { + digit = *first - 'a' + 10; + } + else if (*first >= 'A' && *first <= 'F') + { + digit = *first - 'A' + 10; + } + else + { + break; + } + result = 16 * result + digit; + } + out = result; + + if(first != last) + { + return false; + } + else + { + return true; + } +} + +// convert string to floating point +bool StringToFloat(const char* first, const char* last, float& out) +{ + // sign + float sign = 1; + if (first != last) + { + if (*first == '-') + { + sign = -1; + ++first; + } + else if (*first == '+') + { + ++first; + } + } + + // integer part + float result = 0; + for (; first != last && IsDigit(*first); ++first) + { + result = 10 * result + (*first - '0'); + } + + // fraction part + if (first != last && *first == '.') + { + ++first; + + float inv_base = 0.1f; + for (; first != last && IsDigit(*first); ++first) + { + result += (*first - '0') * inv_base; + inv_base *= 0.1f; + } + } + + // result w\o exponent + result *= sign; + + // exponent + bool exponent_negative = false; + int exponent = 0; + if (first != last && (*first == 'e' || *first == 'E')) + { + ++first; + + if (*first == '-') + { + exponent_negative = true; + ++first; + } + else if (*first == '+') + { + ++first; + } + + if(first == last || !IsDigit(*first)) + { + return false; + } + + for (; first != last && IsDigit(*first); ++first) + { + exponent = 10 * exponent + (*first - '0'); + } + } + + if (exponent) + { + float power_of_ten = 10; + for (; exponent > 1; exponent--) + { + power_of_ten *= 10; + } + + if (exponent_negative) + { + result /= power_of_ten; + } + else + { + result *= power_of_ten; + } + } + + out = result; + + if(first != last) + { + return false; + } + else + { + return true; + } +} + + +bool IsNumber(char c) +{ + bool ret = false; + switch( c ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + ret = true; + break; + } + default: + { + ret = false; + } + } + return ret; +} + +} // anon namespace + + +JsonParserState::JsonParserState(TreeNode* _root) + : mRoot(_root), mCurrent(_root), + mErrorDescription(NULL), mErrorNewLine(0), mErrorColumn(0), mErrorPosition(0), + mNumberOfParsedChars(0), mNumberOfCreatedNodes(0), mFirstParse(false), + mState(STATE_START) +{ + if(_root == NULL) + { + mFirstParse = true; + } +} + +TreeNode* JsonParserState::CreateNewNode(const char* name, TreeNode::NodeType type) +{ + TreeNode* node = NULL; + + node = TreeNodeManipulator::NewTreeNode(); + TreeNodeManipulator modifyNew(node); + modifyNew.SetType(type); + modifyNew.SetName(name); + if(mRoot == NULL) + { + mRoot = node; + mCurrent = TreeNodeManipulator(mRoot); + } + else + { + mCurrent.AddChild(node); + mCurrent = modifyNew; + } + + ++mNumberOfCreatedNodes; + + return node; + +} + +TreeNode* JsonParserState::NewNode(const char* name, TreeNode::NodeType type) +{ + TreeNode* node = NULL; + + if(mFirstParse) + { + node = CreateNewNode(name, type); + } + else + { + // a merging parse + + if(name) + { + const TreeNode* found = mCurrent.GetChild(name); + if( NULL != found ) + { + node = const_cast(found); + } + } + else + { + // if root node + if( mCurrent.GetParent() == NULL ) + { + node = mRoot; + } + } + + if(node) + { + // walk tree and deallocate children as were replacing this node + TreeNodeManipulator modify(node); + + modify.SetName(name); + // Set the type of the existing node, this may remove children where necessary + // (changing from container type to value type) + modify.SetType(type); + + mCurrent = modify; + } + else + { + // if not found then create new + node = CreateNewNode(name, type); + } + } + + return node; +} + +TreeNode* JsonParserState::GetRoot() +{ + return mRoot; +} + +bool JsonParserState::Error(const char* description) +{ + mErrorDescription = description; + return false; +} + +bool JsonParserState::ParseWhiteSpace() +{ + bool c_comment = false; + bool cpp_comment = false; + + // skip white space + char nextChar = 0; + while(1) + { + char c = Char(); + + if(c == '\xA') + { + NewLine(); + } + + if( AtLeast(2) ) + { + nextChar = mIter[1]; + } + else + { + nextChar = 0; + } + + if( cpp_comment ) + { + if( '\n' == c ) + { + cpp_comment = false; + Advance(1); + continue; // rather than carry on as comments may be back to back + } + } + else if( !c_comment && (c == '/' && nextChar == '/') ) + { + cpp_comment = true; + } + + if( c_comment ) + { + if( c == '*' && nextChar == '/' ) + { + c_comment = false; + Advance(2); + continue; + } + } + else if( !cpp_comment && (c == '/' && nextChar == '*') ) + { + c_comment = true; + } + + if( ! (c_comment || cpp_comment) ) + { + if( ! (c == '\x20' || c == '\x9' || c == '\xD' || c == '\xA' ) ) + { + break; + } + } + + if( AdvanceEnded(1) ) + { + break; + } + + } // while(1) + + return true; +} // ParseWhiteSpace + +bool JsonParserState::ParseSymbol(const std::string& symbol) +{ + if( AtLeast( symbol.size() ) ) + { + for(int i = 0; i < static_cast( symbol.size() ); ++i) + { + if(*mIter != symbol[i]) + { + return false; + } + Advance(1); + } + return true; + } + else + { + return false; + } +} + +bool JsonParserState::ParseTrue() +{ + if( ParseSymbol("true") ) + { + mCurrent.SetInteger(1); + mCurrent.SetType(TreeNode::BOOLEAN); + return true; + } + else + { + return Error("Unexpected character; expected symbol ie 'true'"); + } +} + +bool JsonParserState::ParseFalse() +{ + if( ParseSymbol("false") ) + { + mCurrent.SetInteger(0); + mCurrent.SetType(TreeNode::BOOLEAN); + return true; + } + else + { + return Error("Unexpected character; expected symbol ie 'false'"); + } +} + +bool JsonParserState::ParseNULL() +{ + if( ParseSymbol("null") ) + { + mCurrent.SetType(TreeNode::IS_NULL); + return true; + } + else + { + return Error("Unexpected character; expected symbol ie 'null'"); + } +} + +bool JsonParserState::ParseNumber() +{ + mCurrent.SetType( TreeNode::INTEGER ); + + VectorCharIter first = mIter; + char c = Char(); + + if( !(c == '-' || IsNumber(c) ) ) + { + return Error("Number must start with '-' or 0-9"); + } + + while ( IsNumber(c) || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-' ) + { + if (c == '.' || c == 'e' || c == 'E') + { + mCurrent.SetType( TreeNode::FLOAT ); + } + Advance(1); + c = Char(); + } + + if( mCurrent.GetType() == TreeNode::INTEGER ) + { + int i = 0; + if( StringToInteger(&(*first), &(*mIter), i ) ) + { + mCurrent.SetInteger(i); + } + else + { + return Error("Bad integer number"); + } + } + + if(mCurrent.GetType() == TreeNode::FLOAT) + { + float f = 0.f; + if( StringToFloat(&(*first), &(*mIter), f) ) + { + mCurrent.SetFloat(f); + } + else + { + return Error("Bad float number"); + } + } + + return (mCurrent.GetType() == TreeNode::INTEGER) || (mCurrent.GetType() == TreeNode::FLOAT); +} + +char* JsonParserState::EncodeString() +{ + int substitution = 0; + VectorCharIter first = mIter; + VectorCharIter last = mIter; + + while (*mIter) + { + if (static_cast(*mIter) < '\x20') + { + static_cast( Error("Control characters not allowed in strings") ); + return NULL; + } + else if (*mIter == '\\' && AtLeast(2)) + { + switch (*(mIter+1)) + { + case '"': + { + *last = '"'; + break; + } + case '\\': + { + *last = '\\'; + break; + } + case '/': + { + *last = '/'; + break; + } + case 'b': + { + *last = '\b'; + break; + } + case 'f': + { + *last = '\f'; + break; + } + case 'n': + { + *last = '\n'; + break; + } + case 'r': + { + *last = '\r'; + break; + } + case 't': + { + *last = '\t'; + break; + } + case 'u': + { + unsigned int codepoint; + if( !AtLeast(6) ) + { + static_cast( Error("Bad unicode codepoint; not enough characters") ); + return NULL; + } + if ( !HexStringToUnsignedInteger(&(*(mIter + 2)), &(*(mIter + 6)), codepoint) ) + { + static_cast( Error("Bad unicode codepoint") ); + return NULL; + } + + if (codepoint <= 0x7F) + { + *last = (char)codepoint; + } + else if (codepoint <= 0x7FF) + { + *last++ = (char)(0xC0 | (codepoint >> 6)); + *last = (char)(0x80 | (codepoint & 0x3F)); + } + else if (codepoint <= 0xFFFF) + { + *last++ = (char)(0xE0 | (codepoint >> 12)); + *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + *last = (char)(0x80 | (codepoint & 0x3F)); + } + + Advance(4); + break; + } // case 'u' unicode + + default: + { + static_cast( Error("Unrecognized escape sequence") ); + return NULL; + } + } + + ++last; + Advance(2); + } + else if (*mIter == '{') + { + if((0 == substitution) && (*last != '\\')) + { + substitution = 1; + } + *last++ = *mIter; + Advance(1); + } + else if (*mIter == '}') + { + if(substitution) + { + substitution++; + } + *last++ = *mIter; + Advance(1); + } + else if (*mIter == '"') + { + *last = 0; + Advance(1); + break; + } + else + { + *last++ = *mIter; + Advance(1); + } + + } // while(*mIter) + + mNumberOfParsedChars += last - first; + mNumberOfParsedChars += 1 ; // null terminator + + // return true; + return &(*first); + +} // ParseString() + +bool JsonParserState::ParseJson(VectorChar& source) +{ + Reset(); + + if( 0 == source.size() ) + { + return Error("Empty source buffer to parse"); + } + + mIter = source.begin(); + mEnd = source.end(); + + char* name = NULL; + char currentChar = 0; + char lastCharacter = 0; + + if( !ParseWhiteSpace() ) + { + return false; + } + + while(mIter != mEnd) + { + lastCharacter = currentChar; + currentChar = Char(); + + switch( mState ) + { + case STATE_START: + { + if( '{' == currentChar ) + { + NewNode(name, TreeNode::OBJECT); + mState = STATE_OBJECT; + } + else if( '[' == currentChar ) + { + NewNode(name, TreeNode::ARRAY); + mState = STATE_VALUE; + } + else + { + return Error("Json must start with object {} or array []"); + } + + AdvanceSkipWhiteSpace(1); + break; + } + case STATE_OBJECT: + { + if( '}' == currentChar ) + { + if( !UpToParent() ) + { + return false; + } + mState = STATE_VALUE; + } + else if ( '"' == currentChar ) + { + mState = STATE_KEY; + } + else + { + return Error("Unexpected character"); + } + + AdvanceSkipWhiteSpace(1); + break; + } + case STATE_KEY: + { + name = EncodeString(); + if( NULL == name ) + { + return false; + } + if( !ParseWhiteSpace() ) + { + return false; + } + if( ':' != Char()) + { + return Error("Expected ':'"); + } + if( !ParseWhiteSpace() ) + { + return false; + } + mState = STATE_VALUE; + + AdvanceSkipWhiteSpace(1); + break; + } + case STATE_VALUE: + { + if( '"' == currentChar ) + { + Advance(1); + NewNode(name, TreeNode::STRING); + if( char* value = EncodeString() ) + { + mCurrent.SetString(value); + } + else + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + } + else if( IsNumber(currentChar) || currentChar == '-' ) + { + NewNode(name, TreeNode::IS_NULL); + if( !ParseNumber() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + } + else if( '{' == currentChar ) + { + NewNode(name, TreeNode::OBJECT); + mState = STATE_OBJECT; + AdvanceSkipWhiteSpace(1); + } + else if( '}' == currentChar ) + { + if(',' == lastCharacter) + { + return Error("Expected a value"); + } + + if(mCurrent.GetType() != TreeNode::OBJECT) + { + return Error("Mismatched array definition"); + } + + if(mCurrent.GetParent() == NULL) + { + mState = STATE_END; + } + else + { + if( !UpToParent() ) + { + return false; + } + } + AdvanceSkipWhiteSpace(1); + } + else if( '[' == currentChar ) + { + NewNode(name, TreeNode::ARRAY); + mState = STATE_VALUE; + AdvanceSkipWhiteSpace(1); + } + else if( ']' == currentChar ) + { + if(',' == lastCharacter) + { + return Error("Expected a value"); + } + + if(mCurrent.GetType() != TreeNode::ARRAY) + { + return Error("Mismatched braces in object definition"); + } + + if(mCurrent.GetParent() == NULL) + { + mState = STATE_END; + } + else + { + if( !UpToParent() ) + { + return false; + } + } + AdvanceSkipWhiteSpace(1); + } + else if( 't' == currentChar ) + { + NewNode(name, TreeNode::BOOLEAN); + if( !ParseTrue() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + } + else if( 'n' == currentChar ) + { + NewNode(name, TreeNode::IS_NULL); + if( !ParseNULL() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + } + else if( 'f' == currentChar) + { + NewNode(name, TreeNode::BOOLEAN); + if( !ParseFalse() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + } + else if( ',' == currentChar ) + { + if( 0 == mCurrent.Size() ) + { + return Error("Missing Value"); + } + + if(mCurrent.GetType() == TreeNode::OBJECT) + { + mState = STATE_OBJECT; // to get '"' in '"key":val' + } + else if(mCurrent.GetType() == TreeNode::ARRAY) + { + mState = STATE_VALUE; // array so just get next value + } + else + { + return Error("Unexpected character"); + } + AdvanceSkipWhiteSpace(1); + } + else + { + return Error("Unexpected character"); + } + + name = NULL; + + break; + } // case STATE_VALUE + case STATE_END: + { + return Error("Unexpected character. Json must have one object or array at its root"); + break; + } + } // switch(mState) + + } // while(1) + + // + if( mState != STATE_END ) + { + return Error("Unexpected termination character"); + } + + mIter = source.end(); + + return true; + +} // ParseJson + + +void JsonParserState::Reset() +{ + mCurrent = TreeNodeManipulator(mRoot); + + mErrorDescription = NULL; + mErrorNewLine = 0; + mErrorColumn = 0; + mErrorPosition = 0; +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/builder/json-parser-state.h b/dali-toolkit/internal/builder/json-parser-state.h new file mode 100644 index 0000000..28a9330 --- /dev/null +++ b/dali-toolkit/internal/builder/json-parser-state.h @@ -0,0 +1,313 @@ +#ifndef __DALI_JSON_PARSE_STATE_H__ +#define __DALI_JSON_PARSE_STATE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/* + * A safer std::advance() + */ +template +inline int AdvanceIter(IteratorType& iter, EndIteratorType& end, int n) +{ + for(int i =0; i < n; ++i) + { + if(iter == end) + { + return n - i; + } + ++iter; + } + return n; +} + +/* + * Maintains parser state machine + * + * If a NULL root node is passed in the constructor then a faster non merging parse is performed (the first pass). + * Otherwise the json tree is merged (and requires slower searching) + */ +class JsonParserState +{ +public: + /* + * Constructor + * @param tree Tree to start with, pass NULL if no existing tree + */ + explicit JsonParserState(TreeNode* tree); + + /* + * Parse json source + * The source is modified in place + * @param source The vector buffer to parse + * @return true if parsed successfully + */ + bool ParseJson(VectorChar& source); + + /* + * Get the root node + * @return The root TreeNode + */ + TreeNode* GetRoot(); + + /* + * Get the error description of the last parse + * @return The error description or NULL if no error + */ + const char* GetErrorDescription() { return mErrorDescription; } + + /* + * Get the error line number + * @return The line number of the error + */ + int GetErrorLineNumber() { return mErrorNewLine; } + + /* + * Get the error column + * @return The error column + */ + int GetErrorColumn() { return mErrorColumn; } + + /* + * Get the error position + * @return The error position + */ + int GetErrorPosition() { return mErrorPosition; } + + /* + * Get the size of the string data that has been parsed + * @return The size of string data + */ + int GetParsedStringSize() { return mNumberOfParsedChars; }; + + /* + * Get the number of nodes created + * @return The number of nodes + */ + int GetCreatedNodeCount() { return mNumberOfCreatedNodes; }; + +private: + VectorCharIter mIter; ///< Current position + VectorCharIter mStart; ///< Start position + VectorCharIter mEnd; ///< End of buffer being parsed + TreeNode* mRoot; ///< Root node created + TreeNodeManipulator mCurrent; ///< The Current modifiable node + const char* mErrorDescription; ///< The error description if set + int mErrorNewLine; ///< The error line number + int mErrorColumn; ///< The error column + int mErrorPosition; ///< The error position + int mNumberOfParsedChars; ///< The size of string data + int mNumberOfCreatedNodes; ///< The number of nodes created + bool mFirstParse; ///< Flag if first parse + + /* + * The current parse state + */ + enum State + { + STATE_START, + STATE_OBJECT, + STATE_KEY, + STATE_VALUE, + STATE_END, + }; + + State mState; + + // inhibited copy construct and assignment + JsonParserState(const JsonParserState&); + const JsonParserState& operator=(const JsonParserState&); + + /* + * Parse over white space + * Increments the current position + * @return true if no parse errors + */ + bool ParseWhiteSpace(); + + /* + * Parse over a number, setting the current node if found + * Increments the current position. Sets error data if parse error. + * @return true if found, false if parse error + */ + bool ParseNumber(); + + /* + * Parse over a symbol + * Increments the current position. Sets error data if parse error. + * @return true if found, false if parse error + */ + bool ParseSymbol(const std::string& symbol); + + /* + * Parse over 'true' symbol, setting the current node if found + * Increments the current position. Sets error data if parse error. + * @return true if found, false if parse error + */ + bool ParseTrue(); + + /* + * Parse over 'false' symbol, setting the current node if found + * Increments the current position. Sets error data if parse error. + * @return true if found, false if parse error + */ + bool ParseFalse(); + + /* + * Parse over 'null' symbol, setting the current node if found + * Increments the current position. Sets error data if parse error. + * @return true if found, false if parse error + */ + bool ParseNULL(); + + /* + * Parse over a string from the current position and insert escaped + * control characters in place in the string and a null terminator. + * This function works from and modifes the current buffer position. + * @return the start of the null terminated string + */ + char* EncodeString(); + + /* + * Create a new node with name and type + */ + TreeNode* CreateNewNode(const char* name, TreeNode::NodeType type); + + /* + * Create a new node if first parse, else check if the node already + * exists and set it to a new type + */ + TreeNode* NewNode(const char* name, TreeNode::NodeType type); + + /* + * Set error meta data + * @returns always false. + */ + bool Error(const char* description); + + /* + * Reset state for another parse + */ + void Reset(); + + /* + * Set current to its parent + * @return true if we had a parent, false and error otherwise + */ + inline bool UpToParent() + { + if(NULL == mCurrent.GetParent()) + { + return Error("Attempt to walk up above root"); + } + mCurrent = TreeNodeManipulator( mCurrent.GetParent() ); + return true; + } + + /* + * Get the current character + */ + inline char Char() + { + return *mIter; + } + + /* + * @return True if there are at least n character left + */ + inline bool AtLeast(int n) + { + // The standard suggests vector.end() can be decremented as + // iter v.back() { *--v.end() } + // (ISO/IEC 14882:2003 C++ Standard 23.1.1/12 – Sequences) + return (mEnd - mIter) > n; + } + + /* + * @return True if at the end of the data to parse + */ + inline bool AtEnd() + { + return mEnd == mIter; + } + + /* + * Advance current position by n characters or stop at mEnd + */ + inline void Advance(int n) + { + int c = AdvanceIter(mIter, mEnd, n); + mErrorPosition += c; + mErrorColumn += c; + } + + /* + * Advance by n charaters and return true if we reached the end + */ + inline bool AdvanceEnded(int n) + { + int c = AdvanceIter(mIter, mEnd, n); + mErrorPosition += c; + mErrorColumn += c; + return mEnd == mIter; + } + + /* + * Advance by at least n characters (stopping at mEnd) and skip any whitespace after n. + */ + inline void AdvanceSkipWhiteSpace(int n) + { + int c = AdvanceIter(mIter, mEnd, n); + mErrorPosition += c; + mErrorColumn += c; + static_cast( ParseWhiteSpace() ); + } + + /* + * Increment new line counters + */ + inline void NewLine() + { + ++mErrorNewLine; + mErrorColumn = 0; + } + +}; + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + + +#endif // header diff --git a/dali-toolkit/internal/builder/optional-value.h b/dali-toolkit/internal/builder/optional-value.h new file mode 100644 index 0000000..51421ac --- /dev/null +++ b/dali-toolkit/internal/builder/optional-value.h @@ -0,0 +1,92 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUILDER_OPTIONAL__ +#define __DALI_TOOLKIT_INTERNAL_BUILDER_OPTIONAL__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +template +struct OptionalTypes +{ + typedef T ValueType; + typedef const T& ReturnType; + static ReturnType Get(const ValueType& v) { return v; } + static ValueType Set(const ReturnType v) { return v; } + static bool Ok(const ValueType v) { return true; } +}; + +template +struct OptionalTypes +{ + typedef T* ValueType; + typedef const T* ReturnType; + static ReturnType Get(const ValueType v) { return v; } + static ValueType Set(const ReturnType v) { return v; } + static bool Ok(const ReturnType v) { return NULL != v; } +}; + +template +struct OptionalTypes +{ + typedef T* ValueType; + typedef const T& ReturnType; + static ReturnType Get(const ValueType v) { return *v; } + static ValueType Set(const ReturnType v) { return &v; } + static bool Ok(const ReturnType v) { return true; } +}; + +template +class OptionalValue +{ +public: + typedef void ( OptionalValue::*bool_type )() const; + typedef typename OptionalTypes::ReturnType ReturnType; + typedef typename OptionalTypes::ValueType ValueType; + + OptionalValue(): mOk(false), mValue() {} + OptionalValue( T value ): mOk(OptionalTypes::Ok(value)), mValue(OptionalTypes::Set(value)) {} + OptionalValue( bool b, T value ): mOk(b), mValue(OptionalTypes::Set(value)) {} + + ReturnType operator *() const { return OptionalTypes::Get(mValue); } + + // safe bool idiom + operator bool_type() const { + return mOk == true ? &OptionalValue::this_type_does_not_support_comparisons : 0; + } + + template + bool operator!=( const OT& rhs ) + { + this->this_type_does_not_support_comparisons(); + return false; + } + + template + bool operator==( const OT& rhs ) + { + this->this_type_does_not_support_comparisons(); + return false; + } + +private: + bool mOk; + ValueType mValue; + void this_type_does_not_support_comparisons() const {} + // todo operator=() ? use OptionalTypes::Ok(mValue) +}; + + +#endif // header diff --git a/dali-toolkit/internal/builder/tree-node-manipulator.cpp b/dali-toolkit/internal/builder/tree-node-manipulator.cpp new file mode 100644 index 0000000..c396f03 --- /dev/null +++ b/dali-toolkit/internal/builder/tree-node-manipulator.cpp @@ -0,0 +1,522 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +void Indent(std::ostream& o, int indent) +{ + for (int i = 0; i < indent; ++i) + { + o << " "; + } +} + +} + +TreeNodeManipulator::TreeNodeManipulator(TreeNode* node) + : mNode(node) +{ +} + +TreeNode* TreeNodeManipulator::NewTreeNode() +{ + return new TreeNode(); +} + +void TreeNodeManipulator::ShallowCopy(const TreeNode* from, TreeNode* to) +{ + DALI_ASSERT_DEBUG(from); + DALI_ASSERT_DEBUG(to); + + if( from ) + { + to->mName = from->mName; + to->mType = from->mType; + to->mSubstituion = from->mSubstituion; + switch(from->mType) + { + case TreeNode::INTEGER: + { + to->mIntValue = from->mIntValue; + break; + } + case TreeNode::FLOAT: + { + to->mFloatValue = from->mFloatValue; + break; + } + case TreeNode::STRING: + { + to->mStringValue = from->mStringValue; + break; + } + case TreeNode::BOOLEAN: + { + to->mIntValue = from->mIntValue; + break; + } + case TreeNode::IS_NULL: + case TreeNode::OBJECT: + case TreeNode::ARRAY: + { + break; + } + } + } + +} + +void TreeNodeManipulator::MoveNodeStrings(VectorCharIter& start, const VectorCharIter& sentinel) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + if(mNode->mName) + { + mNode->mName = CopyString(mNode->mName, start, sentinel); + } + + if(TreeNode::STRING == mNode->mType) + { + mNode->mStringValue = CopyString(mNode->mStringValue, start, sentinel); + } +} + +void TreeNodeManipulator::MoveStrings(VectorCharIter& start, const VectorCharIter& sentinel) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + TreeNodeManipulator modify(mNode); + modify.MoveNodeStrings(start, sentinel); + RecurseMoveChildStrings(start, sentinel); +} + +void TreeNodeManipulator::RecurseMoveChildStrings(VectorCharIter& start, const VectorCharIter& sentinel) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + TreeNode* child = mNode->mFirstChild; + while(child) + { + TreeNodeManipulator manipChild(child); + manipChild.MoveNodeStrings(start, sentinel); + child = child->mNextSibling; + } + + child = mNode->mFirstChild; + while(child) + { + TreeNodeManipulator manipChild(child); + manipChild.RecurseMoveChildStrings(start, sentinel); + child = child->mNextSibling; + } +} + +void TreeNodeManipulator::RemoveChildren() +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + CollectNodes collector; + + DepthFirst( mNode, collector ); + + for(CollectNodes::iterator iter = collector.nodes.begin(); iter != collector.nodes.end(); ++iter) + { + if( *iter != mNode) + { + delete *iter; + } + } + + mNode->mFirstChild = NULL; + mNode->mLastChild = NULL; +} + +TreeNode* TreeNodeManipulator::Copy(const TreeNode& tree, int& numberNodes, int& numberChars) +{ + TreeNode* root = NewTreeNode(); + + ShallowCopy(&tree, root); + + if(tree.mName) + { + numberChars += std::strlen(tree.mName); + } + + if(TreeNode::STRING == tree.mType) + { + numberChars += std::strlen(tree.mStringValue); + } + + ++numberNodes; + + CopyChildren(&tree, root, numberNodes, numberChars); + + return root; +} + +void TreeNodeManipulator::CopyChildren(const TreeNode* from, TreeNode* to, int& numberNodes, int& numberChars) +{ + DALI_ASSERT_DEBUG(from && "Operation on NULL JSON node"); + DALI_ASSERT_DEBUG(to); + + for( TreeNode::ConstIterator iter = from->CBegin(); iter != from->CEnd(); ++iter) + { + const TreeNode* child = &((*iter).second); + if(child->mName) + { + numberChars += std::strlen(child->mName) + 1; + } + + if(TreeNode::STRING == child->mType) + { + numberChars += std::strlen(child->mStringValue) + 1; + } + + TreeNode* newNode = NewTreeNode(); + + ShallowCopy(child, newNode); + + TreeNodeManipulator modify(to); + + modify.AddChild(newNode); + + ++numberNodes; + + CopyChildren(child, newNode, numberNodes, numberChars); + } +} + +TreeNode *TreeNodeManipulator::AddChild(TreeNode *rhs) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + rhs->mParent = mNode; + if (mNode->mLastChild) + { + mNode->mLastChild = mNode->mLastChild->mNextSibling = rhs; + } + else + { + mNode->mFirstChild = mNode->mLastChild = rhs; + } + return rhs; +} + +TreeNode::NodeType TreeNodeManipulator::GetType() const +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + return mNode->GetType(); +} + +size_t TreeNodeManipulator::Size() const +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + return mNode->Size(); +} + +void TreeNodeManipulator::SetType( TreeNode::NodeType type) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + + if( mNode->mType != type ) + { + mNode->mType = type; + + if( NULL != mNode->mFirstChild ) + { + // value types have no children + bool removeChildren = ! (TreeNode::OBJECT == type || TreeNode::ARRAY == type); + + // ie if swapping array for object + removeChildren = (removeChildren == true) ? true : type != mNode->mType; + + // so remove any children + if( removeChildren && NULL != mNode->mFirstChild) + { + RemoveChildren(); + } + } + } +} + +void TreeNodeManipulator::SetName( const char* name ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + mNode->mName = name; +} + +void TreeNodeManipulator::SetSubstitution( bool b ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + mNode->mSubstituion = b; +} + +TreeNode* TreeNodeManipulator::GetParent() const +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + return NULL == mNode ? NULL : mNode->mParent; +} + +const TreeNode* TreeNodeManipulator::GetChild(const std::string& name) const +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + return NULL == mNode ? NULL : mNode->GetChild(name); +} + +void TreeNodeManipulator::SetString( const char* string ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + SetType(TreeNode::STRING); + mNode->mStringValue = string; +} + +void TreeNodeManipulator::SetInteger( int i ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + SetType(TreeNode::INTEGER); + mNode->mIntValue = i; +} + +void TreeNodeManipulator::SetFloat( float f ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + SetType(TreeNode::FLOAT); + mNode->mFloatValue = f; +} + +void TreeNodeManipulator::SetBoolean( bool b ) +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + SetType(TreeNode::BOOLEAN); + mNode->mIntValue = b == true ? 1 : 0; +} + +void TreeNodeManipulator::Write(std::ostream& output, int indent) const +{ + DALI_ASSERT_DEBUG(mNode && "Operation on NULL JSON node"); + DoWrite(mNode, output, indent); +} + +void TreeNodeManipulator::DoWrite(const TreeNode *value, std::ostream& output, int indent) const +{ + DALI_ASSERT_DEBUG(value && "Operation on NULL JSON node"); + + Indent(output, indent); + + if (value->GetName()) + { + output << "\"" << value->GetName() << "\":"; + } + + switch(value->GetType()) + { + case TreeNode::IS_NULL: + { + output << "null"; + if(NULL != value->mNextSibling) + { + output << ","; + } + if(indent) + { + output << std::endl; + } + break; + } + case TreeNode::OBJECT: + case TreeNode::ARRAY: + { + if( value->GetType() == TreeNode::OBJECT) + { + output << "{"; + if(indent) + { + output << std::endl; + } + } + else + { + output << "["; + if(indent) + { + output << std::endl; + } + } + + for (TreeNode::ConstIterator it = value->CBegin(); it != value->CEnd(); ++it) + { + DoWrite( &((*it).second), output, indent + 1); + } + Indent(output, indent); + if( value->GetType() == TreeNode::OBJECT ) + { + output << "}"; + if(indent) + { + output << std::endl; + } + } + else + { + output << "]"; + if(indent) + { + output << std::endl; + } + } + break; + } + case TreeNode::STRING: + { + output << "\"" << value->GetString() << "\""; + if(NULL != value->mNextSibling) + { + output << ","; + } + if(indent) + { + output << std::endl; + } + + break; + } + case TreeNode::INTEGER: + { + output << value->GetInteger(); + if(NULL != value->mNextSibling) + { + output << ","; + } + if(indent) + { + output << std::endl; + } + + break; + } + case TreeNode::FLOAT: + { + output.setf( std::ios::floatfield ); + output << value->GetFloat(); + output.unsetf( std::ios::floatfield ); + if(NULL != value->mNextSibling) + { + output << ","; + } + if(indent) + { + output << std::endl; + } + break; + } + case TreeNode::BOOLEAN: + { + if( value->GetInteger() ) + { + output << "true"; + } + else + { + output << "false"; + } + if(NULL != value->mNextSibling) + { + output << ","; + } + if(indent) + { + output << std::endl; + } + + break; + } + } // switch +} // DoWrite + + +const TreeNode* FindIt(const std::string& childName, const TreeNode* node) +{ + DALI_ASSERT_DEBUG(node); + + const TreeNode* found = NULL; + + if( node ) + { + if( NULL != (found = node->GetChild(childName)) ) + { + return found; + } + else + { + for(TreeNode::ConstIterator iter = node->CBegin(); iter != node->CEnd(); ++iter) + { + if( NULL != (found = FindIt(childName, &((*iter).second)) ) ) + { + return found; + } + } + } + } + return found; +} + +char *CopyString( const char *fromString, VectorCharIter& iter, const VectorCharIter& sentinel) +{ + DALI_ASSERT_DEBUG(fromString); + DALI_ASSERT_DEBUG(iter != sentinel); + + char *start= &(*iter); + const char *ptr = fromString; + + if(ptr) + { + while(*ptr != 0) + { + DALI_ASSERT_DEBUG(iter != sentinel); + *iter++ = *ptr++; + } + + *iter++ = 0; + } + return start; +} + + +} // namespace internal + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/internal/builder/tree-node-manipulator.h b/dali-toolkit/internal/builder/tree-node-manipulator.h new file mode 100644 index 0000000..3b39e37 --- /dev/null +++ b/dali-toolkit/internal/builder/tree-node-manipulator.h @@ -0,0 +1,260 @@ +#ifndef __DALI_SCRIPT_TREE_NODE_MANIPULATOR_H__ +#define __DALI_SCRIPT_TREE_NODE_MANIPULATOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include // pair +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +typedef std::vector VectorChar; +typedef VectorChar::iterator VectorCharIter; + +/* + * TreeNodeManipulator performs modification operations on a TreeNode which are + * otherwise prohibited on the TreeNode public interface. + */ +class TreeNodeManipulator +{ +public: + /* + * Constructor + * @param node The TreeNode to modify + */ + explicit TreeNodeManipulator(TreeNode* node); + + /* + * Create a new TreeNode instance + * @return new TreeNode + */ + static TreeNode* NewTreeNode(); + + /* + * Shallow copy node data + * Shallow copy the data but doesnt parent or copy children + * @param from Node to copy from + * @param to Node to copy to + */ + static void ShallowCopy(const TreeNode* from, TreeNode* to); + + /* + * Moves all string data to a new buffer. There must be enough space for all string data. + * @param start The buffer start + * @param sentinel The end of the buffer + */ + void MoveStrings(VectorCharIter& start, const VectorCharIter& sentinel); + + /* + * Remove all children from the node + */ + void RemoveChildren(); + + /* + * Make a deep copy of the tree. + * @param tree The tree to copy + * @param numberOfNodes The number of nodes that were copied + * @param numberOfChars The size of string data. + */ + static TreeNode* Copy(const TreeNode& tree, int& numberOfNodes, int& numberOfChars); + + /* + * Add child to the node + * @param child The child to add + * @return the added child + */ + TreeNode *AddChild(TreeNode *child); + + /* + * Change the type of the Node + * NB: If the type changes from a type with children to a value type without children then + * the children are removed + * @param type The new type + */ + void SetType( TreeNode::NodeType type); + + /* + * Set the name of the node + * @param name The name to set + */ + void SetName( const char* name ); + + /* + * Set the substituion flag + * The substitution flag indicates this nodes string value contains a reference to another node + * in the tree. + * @param on The state + */ + void SetSubstitution( bool on ); + + /* + * Get the nodes type + * @return The nodes type + */ + TreeNode::NodeType GetType() const; + + /* + * Get the number of children of the node + * @return The number of children + */ + size_t Size() const; + + /* + * Set the node as a string value + * @param string The string value + */ + void SetString( const char* string ); + + /* + * Set the node as an integer value + * @param i The integer + */ + void SetInteger( int i ); + + /* + * Set the node as an float value + * @param f The float + */ + void SetFloat( float f ); + + /* + * Set the node as an boolean value + * @param b The boolean + */ + void SetBoolean( bool b ); + + /* + * Get the nodes parent + * @return The nodes parent + */ + TreeNode* GetParent() const; + + /* + * Get the nodes child by name + * @param name The childs name + * @return The nodes if found, else NULL + */ + const TreeNode* GetChild(const std::string& name) const; + + /* + * @copydoc Dali::Scripting::JsonParser::Write() + */ + void Write(std::ostream& output, int indent) const; + +private: + TreeNode *mNode; + + /* + * Move the nodes strings to the buffer + */ + void MoveNodeStrings(VectorCharIter& start, const VectorCharIter& sentinel); + + /* + * Recursively move child strings to the buffer + */ + void RecurseMoveChildStrings(VectorCharIter& start, const VectorCharIter& sentinel); + + /* + * Recursively copy children + */ + static void CopyChildren(const TreeNode* from, TreeNode* to, int& numberNodes, int& numberChars); + + /* + * Do write to string stream + */ + void DoWrite(const TreeNode *value, std::ostream& output, int ident) const; + +}; + +/* + * Collect nodes + */ +struct CollectNodes : public std::unary_function +{ + CollectNodes() {}; + + /* + * Call operator to add nodes to the list + */ + result_type operator()(argument_type& n) + { + DALI_ASSERT_DEBUG(n && "Operation on NULL JSON node"); + nodes.push_back(n); + } + + typedef std::vector VectorNodes; + typedef VectorNodes::iterator iterator; + + VectorNodes nodes; ///< List of collected nodes +}; + +/* + * Depth first walk of nodes applying given operation (unary_function) + */ +template +void DepthFirst( TreeNode* node, Operation& operation) +{ + DALI_ASSERT_DEBUG(node && "Operation on NULL JSON node"); + + for(TreeNode::ConstIterator iter = node->CBegin(); iter != node->CEnd(); ++iter) + { + // iterator access is const for external api but were modifying + DepthFirst( const_cast(&((*iter).second)), operation); + } + + operation(node); + +} + +/* + * Recursive search on the tree for the child with the given name + * @param childName The name to find + * @param tree The tree to search + * @return the TreeNode if found, else NULL + */ +const TreeNode* FindIt(const std::string& childName, const TreeNode* tree); + +/* + * Copy string to a buffer + * Raises if there is not enough space in the buffer + * @param fromString The string + * @param iter The start of the buffer + * @param sentinel The buffer sentinel + * @return The start of the given buffer + */ +char *CopyString( const char *fromString, VectorCharIter& iter, const VectorCharIter& sentinel); + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_SCRIPT_TREE_NODE_MANIPULATOR_H__ diff --git a/dali-toolkit/internal/controls/alignment/alignment-impl.cpp b/dali-toolkit/internal/controls/alignment/alignment-impl.cpp new file mode 100644 index 0000000..fa09991 --- /dev/null +++ b/dali-toolkit/internal/controls/alignment/alignment-impl.cpp @@ -0,0 +1,642 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "alignment-impl.h" + +// INTERNAL INCLUDES + +// EXTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +//Type Registration +BaseHandle Create() +{ + return Toolkit::Alignment::New(); +} + +TypeRegistration mType( typeid(Toolkit::Alignment), typeid(Toolkit::Control), Create ); + +struct ScaleToFillConstraint +{ + /** + * @param padding to be added. + */ + ScaleToFillConstraint( const Toolkit::Alignment::Padding& padding ) + : mPadding( padding ) + {} + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + ScaleToFillConstraint( const ScaleToFillConstraint& rhs ) + : mPadding( rhs.mPadding ) + {} + + /** + * Called by render thread + */ + Vector3 operator()( const Vector3& currentSize, + const PropertyInput& parentSizeProperty ) + { + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + return GetSize( currentSize, parentSize ); + } + + inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize ) + { + const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right ); + const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom ); + + // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor + if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 ) ) + { + // no point trying to squeeze actors into this small size + return Vector3::ZERO; + } + return Vector3( parentSizeWidth, parentSizeHeight, parentSize.depth ); + } + + const Toolkit::Alignment::Padding mPadding; +}; + +struct ScaleToFitKeepAspectConstraint +{ + /** + * @param padding to be added. + */ + ScaleToFitKeepAspectConstraint( const Toolkit::Alignment::Padding& padding ) + : mPadding( padding ), + mSizeStored( false ), + mOriginalSize() + {} + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + ScaleToFitKeepAspectConstraint( const ScaleToFitKeepAspectConstraint& rhs ) + : mPadding( rhs.mPadding ), + mSizeStored( rhs.mSizeStored ), + mOriginalSize( rhs.mOriginalSize ) + {} + + /** + * Called by render thread + */ + Vector3 operator()( const Vector3& currentSize, + const PropertyInput& parentSizeProperty ) + { + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + return GetSize( currentSize, parentSize ); + } + + inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize ) + { + if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) ) + { + mOriginalSize = currentSize; + mSizeStored = true; + } + + const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right ); + const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom ); + + // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor + if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )|| + ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) ) + { + // no point trying to squeeze actors into this small size + return Vector3::ZERO; + } + + return mOriginalSize * std::min( ( parentSizeWidth / mOriginalSize.width ), ( parentSizeHeight / mOriginalSize.height ) ); + } + + const Toolkit::Alignment::Padding mPadding; + bool mSizeStored; + Vector3 mOriginalSize; +}; + +struct ScaleToFillKeepAspectConstraint +{ + /** + * @param padding to be added. + */ + ScaleToFillKeepAspectConstraint( const Toolkit::Alignment::Padding& padding ) + : mPadding( padding ), + mSizeStored( false ), + mOriginalSize() + { } + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + ScaleToFillKeepAspectConstraint( const ScaleToFillKeepAspectConstraint& rhs ) + : mPadding( rhs.mPadding ), + mSizeStored( rhs.mSizeStored ), + mOriginalSize( rhs.mOriginalSize ) + {} + + /** + * Called by render thread + */ + Vector3 operator()( const Vector3& currentSize, + const PropertyInput& parentSizeProperty ) + { + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + return GetSize( currentSize, parentSize ); + } + + Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize ) + { + if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) ) + { + mOriginalSize = currentSize; + mSizeStored = true; + } + + const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right ); + const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom ); + + // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor + if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )|| + ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) ) + { + // no point trying to squeeze actors into this small size + return Vector3::ZERO; + } + + return mOriginalSize * std::max( ( parentSizeWidth / mOriginalSize.width ), ( parentSizeHeight / mOriginalSize.height ) ); + } + + const Toolkit::Alignment::Padding mPadding; + bool mSizeStored; + Vector3 mOriginalSize; +}; + +struct ShrinkToFitConstraint +{ + /** + * @param padding to be added. + */ + ShrinkToFitConstraint( const Toolkit::Alignment::Padding& padding ) + : mPadding( padding ), + mSizeStored( false ), + mOriginalSize() + {} + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + ShrinkToFitConstraint( const ShrinkToFitConstraint& rhs ) + : mPadding( rhs.mPadding ), + mSizeStored( rhs.mSizeStored ), + mOriginalSize( rhs.mOriginalSize ) + { } + + /** + * Called by render thread + */ + Vector3 operator()( const Vector3& currentSize, + const PropertyInput& parentSizeProperty ) + { + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + return GetSize( currentSize, parentSize ); + } + + Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize ) + { + if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) ) + { + mOriginalSize = currentSize; + mSizeStored = true; + } + + const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right ); + const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom ); + + // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor + if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )|| + ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) ) + { + // no point trying to squeeze actors into this small size + return Vector3::ZERO; + } + + return Vector3( std::min( parentSizeWidth, mOriginalSize.width ), std::min( parentSizeHeight, mOriginalSize.height ), std::min( parentSize.depth, mOriginalSize.depth ) ); + } + + const Toolkit::Alignment::Padding mPadding; + bool mSizeStored; + Vector3 mOriginalSize; +}; + +/** + * Constraint that uses naturalSize if it fits inside parent and parent size if not. It also adds some padding pixels + */ +struct ShrinkToFitKeepAspectConstraint +{ + /** + * @param padding to be added. + */ + ShrinkToFitKeepAspectConstraint( const Toolkit::Alignment::Padding& padding ) + : mPadding( padding ), + mSizeStored( false ), + mOriginalSize() + {} + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + ShrinkToFitKeepAspectConstraint( const ShrinkToFitKeepAspectConstraint& rhs ) + : mPadding( rhs.mPadding ), + mSizeStored( rhs.mSizeStored ), + mOriginalSize( rhs.mOriginalSize ) + { } + + /** + * Called by render thread + */ + Vector3 operator()( const Vector3& currentSize, + const PropertyInput& parentSizeProperty ) + { + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + return GetSize( currentSize, parentSize ); + } + + inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize ) + { + if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) ) + { + mOriginalSize = currentSize; + mSizeStored = true; + } + + const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right ); + const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom ); + + // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor + if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )|| + ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) ) + { + // no point trying to squeeze actors into this small size + return Vector3::ZERO; + } + + return Vector3( ShrinkInside( Vector2( parentSizeWidth, parentSizeHeight ), Vector2( mOriginalSize ) ) ); + } + + const Toolkit::Alignment::Padding mPadding; + bool mSizeStored; + Vector3 mOriginalSize; +}; + +/** + * Constraint that modifies the contained actor taking into account the padding value. + */ +struct PositionConstraint +{ + /** + * @param padding The padding value + * @param horizontalAlignment The horizontal alignment. + * @param verticalAlignment The vertical alignment. + */ + PositionConstraint( const Toolkit::Alignment::Padding& padding, Toolkit::Alignment::Type horizontalAlignment, Toolkit::Alignment::Type verticalAlignment ) + : mPadding( padding ), + mHorizontalAlignment( horizontalAlignment ), + mVerticalAlignment( verticalAlignment ) + {} + + /** + * CopyConstructor. Used by Boost. + * @param rhs Copying from. + */ + PositionConstraint( const PositionConstraint& rhs ) + : mPadding( rhs.mPadding ), + mHorizontalAlignment( rhs.mHorizontalAlignment ), + mVerticalAlignment( rhs.mVerticalAlignment ) + {} + + /** + * Called by render thread. + */ + Vector3 operator()( const Vector3& currentPosition, + const PropertyInput& currentSizeProperty, + const PropertyInput& parentSizeProperty ) + { + const Vector3& currentSize( currentSizeProperty.GetVector3() ); + const Vector3& parentSize( parentSizeProperty.GetVector3() ); + + Vector3 position( 0.f, 0.f, 0.f ); + + switch( mHorizontalAlignment ) + { + case Dali::Toolkit::Alignment::HorizontalLeft: + { + position.x += mPadding.left; + break; + } + case Dali::Toolkit::Alignment::HorizontalCenter: + { + if( currentSize.width + mPadding.left + mPadding.right >= parentSize.width ) + { + position.x += 0.5f * ( mPadding.left - mPadding.right ); + } + break; + } + case Dali::Toolkit::Alignment::HorizontalRight: + { + position.x -= mPadding.right; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"Wrong horizontal alignment value" ); + break; + } + } + + switch( mVerticalAlignment ) + { + case Dali::Toolkit::Alignment::VerticalTop: + { + position.y += mPadding.top; + break; + } + case Dali::Toolkit::Alignment::VerticalCenter: + { + if( currentSize.height + mPadding.top + mPadding.bottom >= parentSize.height ) + { + position.y += 0.5f * ( mPadding.top - mPadding.bottom ); + } + break; + } + case Dali::Toolkit::Alignment::VerticalBottom: + { + position.y -= mPadding.bottom; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"Wrong vertical alignment value" ); + break; + } + } + + return position; + } + + const Toolkit::Alignment::Padding mPadding; + const Toolkit::Alignment::Type mHorizontalAlignment; + const Toolkit::Alignment::Type mVerticalAlignment; +}; + +void SetPositionConstraint( Actor actor, const Toolkit::Alignment::Padding& padding, Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical ) +{ + Constraint constraint = Constraint::New( Actor::POSITION, + LocalSource( Actor::SIZE ), + ParentSource( Actor::SIZE ), + PositionConstraint( padding, horizontal, vertical ) ); + actor.ApplyConstraint( constraint ); +} +} // namespace + +Toolkit::Alignment Alignment::New( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical ) +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< Alignment > internalAlignment = new Alignment( horizontal, vertical ); + + // Pass ownership to Toolkit::View + Toolkit::Alignment alignment( *internalAlignment ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalAlignment->Initialize(); + + return alignment; +} + +void Alignment::SetAlignmentType( Toolkit::Alignment::Type type ) +{ + // Horizontal Alignment + bool horizontalSet(false); + + if( type & Toolkit::Alignment::HorizontalLeft ) + { + mHorizontal = Toolkit::Alignment::HorizontalLeft; + horizontalSet = true; + } + if( type & Toolkit::Alignment::HorizontalCenter ) + { + DALI_ASSERT_ALWAYS(!horizontalSet); + mHorizontal = Toolkit::Alignment::HorizontalCenter; + horizontalSet = true; + } + if( type & Toolkit::Alignment::HorizontalRight ) + { + DALI_ASSERT_ALWAYS(!horizontalSet); + mHorizontal = Toolkit::Alignment::HorizontalRight; + } + + // Vertical Alignment + bool verticalSet(false); + + if( type & Toolkit::Alignment::VerticalTop ) + { + mVertical = Toolkit::Alignment::VerticalTop; + verticalSet = true; + } + if( type & Toolkit::Alignment::VerticalCenter ) + { + DALI_ASSERT_ALWAYS(!verticalSet); + mVertical = Toolkit::Alignment::VerticalCenter; + verticalSet = true; + } + if( type & Toolkit::Alignment::VerticalBottom ) + { + DALI_ASSERT_ALWAYS(!verticalSet); + mVertical = Toolkit::Alignment::VerticalBottom; + } + + RelayoutRequest(); +} + +Toolkit::Alignment::Type Alignment::GetAlignmentType() const +{ + return Toolkit::Alignment::Type( mHorizontal | mVertical ); +} + +void Alignment::SetScaling( Toolkit::Alignment::Scaling scaling ) +{ + mScaling = scaling; + + RelayoutRequest(); +} + +Toolkit::Alignment::Scaling Alignment::GetScaling() const +{ + return mScaling; +} + +void Alignment::SetPadding( const Toolkit::Alignment::Padding& padding ) +{ + DALI_ASSERT_ALWAYS( ( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f ) ); + + mPadding = padding; + + RelayoutRequest(); +} + +const Toolkit::Alignment::Padding& Alignment::GetPadding() const +{ + return mPadding; +} + +void Alignment::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + // lay out the actors + Vector3 anchorPointAndParentOrigin = Vector3::ZERO; + anchorPointAndParentOrigin.z = 0.5f; + // anchorPoint.x is initialized to 0.0, which is HorizontalLeft + if( Toolkit::Alignment::HorizontalCenter == mHorizontal ) + { + anchorPointAndParentOrigin.x = 0.5f; + } + else if( Toolkit::Alignment::HorizontalRight == mHorizontal ) + { + anchorPointAndParentOrigin.x = 1.0f; + } + // anchorPoint.y is initialized to 0.0, which is VerticalTop + if( Toolkit::Alignment::VerticalCenter == mVertical ) + { + anchorPointAndParentOrigin.y = 0.5f; + } + else if( Toolkit::Alignment::VerticalBottom == mVertical ) + { + anchorPointAndParentOrigin.y = 1.0f; + } + + unsigned int childCount = Self().GetChildCount(); + for( unsigned int i=0; i +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Alignment; + +namespace Internal +{ + +/** + * Alignment is a control to position and resize actors inside other container actors. + * @see Dali::Toolkit::Alignment for more details. + */ +class Alignment : public ControlImpl +{ +public: + + /** + * Create an initialized Alignment. + * @param type Type of alignment. + * @return A handle to a newly allocated Dali resource. + */ + static Toolkit::Alignment New( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical ); + + /** + * @copydoc Dali::Toolkit::Alignment::SetAlignmentType() + */ + void SetAlignmentType( Toolkit::Alignment::Type type ); + + /** + * @copydoc Dali::Toolkit::Alignment::GetAlignmentType() + */ + Toolkit::Alignment::Type GetAlignmentType() const; + + /** + * @copydoc Dali::Toolkit::Alignment::SetScaling() + */ + void SetScaling( Toolkit::Alignment::Scaling scaling ); + + /** + * @copydoc Dali::Toolkit::Alignment::GetScaling() + */ + Toolkit::Alignment::Scaling GetScaling() const; + + /** + * @copydoc Dali::Toolkit::Alignment::SetPadding() + */ + void SetPadding( const Toolkit::Alignment::Padding& padding ); + + /** + * @copydoc Dali::Toolkit::Alignment::GetPadding() + */ + const Toolkit::Alignment::Padding& GetPadding() const; + + +private: // From Control + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut() + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + +private: + + /** + * Constructor. + * It initializes Alignment members. + */ + Alignment( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Alignment(); + +private: + + // Undefined + Alignment(const Alignment&); + Alignment& operator=(const Alignment&); + +private: + Toolkit::Alignment::Type mHorizontal; ///< Type of alignment. + Toolkit::Alignment::Type mVertical; ///< Type of alignment. + Toolkit::Alignment::Scaling mScaling; ///< Stores the geometry scaling. + Toolkit::Alignment::Padding mPadding; ///< Stores the padding values. +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Alignment& GetImpl( Toolkit::Alignment& alignment ) +{ + DALI_ASSERT_ALWAYS( alignment ); + + Dali::RefObject& handle = alignment.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::Alignment& GetImpl( const Toolkit::Alignment& alignment ) +{ + DALI_ASSERT_ALWAYS( alignment ); + + const Dali::RefObject& handle = alignment.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ALIGNMENT_H__ diff --git a/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.cpp b/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.cpp new file mode 100644 index 0000000..8251623 --- /dev/null +++ b/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.cpp @@ -0,0 +1,595 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "bloom-view-impl.h" +#include "../gaussian-blur-view/gaussian-blur-view-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +using namespace Dali; + +BaseHandle Create() +{ + return Toolkit::BloomView::New(); +} + +TypeRegistration mType( typeid(Toolkit::BloomView), typeid(Toolkit::Control), Create ); + +// default parameters +const float BLOOM_THRESHOLD_DEFAULT = 0.25f; +const float BLOOM_BLUR_STRENGTH_DEFAULT = 1.0f; +const float BLOOM_INTENSITY_DEFAULT = 1.0f; +const float IMAGE_INTENSITY_DEFAULT = 1.0f; +const float BLOOM_SATURATION_DEFAULT = 1.0f; +const float IMAGE_SATURATION_DEFAULT = 1.0f; + +// gaussian blur +const unsigned int BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES = 5; +const float BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH = 1.5f; +const Pixel::Format BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT = Pixel::RGBA8888; +const float BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_FADE_IN = 1.0f; // default, fully blurred +const float BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE = 0.5f; +const float BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE = 0.5f; + +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; + +const std::string BLOOM_BLUR_STRENGTH_PROPERTY_NAME( "BlurStrengthProperty" ); + +const std::string BLOOM_THRESHOLD_PROPERTY_NAME( "uBloomThreshold" ); +const std::string RECIP_ONE_MINUS_BLOOM_THRESHOLD_PROPERTY_NAME( "uRecipOneMinusBloomThreshold" ); +const std::string BLOOM_INTENSITY_PROPERTY_NAME( "uBloomIntensity" ); +const std::string BLOOM_SATURATION_PROPERTY_NAME( "uBloomSaturation" ); +const std::string IMAGE_INTENSITY_PROPERTY_NAME( "uImageIntensity" ); +const std::string IMAGE_SATURATION_PROPERTY_NAME( "uImageSaturation" ); + +/////////////////////////////////////////////////////// +// +// Bloom shaders +// + +const char* const BLOOM_EXTRACT_FRAGMENT_SOURCE = + "uniform float uBloomThreshold;\n" + "uniform float uRecipOneMinusBloomThreshold;\n" + "void main()\n" + "{\n" + " mediump vec4 col;\n" + " col = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y));\n" + " col = (col - uBloomThreshold) * uRecipOneMinusBloomThreshold;\n" // remove intensities lower than the thresold and remap intensities above the threshold to [0..1] + " gl_FragColor = clamp(col, 0.0, 1.0);\n" + "}\n"; + +const char* const COMPOSITE_FRAGMENT_SOURCE = + "uniform float uBloomIntensity;\n" + "uniform float uImageIntensity;\n" + "uniform float uBloomSaturation;\n" + "uniform float uImageSaturation;\n" + + "vec4 ChangeSaturation(vec4 col, float sat)\n" + "{\n" + " float grey = dot(col.rgb, vec3(0.3, 0.6, 0.1));\n" + " return mix(vec4(grey, grey, grey, 1.0), col, sat);\n" + "}\n" + + "void main()\n" + "{\n" + " mediump vec4 image;\n" + " mediump vec4 bloom;\n" + " image = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y));\n" + " bloom = texture2D(sEffect, vec2(vTexCoord.x, vTexCoord.y));\n" + " image = ChangeSaturation(image, uImageSaturation) * uImageIntensity;\n" + " bloom = ChangeSaturation(bloom, uBloomSaturation) * uBloomIntensity;\n" + " image *= 1.0 - clamp(bloom, 0.0, 1.0);\n" // darken base where bloom is strong, to prevent excessive burn-out of result + " gl_FragColor = image + bloom;\n" + "}\n"; + +} // namespace + + + +BloomView::BloomView() + : ControlImpl( false ) // doesn't require touch events + , mBlurNumSamples(BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES) + , mBlurBellCurveWidth(BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH) + , mPixelFormat(BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT) + , mDownsampleWidthScale(BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE) + , mDownsampleHeightScale(BLOOM_GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE) + , mDownsampledWidth( 0.0f ) + , mDownsampledHeight( 0.0f ) + , mTargetSize(Vector2::ZERO) + , mLastSize(Vector2::ZERO) + , mChildrenRoot(Actor::New()) + , mBloomThresholdPropertyIndex(Property::INVALID_INDEX) + , mBlurStrengthPropertyIndex(Property::INVALID_INDEX) + , mBloomIntensityPropertyIndex(Property::INVALID_INDEX) + , mBloomSaturationPropertyIndex(Property::INVALID_INDEX) + , mImageIntensityPropertyIndex(Property::INVALID_INDEX) + , mImageSaturationPropertyIndex(Property::INVALID_INDEX) +{ +} + +BloomView::BloomView( const unsigned int blurNumSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale) + : ControlImpl( false ) // doesn't require touch events + , mBlurNumSamples(blurNumSamples) + , mBlurBellCurveWidth(blurBellCurveWidth) + , mPixelFormat(renderTargetPixelFormat) + , mDownsampleWidthScale(downsampleWidthScale) + , mDownsampleHeightScale(downsampleHeightScale) + , mDownsampledWidth( 0.0f ) + , mDownsampledHeight( 0.0f ) + , mTargetSize(Vector2::ZERO) + , mLastSize(Vector2::ZERO) + , mChildrenRoot(Actor::New()) + , mBloomThresholdPropertyIndex(Property::INVALID_INDEX) + , mBlurStrengthPropertyIndex(Property::INVALID_INDEX) + , mBloomIntensityPropertyIndex(Property::INVALID_INDEX) + , mBloomSaturationPropertyIndex(Property::INVALID_INDEX) + , mImageIntensityPropertyIndex(Property::INVALID_INDEX) + , mImageSaturationPropertyIndex(Property::INVALID_INDEX) +{ +} + +BloomView::~BloomView() +{ +} + +Toolkit::BloomView BloomView::New() +{ + BloomView* impl = new BloomView(); + + Dali::Toolkit::BloomView handle = Dali::Toolkit::BloomView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +Toolkit::BloomView BloomView::New(const unsigned int blurNumSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale) +{ + BloomView* impl = new BloomView( blurNumSamples, blurBellCurveWidth, renderTargetPixelFormat, downsampleWidthScale, downsampleHeightScale); + + Dali::Toolkit::BloomView handle = Dali::Toolkit::BloomView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +///////////////////////////////////////////////////////////// +// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks +// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root. +void BloomView::Add(Actor child) +{ + mChildrenRoot.Add(child); +} + +void BloomView::Remove(Actor child) +{ + mChildrenRoot.Remove(child); +} + + + + + + +/////////////////////////////////////////////////////////// +// +// Private methods +// + +void BloomView::OnInitialize() +{ + // root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task + mChildrenRoot.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mChildrenRoot.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as BloomView object + + + ////////////////////////////////////////////////////// + // Create shaders + + // Create shader used for extracting the bright parts of an image + mBloomExtractShader = ShaderEffect::New( "", BLOOM_EXTRACT_FRAGMENT_SOURCE ); + + // Create shader used to composite bloom and original image to output render target + mCompositeShader = ShaderEffect::New( "", COMPOSITE_FRAGMENT_SOURCE ); + + + ////////////////////////////////////////////////////// + // Create actors + + // Create an ImageActor for rendering from the scene texture to the bloom texture + mBloomExtractImageActor = ImageActor::New(); + mBloomExtractImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mBloomExtractImageActor.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + mBloomExtractImageActor.SetShaderEffect( mBloomExtractShader ); + + // Create an ImageActor for compositing the result (scene and bloom textures) to output + mCompositeImageActor = ImageActor::New(); + mCompositeImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mCompositeImageActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as BloomView object + mCompositeImageActor.SetShaderEffect( mCompositeShader ); + mCompositeImageActor.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + + // Create an ImageActor for holding final result, i.e. the blurred image. This will get rendered to screen later, via default / user render task + mTargetImageActor = ImageActor::New(); + mTargetImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mTargetImageActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as BloomView object + mTargetImageActor.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + + + // Create the Gaussian Blur object + render tasks + // Note that we use mBloomExtractTarget as the source image and also re-use this as the gaussian blur final render target. This saves the gaussian blur code from creating it + // render targets etc internally, so we make better use of resources + // Note, this also internally creates the render tasks used by the Gaussian blur, this must occur after the bloom extraction and before the compositing + mGaussianBlurView = Dali::Toolkit::GaussianBlurView::New(mBlurNumSamples, mBlurBellCurveWidth, mPixelFormat, mDownsampleWidthScale, mDownsampleHeightScale, true); + mGaussianBlurView.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + + ////////////////////////////////////////////////////// + // Create cameras for the renders corresponding to the (potentially downsampled) render targets' size + mRenderDownsampledCamera = CameraActor::New(); + mRenderDownsampledCamera.SetParentOrigin(ParentOrigin::CENTER); + + mRenderFullSizeCamera = CameraActor::New(); + mRenderFullSizeCamera.SetParentOrigin(ParentOrigin::CENTER); + + + //////////////////////////////// + // Connect to actor tree + Self().Add( mChildrenRoot ); + Self().Add( mBloomExtractImageActor ); + Self().Add( mGaussianBlurView ); + Self().Add( mCompositeImageActor ); + Self().Add( mTargetImageActor ); + Self().Add( mRenderDownsampledCamera ); + Self().Add( mRenderFullSizeCamera ); + + // bind properties for / set shader constants to defaults + SetupProperties(); +} + +/** + * ZrelativeToYconstraint + * + * f(current, property, scale) = Vector3(current.x, current.y, property.y * scale) + */ +struct ZrelativeToYconstraint +{ + ZrelativeToYconstraint( float scale ) + : mScale( scale ) + {} + + Vector3 operator()(const Vector3& current, + const PropertyInput& property) + { + Vector3 v; + + v.x = current.x; + v.y = current.y; + v.z = property.GetVector3().y * mScale; + + return v; + } + + float mScale; +}; + +void BloomView::OnControlSizeSet(const Vector3& targetSize) +{ + mTargetSize = Vector2(targetSize); + + // if we are already on stage, need to update render target sizes now to reflect the new size of this actor + if(Self().OnStage()) + { + AllocateResources(); + } +} + +void BloomView::AllocateResources() +{ + // size of render targets etc is based on the size of this actor, ignoring z + if(mTargetSize != mLastSize) + { + mLastSize = mTargetSize; + + // get size of downsampled render targets + mDownsampledWidth = mTargetSize.width * mDownsampleWidthScale; + mDownsampledHeight = mTargetSize.height * mDownsampleHeightScale; + + + ////////////////////////////////////////////////////// + // Create cameras + + // Create and place a camera for the renders corresponding to the (potentially downsampled) render targets' size + mRenderDownsampledCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it + mRenderDownsampledCamera.SetNearClippingPlane(1.0f); + mRenderDownsampledCamera.SetAspectRatio(mDownsampledWidth / mDownsampledHeight); + mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mRenderDownsampledCamera.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); // Rotate to look at origin + + mRenderDownsampledCamera.SetPosition(0.0f, 0.0f, ((mDownsampledHeight * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))); + + // Create and place a camera for the children render, corresponding to its render target size + mRenderFullSizeCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it + mRenderFullSizeCamera.SetNearClippingPlane(1.0f); + mRenderFullSizeCamera.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mRenderFullSizeCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mRenderFullSizeCamera.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); // Rotate to look at origin + + float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f); + mRenderFullSizeCamera.SetPosition(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale); + + + // Children render camera must move when GaussianBlurView object is + // resized. This is since we cannot change render target size - so we need + // to remap the child actors' rendering accordingly so they still exactly + // fill the render target. Note that this means the effective resolution of + // the child render changes as the GaussianBlurView object changes size, + // this is the trade off for not being able to modify render target size + // Change camera z position based on GaussianBlurView actor height + + mRenderFullSizeCamera.RemoveConstraints(); + mRenderFullSizeCamera.ApplyConstraint( Constraint::New( Actor::POSITION, ParentSource( Actor::SIZE ), ZrelativeToYconstraint(cameraPosConstraintScale) ) ); + + + ////////////////////////////////////////////////////// + // Pass size change onto GaussianBlurView, so it matches + mGaussianBlurView.SetSize(mTargetSize); + GetImpl(mGaussianBlurView).AllocateResources(); + + + ////////////////////////////////////////////////////// + // Create render targets + + // create off screen buffer of new size to render our child actors to + mRenderTargetForRenderingChildren = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Dali::Image::Unused ); + mBloomExtractTarget = FrameBufferImage::New( mDownsampledWidth, mDownsampledHeight, mPixelFormat, Dali::Image::Unused ); + mOutputRenderTarget = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Dali::Image::Unused ); + + + ////////////////////////////////////////////////////// + // Point actors and render tasks at new render targets + + mBloomExtractImageActor.SetImage( mRenderTargetForRenderingChildren ); + mBloomExtractImageActor.SetSize(mDownsampledWidth, mDownsampledHeight); // size needs to match render target + + // set GaussianBlurView to blur our extracted bloom + mGaussianBlurView.SetUserImageAndOutputRenderTarget(mBloomExtractTarget, mBloomExtractTarget); + + // use the completed blur in the first buffer and composite with the original child actors render + mCompositeImageActor.SetImage( mRenderTargetForRenderingChildren ); + mCompositeShader.SetEffectImage( mBloomExtractTarget ); + + // set up target actor for rendering result, i.e. the blurred image + mTargetImageActor.SetImage(mOutputRenderTarget); + } +} + +void BloomView::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + // create render task to render our child actors to offscreen buffer + mRenderChildrenTask = taskList.CreateTask(); + mRenderChildrenTask.SetSourceActor( mChildrenRoot ); + mRenderChildrenTask.SetExclusive(true); + mRenderChildrenTask.SetInputEnabled( false ); + mRenderChildrenTask.SetClearEnabled( true ); + + // Extract the bright part of the image and render to a new buffer. Downsampling also occurs at this stage to save pixel fill, if it is set up. + mBloomExtractTask = taskList.CreateTask(); + mBloomExtractTask.SetSourceActor( mBloomExtractImageActor ); + mBloomExtractTask.SetExclusive(true); + mBloomExtractTask.SetInputEnabled( false ); + mBloomExtractTask.SetClearEnabled( true ); + + // GaussianBlurView tasks must be created here, so they are executed in the correct order with respect to BloomView tasks + GetImpl(mGaussianBlurView).CreateRenderTasks(); + + // Use an image actor displaying the children render and composite it with the blurred bloom buffer, targeting the output + mCompositeTask = taskList.CreateTask(); + mCompositeTask.SetSourceActor( mCompositeImageActor ); + mCompositeTask.SetExclusive(true); + mCompositeTask.SetInputEnabled( false ); + mCompositeTask.SetClearEnabled( true ); + + mRenderChildrenTask.SetCameraActor(mRenderFullSizeCamera); // use camera that covers render target exactly + mBloomExtractTask.SetCameraActor(mRenderDownsampledCamera); + mCompositeTask.SetCameraActor(mRenderFullSizeCamera); + + mRenderChildrenTask.SetTargetFrameBuffer( mRenderTargetForRenderingChildren ); + mBloomExtractTask.SetTargetFrameBuffer( mBloomExtractTarget ); + mCompositeTask.SetTargetFrameBuffer( mOutputRenderTarget ); +} + +void BloomView::RemoveRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + taskList.RemoveTask(mRenderChildrenTask); + taskList.RemoveTask(mBloomExtractTask); + + GetImpl(mGaussianBlurView).RemoveRenderTasks(); + + taskList.RemoveTask(mCompositeTask); +} + +void BloomView::OnStageDisconnection() +{ + // TODO: can't call this here, since SetImage() calls fails similarly to above + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Deactivate(); +} + +void BloomView::OnControlStageConnection() +{ + // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Activate(); +} + +void BloomView::Activate() +{ + // make sure resources are allocated and start the render tasks processing + AllocateResources(); + CreateRenderTasks(); +} + +void BloomView::Deactivate() +{ + // stop render tasks processing + // Note: render target resources are automatically freed since we set the Image::Unused flag + RemoveRenderTasks(); +} + +/** + * EqualToConstraintFloat + * + * f(current, property) = property + */ +struct EqualToConstraintFloat +{ + EqualToConstraintFloat(){} + + float operator()(const float current, const PropertyInput& property) {return property.GetFloat();} +}; + +/** + * RecipOneMinusConstraint + * + * f(current, property) = property + */ +struct RecipOneMinusConstraint +{ + RecipOneMinusConstraint(){} + + float operator()(const float current, const PropertyInput& property) + { + return 1.0f / (1.0f - property.GetFloat()); + } +}; + +// create properties and constraints to tie internal shader etc settings to BloomView object. User can therefore animate / set them via BloomView object without knowing about +// internal implementation classes +void BloomView::SetupProperties() +{ + CustomActor self = Self(); + + + /////////////////////////////////////////// + // bloom threshold + + // set defaults, makes sure properties are registered with shader + mBloomExtractShader.SetUniform( BLOOM_THRESHOLD_PROPERTY_NAME, BLOOM_THRESHOLD_DEFAULT ); + mBloomExtractShader.SetUniform( RECIP_ONE_MINUS_BLOOM_THRESHOLD_PROPERTY_NAME, 1.0f / (1.0f - BLOOM_THRESHOLD_DEFAULT) ); + + // Register a property that the user can control to change the bloom threshold + mBloomThresholdPropertyIndex = self.RegisterProperty(BLOOM_THRESHOLD_PROPERTY_NAME, BLOOM_THRESHOLD_DEFAULT); + Property::Index shaderBloomThresholdPropertyIndex = mBloomExtractShader.GetPropertyIndex(BLOOM_THRESHOLD_PROPERTY_NAME); + Constraint bloomThresholdConstraint = Constraint::New(shaderBloomThresholdPropertyIndex, Source(self, mBloomThresholdPropertyIndex), EqualToConstraintFloat()); + mBloomExtractShader.ApplyConstraint(bloomThresholdConstraint); + + // precalc 1.0 / (1.0 - threshold) on CPU to save shader insns, using constraint to tie to the normal threshold property + Property::Index shaderRecipOneMinusBloomThresholdPropertyIndex = mBloomExtractShader.GetPropertyIndex(RECIP_ONE_MINUS_BLOOM_THRESHOLD_PROPERTY_NAME); + Constraint thresholdConstraint = Constraint::New( shaderRecipOneMinusBloomThresholdPropertyIndex, LocalSource(shaderBloomThresholdPropertyIndex), RecipOneMinusConstraint()); + mBloomExtractShader.ApplyConstraint(thresholdConstraint); + + + //////////////////////////////////////////// + // bloom strength + + // Register a property that the user can control to fade the blur in / out via internal GaussianBlurView object + mBlurStrengthPropertyIndex = self.RegisterProperty(BLOOM_BLUR_STRENGTH_PROPERTY_NAME, BLOOM_BLUR_STRENGTH_DEFAULT); + Constraint blurStrengthConstraint = Constraint::New( mGaussianBlurView.GetBlurStrengthPropertyIndex(), Source(self, mBlurStrengthPropertyIndex), EqualToConstraintFloat()); + mGaussianBlurView.ApplyConstraint(blurStrengthConstraint); + + + //////////////////////////////////////////// + // bloom intensity + + // Register a property that the user can control to fade the bloom intensity via internally hidden shader + mBloomIntensityPropertyIndex = self.RegisterProperty(BLOOM_INTENSITY_PROPERTY_NAME, BLOOM_INTENSITY_DEFAULT); + mCompositeShader.SetUniform( BLOOM_INTENSITY_PROPERTY_NAME, BLOOM_INTENSITY_DEFAULT ); + Property::Index shaderBloomIntensityPropertyIndex = mCompositeShader.GetPropertyIndex(BLOOM_INTENSITY_PROPERTY_NAME); + Constraint bloomIntensityConstraint = Constraint::New( shaderBloomIntensityPropertyIndex, Source(self, mBloomIntensityPropertyIndex), EqualToConstraintFloat()); + mCompositeShader.ApplyConstraint(bloomIntensityConstraint); + + + //////////////////////////////////////////// + // bloom saturation + + // Register a property that the user can control to fade the bloom saturation via internally hidden shader + mBloomSaturationPropertyIndex = self.RegisterProperty(BLOOM_SATURATION_PROPERTY_NAME, BLOOM_SATURATION_DEFAULT); + mCompositeShader.SetUniform( BLOOM_SATURATION_PROPERTY_NAME, BLOOM_SATURATION_DEFAULT ); + Property::Index shaderBloomSaturationPropertyIndex = mCompositeShader.GetPropertyIndex(BLOOM_SATURATION_PROPERTY_NAME); + Constraint bloomSaturationConstraint = Constraint::New( shaderBloomSaturationPropertyIndex, Source(self, mBloomSaturationPropertyIndex), EqualToConstraintFloat()); + mCompositeShader.ApplyConstraint(bloomSaturationConstraint); + + + //////////////////////////////////////////// + // image intensity + + // Register a property that the user can control to fade the image intensity via internally hidden shader + mImageIntensityPropertyIndex = self.RegisterProperty(IMAGE_INTENSITY_PROPERTY_NAME, IMAGE_INTENSITY_DEFAULT); + mCompositeShader.SetUniform( IMAGE_INTENSITY_PROPERTY_NAME, IMAGE_INTENSITY_DEFAULT ); + Property::Index shaderImageIntensityPropertyIndex = mCompositeShader.GetPropertyIndex(IMAGE_INTENSITY_PROPERTY_NAME); + Constraint imageIntensityConstraint = Constraint::New( shaderImageIntensityPropertyIndex, Source(self, mImageIntensityPropertyIndex), EqualToConstraintFloat()); + mCompositeShader.ApplyConstraint(imageIntensityConstraint); + + + //////////////////////////////////////////// + // image saturation + + // Register a property that the user can control to fade the image saturation via internally hidden shader + mImageSaturationPropertyIndex = self.RegisterProperty(IMAGE_SATURATION_PROPERTY_NAME, IMAGE_SATURATION_DEFAULT); + mCompositeShader.SetUniform( IMAGE_SATURATION_PROPERTY_NAME, IMAGE_SATURATION_DEFAULT ); + Property::Index shaderImageSaturationPropertyIndex = mCompositeShader.GetPropertyIndex(IMAGE_SATURATION_PROPERTY_NAME); + Constraint imageSaturationConstraint = Constraint::New( shaderImageSaturationPropertyIndex, Source(self, mImageSaturationPropertyIndex), EqualToConstraintFloat()); + mCompositeShader.ApplyConstraint(imageSaturationConstraint); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.h b/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.h new file mode 100644 index 0000000..72b0385 --- /dev/null +++ b/dali-toolkit/internal/controls/bloom-view/bloom-view-impl.h @@ -0,0 +1,191 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BLOOM_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_BLOOM_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class BloomView; + +namespace Internal +{ + +/** + * BloomEffect implementation class + */ +class BloomView : public ControlImpl +{ +public: + /** + * @copydoc Dali::Toolkit::BloomView::BloomView + */ + BloomView(); + + /** + * @copydoc Dali::Toolkit::BloomView::BloomView + */ + BloomView(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale); + + /** + * @copydoc Dali::Toolkit::BloomView::~BloomView + */ + virtual ~BloomView(); + + /** + * @copydoc Dali::Toolkit::BloomView::New + */ + static Dali::Toolkit::BloomView New(); + static Dali::Toolkit::BloomView New( const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale); + + void Add(Actor child); + void Remove(Actor child); + + void Activate(); + void Deactivate(); + + Property::Index GetBloomThresholdPropertyIndex() const {return mBloomThresholdPropertyIndex;} + Property::Index GetBlurStrengthPropertyIndex() const {return mBlurStrengthPropertyIndex;} + Property::Index GetBloomIntensityPropertyIndex() const {return mBloomIntensityPropertyIndex;} + Property::Index GetBloomSaturationPropertyIndex() const {return mBloomSaturationPropertyIndex;} + Property::Index GetImageIntensityPropertyIndex() const {return mImageIntensityPropertyIndex;} + Property::Index GetImageSaturationPropertyIndex() const {return mImageSaturationPropertyIndex;} + +private: + + virtual void OnInitialize(); + virtual void OnControlSizeSet(const Vector3& targetSize); + virtual void OnStageDisconnection(); + + virtual void OnControlStageConnection(); + + void AllocateResources(); + void CreateRenderTasks(); + void RemoveRenderTasks(); + + void SetupProperties(); + + + ///////////////////////////////////////////////////////////// + unsigned int mBlurNumSamples; // number of blur samples in each of horiz/vert directions + float mBlurBellCurveWidth; // constant used when calculating the gaussian weights + Pixel::Format mPixelFormat; // pixel format used by render targets + + ///////////////////////////////////////////////////////////// + // downsampling is used for the separated blur passes to get increased blur with the same number of samples and also to make rendering quicker + float mDownsampleWidthScale; + float mDownsampleHeightScale; + float mDownsampledWidth; + float mDownsampledHeight; + + + ///////////////////////////////////////////////////////////// + // for checking if we need to reallocate render targets + Vector2 mTargetSize; + Vector2 mLastSize; + + ///////////////////////////////////////////////////////////// + // for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks + Actor mChildrenRoot; + + ///////////////////////////////////////////////////////////// + // for mapping offscreen renders to render target sizes + CameraActor mRenderFullSizeCamera; + CameraActor mRenderDownsampledCamera; + + ///////////////////////////////////////////////////////////// + // for rendering all user added children to offscreen target + FrameBufferImage mRenderTargetForRenderingChildren; + RenderTask mRenderChildrenTask; + + ///////////////////////////////////////////////////////////// + // for extracting bright parts of image to an offscreen target + FrameBufferImage mBloomExtractTarget; // for rendering bright parts of image into separate texture, also used as target for gaussian blur + RenderTask mBloomExtractTask; + ShaderEffect mBloomExtractShader; + ImageActor mBloomExtractImageActor; + + ///////////////////////////////////////////////////////////// + // for blurring extracted bloom + Dali::Toolkit::GaussianBlurView mGaussianBlurView; + + ///////////////////////////////////////////////////////////// + // for compositing bloom and children renders to offscreen target + RenderTask mCompositeTask; + ShaderEffect mCompositeShader; + ImageActor mCompositeImageActor; + + ///////////////////////////////////////////////////////////// + // for holding blurred result + FrameBufferImage mOutputRenderTarget; + ImageActor mTargetImageActor; + + ///////////////////////////////////////////////////////////// + // Properties for setting by user, e.g. by animations + Property::Index mBloomThresholdPropertyIndex; + Property::Index mBlurStrengthPropertyIndex; + Property::Index mBloomIntensityPropertyIndex; + Property::Index mBloomSaturationPropertyIndex; + Property::Index mImageIntensityPropertyIndex; + Property::Index mImageSaturationPropertyIndex; + +private: + + // Undefined copy constructor. + BloomView( const BloomView& ); + + // Undefined assignment operator. + BloomView& operator=( const BloomView& ); +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods +inline Toolkit::Internal::BloomView& GetImpl( Toolkit::BloomView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +inline const Toolkit::Internal::BloomView& GetImpl( const Toolkit::BloomView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + const Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_BLOOM_VIEW_H__ diff --git a/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.cpp b/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.cpp new file mode 100644 index 0000000..5f88652 --- /dev/null +++ b/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.cpp @@ -0,0 +1,393 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "bubble-emitter-impl.h" + +//EXTERNAL INCLUDES +#include + +//INTERNAL INCLUDES +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +BubbleEmitter::BubbleEmitter( const Vector2& movementArea, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ) +: ControlImpl( true ), + mMovementArea( movementArea ), + mShapeImage( shapeImage ), + mTotalNumOfBubble( maximumNumberOfBubble ), + mBubbleSizeRange( bubbleSizeRange ), + mCurrentUniform( 0 ), + mDensity( 5 ) +{ +} + +BubbleEmitter::~BubbleEmitter() +{ +} + +Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ) +{ + // Create the implementation + IntrusivePtr internalBubbleEmitter ( new BubbleEmitter( winSize, shapeImage, + maximumNumberOfBubble,bubbleSizeRange ) ); + + // Pass ownership to Toolkit::BubbleEmitter handle + Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter ); + + //Second phase of implementeation : Initialization + internalBubbleEmitter->OnInitialize(); + + return bubbleEmitter; +} + +void BubbleEmitter::OnInitialize() +{ + // Create the root actor, all the meshActor should be its children + mBubbleRoot = Actor::New(); + mBubbleRoot.SetSize(mMovementArea); + + // Prepare the frame buffer to store the color adjusted background image + mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::Unused ); + + // Generate the material object, which is used by all meshActors + GenMaterial(); + + // Calculate how many BubbleEffect shaders are required + if( mTotalNumOfBubble>100 ) + { + mNumBubblePerShader = 100; + mNumShader = mTotalNumOfBubble / 100; + } + else + { + mNumBubblePerShader = mTotalNumOfBubble; + mNumShader = 1; + } + + mMesh.resize( mNumShader ); + mMeshActor.resize( mNumShader ); + mEffect.resize( mNumShader ); + + // Create the meshActor group and bubbleEffect group to emit bubbles following the given track, such as finger touch track. + MeshData meshData; + ConstructBubbleMesh( meshData, mNumBubblePerShader*mDensity); + for(unsigned int i=0; i < mNumShader; i++ ) + { + mMesh[i] = Mesh::New( meshData ); + mMeshActor[i] = MeshActor::New( mMesh[i] ); + mMeshActor[i].SetAffectedByLighting( false ); + mMeshActor[i].SetParentOrigin(ParentOrigin::TOP_LEFT); + mEffect[i] = BubbleEffect::New( mNumBubblePerShader, mShapeImage.GetFilename() ); + mEffect[i].SetEffectImage( mEffectImage ); + mEffect[i].SetMovementArea( mMovementArea ); + mMeshActor[i].SetShaderEffect( mEffect[i] ); + mBubbleRoot.Add( mMeshActor[i] ); + } + + // Create the extra meshActor and bubbleEffect to emit bubbles in totally random angle. + MeshData meshDataForNoise; + ConstructBubbleMesh( meshDataForNoise, mNumBubblePerShader); + mMeshActorForNoise = MeshActor::New( Mesh::New(meshDataForNoise) ); + mMeshActorForNoise.SetAffectedByLighting( false ); + mMeshActorForNoise.SetParentOrigin(ParentOrigin::TOP_LEFT); + mEffectForNoise = BubbleEffect::New( mNumBubblePerShader, mShapeImage.GetFilename() ); + mEffectForNoise.SetMovementArea( mMovementArea ); + mEffectForNoise.SetEffectImage( mEffectImage ); + mMeshActorForNoise.SetShaderEffect( mEffectForNoise ); + mBubbleRoot.Add( mMeshActorForNoise ); + + // Create a cameraActor for the off screen render task. + mCameraActor = CameraActor::New(mMovementArea); + mCameraActor.SetParentOrigin(ParentOrigin::CENTER); + mCameraActor.SetInvertYAxis(false); + Stage::GetCurrent().Add(mCameraActor); +} + +Actor BubbleEmitter::GetRootActor() +{ + return mBubbleRoot; +} + +void BubbleEmitter::SetBackground( Image bgImage, const Vector3& hsvDelta ) +{ + ImageActor sourceActor = ImageActor::New( bgImage ); + sourceActor.SetSize( mMovementArea ); + sourceActor.SetParentOrigin(ParentOrigin::CENTER); + Stage::GetCurrent().Add( sourceActor ); + + ColorAdjuster colorAdjuster = ColorAdjuster::New( hsvDelta, true /*ignore alpha to make bubble color always*/ ); + sourceActor.SetShaderEffect( colorAdjuster ); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + RenderTask task = taskList.CreateTask(); + task.SetRefreshRate( RenderTask::REFRESH_ONCE ); + task.SetSourceActor( sourceActor ); + task.SetExclusive(true); + task.SetCameraActor(mCameraActor); + task.SetTargetFrameBuffer( mEffectImage ); + task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished); +} + +void BubbleEmitter::SetShapeImage( Image shapeImage ) +{ + mCustomMaterial.SetDiffuseTexture( shapeImage ); + + //Get pixel width of the shape + float width = Image::GetImageSize(shapeImage.GetFilename()).width; + + for(unsigned int i=0; i < mNumShader; i++ ) + { + mEffect[i].SetShapeImageWidth(width); + } + mEffectForNoise.SetShapeImageWidth(width); +} + +void BubbleEmitter::SetBubbleScale( float scale ) +{ + for(unsigned int i=0; i < mNumShader; i++ ) + { + mEffect[i].SetDynamicScale( scale ); + } + mEffectForNoise.SetDynamicScale( scale ); +} + +void BubbleEmitter::SetBubbleDensity( unsigned int density ) +{ + DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " ); + + if( density == mDensity ) + { + return; + } + else + { + mDensity = density; + MeshData meshData; + ConstructBubbleMesh( meshData, mNumBubblePerShader*mDensity); + for(unsigned int i=0; i < mNumShader; i++ ) + { + mMesh[i].UpdateMeshData(meshData); + } + } +} + +// clear the resources created for the off screen rendering +void BubbleEmitter::OnRenderFinished(RenderTask& source) +{ + Actor sourceActor = source.GetSourceActor(); + sourceActor.RemoveShaderEffect(); + Stage::GetCurrent().Remove(sourceActor); + sourceActor.Reset(); + Stage::GetCurrent().GetRenderTaskList().RemoveTask(source); +} + +void BubbleEmitter::SetBlendMode( bool enable ) +{ + if(enable) + { + for(unsigned int i=0; i < mNumShader; i++ ) + { + // linear overlay + // TODO: if BlendColor would be public api from renderable actor, then can optimize the constant color + mMeshActor[i].SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE, + BlendingFactor::ZERO, BlendingFactor::ONE); + } + mMeshActorForNoise.SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE, + BlendingFactor::ZERO, BlendingFactor::ONE); + } + else + { + for(unsigned int i=0; i < mNumShader; i++ ) + { + // using default blend func + mMeshActor[i].SetBlendFunc( BlendingFactor::SRC_ALPHA, BlendingFactor::ONE_MINUS_SRC_ALPHA, + BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA ); + } + mMeshActorForNoise.SetBlendFunc( BlendingFactor::SRC_ALPHA, BlendingFactor::ONE_MINUS_SRC_ALPHA, + BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA ); + } +} + +void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ) +{ + unsigned int curUniform = mCurrentUniform % mNumBubblePerShader; + unsigned int groupIdx = mCurrentUniform / mNumBubblePerShader; + SetBubbleParameter( mEffect[groupIdx], curUniform, emitPosition, direction, displacement); + animation.AnimateTo( Property( mEffect[groupIdx], mEffect[groupIdx].GetPercentagePropertyName(curUniform) ), + 1.f, AlphaFunctions::Linear ); + + if( mCurrentUniform % mNumShader == 0 ) + { + unsigned int uniform = mCurrentUniform / mNumShader; + SetBubbleParameter(mEffectForNoise, uniform, emitPosition, displacement); + animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetPercentagePropertyName(uniform) ), + 1.f, AlphaFunctions::Linear ); + } + + mCurrentUniform = (mCurrentUniform + 1) % mTotalNumOfBubble; +} + +void BubbleEmitter::StartExplosion( float duration, float multiple ) +{ + Animation animation = Animation::New( duration ); + for(unsigned int i=0; i < mNumShader; i++ ) + { + animation.AnimateTo( Property( mEffect[i], mEffect[i].GetMagnificationPropertyName() ), + multiple, AlphaFunctions::EaseOut); + } + animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetMagnificationPropertyName() ), + multiple, AlphaFunctions::EaseOut); + animation.Play(); + + animation.FinishedSignal().Connect(this, &BubbleEmitter::OnExplosionFinished); +} + +void BubbleEmitter::Restore() +{ + for(unsigned int i=0; i < mNumShader; i++ ) + { + mEffect[i].ResetParameters(); + } + mEffectForNoise.ResetParameters(); +} + +void BubbleEmitter::GenMaterial() +{ + mCustomMaterial = Material::New("CustomMaterial"); + mCustomMaterial.SetOpacity(1.0f); + mCustomMaterial.SetDiffuseColor(Color::WHITE); + mCustomMaterial.SetAmbientColor(Vector4(0.0, 0.1, 0.1, 1.0)); + mCustomMaterial.SetMapU( Material::MAPPING_MODE_WRAP ); + mCustomMaterial.SetMapV( Material::MAPPING_MODE_WRAP ); + mCustomMaterial.SetDiffuseTexture( mShapeImage ); +} + +void BubbleEmitter::AddVertex(MeshData::VertexContainer& vertices, Vector3 XYZ, Vector2 UV) +{ + MeshData::Vertex meshVertex; + meshVertex.x = XYZ.x; + meshVertex.y = XYZ.y; + meshVertex.z = XYZ.z; + meshVertex.u = UV.x; + meshVertex.v = UV.y; + vertices.push_back(meshVertex); +} + +void BubbleEmitter::AddTriangle(MeshData::FaceIndices& faces, +size_t v0, size_t v1, size_t v2) +{ + faces.push_back(v0); + faces.push_back(v1); + faces.push_back(v2); +} + +void BubbleEmitter::ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble) +{ + MeshData::VertexContainer vertices; + MeshData::FaceIndices faces; + BoneContainer bones(0); + + for(unsigned int index = 0; index < numOfBubble; index ++) + { + float curSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y); + if(rand()%100 < 1) + { + curSize *= 2.f; + } + float depth = static_cast( index ); + AddVertex( vertices, Vector3(0.f,0.f,depth), Vector2(0.f,0.f) ); + AddVertex( vertices, Vector3(0.f,curSize,depth), Vector2( 0.f,1.f )); + AddVertex( vertices, Vector3(curSize,curSize,depth), Vector2(1.f,1.f) ); + AddVertex( vertices, Vector3(curSize,0.f,depth), Vector2(1.f,0.f) ); + + unsigned int idx = index * 4; + AddTriangle( faces, idx, idx+1, idx+2); + AddTriangle( faces, idx, idx+2, idx+3); + } + + meshData.SetData(vertices, faces, bones, mCustomMaterial); + meshData.SetHasColor(false); + meshData.SetHasTextureCoords(true); +} + +void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform, + const Vector2& emitPosition, const Vector2& displacement ) +{ + int halfRange = displacement.x / 2; + Vector2 randomVec(rand()%static_cast(displacement.x) - halfRange, rand()%static_cast(displacement.y) - halfRange); + if(randomVec.y > 0.0f) + { + randomVec.y *= 0.33f; + } + + Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y ); + effect.SetStartAndEndPosition( curUniform, startAndEndPos ); + + effect.SetPercentage( curUniform, 0.f); +} + +void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform, + const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ) +{ + Vector2 dir(direction); + + int halfRange = displacement.x / 2; + // for the y coordinate, always negative, so bubbles always go upwards + Vector2 randomVec(rand()%static_cast(displacement.x) - halfRange, -rand()%static_cast(displacement.y)); + dir.Normalize(); + randomVec.x -= dir.x*halfRange; + randomVec.y *= 1.0f - fabsf(dir.x)*0.33f; + + if(randomVec.y > 0.0f) + { + randomVec.y *= 0.33f; + } + Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y ); + effect.SetStartAndEndPosition( curUniform, startAndEndPos ); + + effect.SetPercentage( curUniform, 0.f); +} + +void BubbleEmitter::OnExplosionFinished( Animation& source ) +{ + Restore(); +} + +float BubbleEmitter::RandomRange(float f0, float f1) +{ + return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.h b/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.h new file mode 100644 index 0000000..5a42bc7 --- /dev/null +++ b/dali-toolkit/internal/controls/bubble-effect/bubble-emitter-impl.h @@ -0,0 +1,241 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUBBLE_EMITTER_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_BUBBLE_EMITTER_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * BubbleEmitter implementation class. + */ +class BubbleEmitter : public ControlImpl +{ +public: + + /** + * Destructor + */ + ~BubbleEmitter(); + + /** + * @copydoc Toolkit::BubbleEmitter::New + */ + static Toolkit::BubbleEmitter New( const Vector2& winSize, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ); + + /** + * @copydoc Toolkit::BubbleEmitter::GetRootActor + */ + Actor GetRootActor(); + + /** + * @copydoc Toolkit::BubbleEmitter::SetBackground + */ + void SetBackground( Image bgImage, const Vector3& hsvDelta ); + + /** + * @copydoc Toolkit::BubbleEmitter::SetShapeImage + */ + void SetShapeImage( Image shapeImage ); + + /** + * @copydoc Toolkit::BubbleEmiter::SetBubbleScale + */ + void SetBubbleScale( float scale ); + + /** + * @copydoc Toolkit::BubbleEmitter::SetBubbleDensity + */ + void SetBubbleDensity( unsigned int density ); + + /** + * @copydoc Toolkit::BubbleEmitter::SetBlendMode + */ + void SetBlendMode( bool enable ); + + /** + * @copydoc Toolkit::BubbleEmitter::EmitBubble + */ + void EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ); + + /** + * @copydoc Toolkit::BubbleEmitter::StartExplosion + */ + void StartExplosion( float duration, float multiple ); + + /** + * @copydoc Toolkit::BubbleEmitter::Restore + */ + void Restore(); + +private: + + /** + * Construct a new BubbleEmitter object. + * @param[in] movementArea The size of the bubble moving area + * @param[in] shapeImage The alpha channnel of this texture defines the bubble shape. + * @param[in] maximumNumberOfBubble The maximum number of bubble needed. + * @param[in] bubbleSizeRange The size range of the bubbles; x component is the minimal size, and y component is the maximum size. + */ + BubbleEmitter( const Vector2& movementArea, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ); + + /** + * This method is called after the CubeTransitionEffect has been initialized. + * The meshActors and BubbleEffects are created here. + */ + void OnInitialize(); + + /** + * Callback function of the finished signal of off-screen render task. + * @param[in] source The render task used to create the color adjusted background image. + */ + void OnRenderFinished(RenderTask& source); + + /** + * Generate the material object which is attached to the meshActor to describe its color, texture, texture mapping mode etc. + */ + void GenMaterial(); + + /** + * Add a vertex to the mesh data. + * @param[in] vertices The collection of vertices. + * @param[in] XYZ The vertex position coordinates. + * @param[in] UV The vertex texture coordinate. + */ + void AddVertex(MeshData::VertexContainer& vertices, Vector3 XYZ, Vector2 UV); + + /** + * Add a triangle to the mesh data. + * @param[in] faces The collection od FaceIndex items. + * @param[in] v0 The index of the first point of the triangle. + * @param[in] v1 The index of the second point of the triangle. + * @param[in] v3 The index of the first point of the triangle. + */ + void AddTriangle(MeshData::FaceIndices& faces,size_t v0, size_t v1, size_t v2); + + /** + * Create a new mesh. + * @param[in] meshData The MeshData object which encompasses all the data required to describe and render the 3D mesh. + * @param[in] numberOfBubble The triangle number in the meshData is 2*numOfBubble; two triangles for each bubble + */ + void ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble); + + /** + * Set the uniform values to the shader effect to emit a bubble + * @param[in] effect The BubbleEffect to render the current bubble + * @param[in] curUniform The index of the uniform array in the shader + * @param[in] emitPosition The start position of the bubble movement. + * @param[in] displacement The displacement used to bound the moving distance of the bubble. + */ + void SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform, + const Vector2& emitPosition, const Vector2& displacement ); + + /** + * Set the uniform values to the shader effect to emit a bubble + * @param[in] effect The BubbleEffect to render the current bubble + * @param[in] curUniform The index of the uniform array in the shader + * @param[in] emitPosition The start position of the bubble movement. + * @param[in] direction The direction used to constrain the bubble to move in an adjacent direction around it. + * @param[in] displacement The displacement used to bound the moving distance of the bubble. + */ + void SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform, + const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ); + + /** + * Callback function of the explosion animation finished signal to reset the shader parameters + * @param[in] source The explosion animation. + */ + void OnExplosionFinished( Animation& source ); + + /** + * Return a random value between the given interval. + * @param[in] f0 The low bound + * @param[in] f1 The up bound + * @return A random value between the given interval + */ + float RandomRange(float f0, float f1); + +private: + + Vector2 mMovementArea; ///< The size of the bubble moving area, usually the same size as the background image actor. + Image mShapeImage; ///< The alpha channnel of this texture defines the bubble shape. + Actor mBubbleRoot; /// mMesh; ///< The mesh vector, each mesh is used to create a meshActor which applies a BubbleEffect. + std::vector mMeshActor; ///< The meshActor vector, its size is mNumShader. + MeshActor mMeshActorForNoise; ///< An Extra mesh data to emit bubbles which emit bubble in totally random angle. + Material mCustomMaterial; ///< The material object which is attached to the meshActor to describe its color, texture, texture mapping mode etc. + + std::vector mEffect; ///< The bubbleEffect vector, corresponding to the mMeshActoe vector. + BubbleEffect mEffectForNoise; ///< The extra bubbleEffect, corresponding to the mMeshActorForNoise. + + unsigned int mCurrentUniform; ///< Keep track of the uniform index for the newly emitted bubble + + FrameBufferImage mEffectImage; ///< The image stores the adjusted color of the background image.The bubbles pick color from this image. + CameraActor mCameraActor; ///< The render task views the scene from the perspective of this actor. + + unsigned int mDensity; ///< How many bubbles will emit at each time, they are controlled by same uniforms in the shader. + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods +inline Internal::BubbleEmitter& GetImpl(Dali::Toolkit::BubbleEmitter& obj) +{ + DALI_ASSERT_ALWAYS(obj && "BubbleEmitter handle is empty"); + Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +inline const Internal::BubbleEmitter& GetImpl(const Dali::Toolkit::BubbleEmitter& obj) +{ + DALI_ASSERT_ALWAYS(obj && "BubbleEmitter handle is empty"); + const Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_BUBBLE_EMITTER_IMPL_H__ */ diff --git a/dali-toolkit/internal/controls/buttons/button-impl.cpp b/dali-toolkit/internal/controls/buttons/button-impl.cpp new file mode 100644 index 0000000..86a3f4f --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/button-impl.cpp @@ -0,0 +1,241 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "button-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace Internal +{ + +using namespace Dali; + +BaseHandle Create() +{ + // empty handle as we cannot create button (but type registered for clicked signal) + return BaseHandle(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::Button), typeid(Toolkit::Control), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::Button::SIGNAL_CLICKED, &Button::DoConnectSignal ); + +} + +Button::Button() +: ControlImpl( true ), + mState( ButtonUp ), + mDimmed( false ), + mPainter( NULL ) +{ +} + +Button::~Button() +{ +} + +void Button::SetDimmed( bool dimmed ) +{ + mDimmed = dimmed; + + // Notifies the painter. + Toolkit::Button handle( GetOwner() ); + if( mPainter ) + { + mPainter->SetDimmed( handle, mDimmed ); + } +} + +bool Button::IsDimmed() const +{ + return mDimmed; +} + +void Button::SetAnimationTime( float animationTime ) +{ + OnAnimationTimeSet( animationTime ); +} + +float Button::GetAnimationTime() const +{ + return OnAnimationTimeRequested(); +} + +void Button::OnAnimationTimeSet( float animationTime ) +{ + // nothing to do. +} + +float Button::OnAnimationTimeRequested() const +{ + return 0.f; +} + +Toolkit::Button::ClickedSignalV2& Button::ClickedSignal() +{ + return mClickedSignalV2; +} + +bool Button::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::Button button = Toolkit::Button::DownCast(handle); + + if( Dali::Toolkit::Button::SIGNAL_CLICKED == signalName ) + { + button.ClickedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +bool Button::OnTouchEvent(const TouchEvent& event) +{ + // Only events are processed when the button is not dimmed and the touch event has only + // one touch point. + if( ( !mDimmed ) && ( 1 == event.GetPointCount() ) ) + { + switch( event.GetPoint(0).state ) + { + case TouchPoint::Down: + { + OnButtonDown(); // Notification for derived classes. + + // Sets the button state to ButtonDown. + mState = ButtonDown; + break; + } + case TouchPoint::Up: + { + OnButtonUp(); // Notification for derived classes. + + // Sets the button state to ButtonUp. + mState = ButtonUp; + break; + } + case TouchPoint::Interrupted: + { + OnTouchPointInterrupted(); // Notification for derived classes. + + // Sets the button state to the default (ButtonUp). + mState = ButtonUp; + break; + } + case TouchPoint::Leave: + { + OnTouchPointLeave(); // Notification for derived classes. + + // Sets the button state to the default (ButtonUp). + mState = ButtonUp; + break; + } + case TouchPoint::Motion: + case TouchPoint::Stationary: // FALLTHROUGH + { + // Nothing to do + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"Point status unhandled." ); + break; + } + } + } + else if( 1 < event.GetPointCount() ) + { + OnTouchPointLeave(); // Notification for derived classes. + + // Sets the button state to the default (ButtonUp). + mState = ButtonUp; + } + + return false; +} + +void Button::OnInitialize() +{ + // Initialize the painter and notifies subclasses. + Toolkit::Button handle( GetOwner() ); + if( mPainter ) + { + mPainter->Initialize( handle ); + } + + mTapDetector = TapGestureDetector::New(); + mTapDetector.Attach(Self()); + mTapDetector.DetectedSignal().Connect(this, &Button::OnTap); + + OnButtonInitialize(); + + Actor self = Self(); + mPropertyDimmed = self.RegisterProperty( Dali::Toolkit::Button::PROPERTY_DIMMED, false, Property::READ_WRITE ); + + self.SetKeyboardFocusable( true ); +} + +void Button::OnControlSizeSet(const Vector3& targetSize) +{ + Toolkit::Button handle( GetOwner() ); + if( mPainter ) + { + mPainter->SetSize( handle, targetSize ); + } +} + +void Button::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + if( index == mPropertyDimmed ) + { + SetDimmed(propertyValue.Get()); + } +} + +void Button::OnTap(Actor actor, TapGesture tap) +{ + // Do nothing. +} + +void Button::OnStageDisconnection() +{ + if( ButtonUp != mState ) + { + OnTouchPointLeave(); // Notification for derived classes. + mState = ButtonUp; + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/buttons/button-impl.h b/dali-toolkit/internal/controls/buttons/button-impl.h new file mode 100644 index 0000000..8365f35 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/button-impl.h @@ -0,0 +1,243 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUTTON_H__ +#define __DALI_TOOLKIT_INTERNAL_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include "button-painter-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +class Button; + +namespace Internal +{ + +/** + * Button is the base class implementation for all buttons. + */ +class Button : public ControlImpl +{ + +protected: + + /** + * Construct a new Button. + */ + Button(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Button(); + +public: + + /** + * @copydoc Dali::Toolkit::Button::SetDimmed( bool dimmed ) + */ + void SetDimmed( bool dimmed ); + + /** + * @copydoc Dali::Toolkit::Button::IsDimmed() const + */ + bool IsDimmed() const; + + /** + * @copydoc Dali::Toolkit::Button::SetAnimationTime() + */ + void SetAnimationTime( float animationTime ); + + /** + * @copydoc Dali::Toolkit::Button::GetAnimationTime() + */ + float GetAnimationTime() const; + +private: + + /** + * This method is called after the button initialization. + * Could be reimplemented in subclasses to provide specific behaviour. + */ + virtual void OnButtonInitialize() { } + + /** + * This method is called from the OnTouchEvent method when the button is down. + * Could be reimplemented in subclasses to provide specific behaviour. + */ + virtual void OnButtonDown() { } + + /** + * This method is called from the OnTouchEvent method when the button is up. + * Could be reimplemented in subclasses to provide specific behaviour. + */ + virtual void OnButtonUp() { } + + /** + * This method is called from the OnTouchEvent method when the touch point leaves the boundary of the button or + * more than one touch points are received. + * Could be reimplemented in subclasses to provide specific behaviour. + */ + virtual void OnTouchPointLeave() { } + + /** + * This method is called from the OnTouchEvent method when the touch point is interrupted. + * Could be reimplemented in subclasses to provide specific behaviour. + */ + virtual void OnTouchPointInterrupted() { } + + /** + * This method is called when the animation time is set. + * Needs to be reimplemented in subclasses to set the animation time in different buttons. + * @param animationTime The animation time in seconds. + */ + virtual void OnAnimationTimeSet( float animationTime ); + + /** + * This method is called when the animation time is requested. + * Needs to be reimplemented in subclases to return the animation time. + * @return The animation time in seconds. + */ + virtual float OnAnimationTimeRequested() const; + +public: + + /** + * @copydoc Dali::Toolkit::Button::ClickedSignal() + */ + Toolkit::Button::ClickedSignalV2& ClickedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: // From CustomActorImpl + + /** + * @copydoc Dali::CustomActorImpl::OnTouchEvent( const TouchEvent& event ) + */ + virtual bool OnTouchEvent( const TouchEvent& event ); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnControlSizeSet( const Vector3& targetSize ) + */ + virtual void OnControlSizeSet( const Vector3& targetSize ); + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + virtual void OnPropertySet( Property::Index index, Property::Value propertyValue ); + +private: + + /** + * Handler for tap events. + * We do not actually do anything when we receive a tap as the button handles tap event through + * the touch event system itself as it requires more than just tap handling (e.g. leave events). + * This stops any of our parents receiving a tap gesture when it occurs within our area. + * @param[in] actor The tapped actor. + * @param[in] tap The tap gesture. + */ + void OnTap(Actor actor, TapGesture tap); + +private: + + /** + * Callback received when the button is disconected from the stage. + * It resets the button status. + */ + void OnStageDisconnection(); + +private: + + // Undefined + Button( const Button& ); + + // Undefined + Button& operator = ( const Button& ); + +protected: // Signals + + enum ButtonState + { + ButtonUp, ///< The button is up. + ButtonDown, ///< The button is down. + }; + + ButtonState mState; ///< Stores the button state. + + bool mDimmed; ///< Stores the dimmed property. + + ButtonPainterPtr mPainter; ///< Pointer to a ButtonPainter base class. + + Toolkit::Button::ClickedSignalV2 mClickedSignalV2; ///< Signal emitted when the button is clicked. + + TapGestureDetector mTapDetector; + + Property::Index mPropertyDimmed; ///< Property index for dimmed. +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Button& GetImplementation( Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::Button& GetImplementation( const Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + const Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_BUTTON_H__ + diff --git a/dali-toolkit/internal/controls/buttons/button-painter-impl.h b/dali-toolkit/internal/controls/buttons/button-painter-impl.h new file mode 100644 index 0000000..4626c3a --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/button-painter-impl.h @@ -0,0 +1,109 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BUTTON_PAINTER_H__ +#define __DALI_TOOLKIT_INTERNAL_BUTTON_PAINTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES + +namespace Dali +{ + +// Forward declarations + +struct Vector3; + +namespace Toolkit +{ + +// Forward declarations + +class Button; + +namespace Internal +{ + +// Forward declarations + +class ButtonPainter; + +// Type definitions + +typedef IntrusivePtr ButtonPainterPtr; + +/** + * ButtonPainter is an interface which provides common functionality to all painters. + */ +class ButtonPainter : public RefObject, public ConnectionTracker +{ +public: + /** + * Destructor. + * + */ + virtual ~ButtonPainter() {} + + /** + * Initializes the painter. + * + * This method is called from the Dali::Toolkit::Internal::Button. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + */ + virtual void Initialize( Toolkit::Button& button ) = 0; + + /** + * Sets the new size. + * + * This method is called from the Dali::Toolkit::Internal::Button when its size changes. + * + * @param[inout] button The button which stores button's images. + * @param[in] size The new size. + */ + virtual void SetSize( Toolkit::Button& button, const Vector3& size ) = 0; + + /** + * This method is called from the Dali::Toolkit::Internal::PushButton when the \e dimmed property changes. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + * @param[in] dimmed The dimmed state. + */ + virtual void SetDimmed( Toolkit::Button& button, bool dimmed ) = 0; + + /** + * Sets the animation time. + * @param [in] animationTime The animation time in seconds. + */ + virtual void SetAnimationTime( float animationTime ) = 0; + + /** + * Retrieves the animation time. + * @return The animation time in seconds. + */ + virtual float GetAnimationTime() const = 0; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_BUTTON_PAINTER_H__ diff --git a/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.cpp b/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.cpp new file mode 100644 index 0000000..9623ff4 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.cpp @@ -0,0 +1,1051 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "check-box-button-default-painter-impl.h" + +// INTERNAL INCLUDES + +#include +#include +#include "check-box-button-impl.h" + +// EXTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +const float FOREGROUND_DEPTH( 0.5f ); +const float BACKGROUND_DEPTH( 0.25f ); + +const float ANIMATION_TIME( 0.26f ); // EFL checkbox tick time + +const std::string PERCENTAGE_PARENT_SIZE_PROPERTY_NAME( "percentage-parent-size" ); + + +/** + * Constraint to wrap an actor in y that is moving vertically + */ +Vector3 EqualToPercentageWidthConstraint( const Vector3& current, + const PropertyInput& percentageProperty, + const PropertyInput& parentSizeProperty ) +{ + float percentage = percentageProperty.GetFloat(); + const Vector3& parentSize = parentSizeProperty.GetVector3(); + + Vector3 size( parentSize ); + size.x *= percentage; + + return size; +} + + +inline Toolkit::Internal::CheckBoxButton& GetCheckBoxButtonImpl( Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::CheckBoxButton& GetCheckBoxButtonImpl( const Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + const Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +} + +CheckBoxButtonDefaultPainter::CheckBoxButtonDefaultPainter() +: CheckBoxButtonPainter(), + mDimmed( false ), + mPaintState( UncheckedState ), + mButton(NULL), + mAnimationTime( ANIMATION_TIME ), + mPercentageParentSizeProperty( Property::INVALID_INDEX ) +{ +} + +CheckBoxButtonDefaultPainter::~CheckBoxButtonDefaultPainter() +{ + if( mCheckInAnimation ) + { + mCheckInAnimation.Clear(); + } + if( mCheckOutAnimation ) + { + mCheckOutAnimation.Clear(); + } +} + +void CheckBoxButtonDefaultPainter::SetBackgroundImage( Toolkit::CheckBoxButton& checkBox, Actor image ) +{ + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetImplementation( checkBox ); + Actor& backgroundImage = checkBoxImpl.GetBackgroundImage(); + Actor& fadeOutBackgroundImage = checkBoxImpl.GetFadeOutBackgroundImage(); + + switch( mPaintState ) + { + case UncheckedState: // FALLTHROUGH + case CheckedState: + case UncheckedCheckedTransition: + case CheckedUncheckedTransition: + { + if( backgroundImage && backgroundImage.GetParent() ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, backgroundImage ); + + backgroundImage = image; + + FadeInImage( checkBox, backgroundImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + } + else + { + backgroundImage = image; + checkBox.Add( backgroundImage ); + } + break; + } + case DimmedUncheckedTransition: // FALLTHROUGH + case DimmedCheckedTransition: + { + StopCheckInAnimation(); + checkBox.Remove( backgroundImage ); + + backgroundImage = image; + + FadeInImage( checkBox, backgroundImage ); + StartCheckInAnimation(); + break; + } + case CheckedDimmedTransition: // FALLTHROUGH + case UncheckedDimmedTransition: + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + + // Replaces the button image. + backgroundImage = image; + + checkBox.Add( backgroundImage ); + FadeOutImage( checkBox, Background, backgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + break; + } + default: + { + backgroundImage = image; + break; + } + } + + backgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + backgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( backgroundImage, BACKGROUND_DEPTH ); +} + +void CheckBoxButtonDefaultPainter::SetCheckedImage( Toolkit::CheckBoxButton& checkBox, Actor image ) +{ + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetImplementation( checkBox ); + Actor& checkedImage = checkBoxImpl.GetCheckedImage(); + Actor& fadeOutCheckedImage = checkBoxImpl.GetFadeOutCheckedImage(); + + switch( mPaintState ) + { + case CheckedState: + { + if( checkedImage && checkedImage.GetParent() ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Foreground, checkedImage ); + + checkedImage = image; + + FadeInImage( checkBox, checkedImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + } + else + { + checkedImage = image; + checkBox.Add( checkedImage ); + } + break; + } + case UncheckedCheckedTransition: // FALLTHROUGH + case DimmedCheckedTransition: + { + StopCheckInAnimation(); + checkBox.Remove( checkedImage ); + + checkedImage = image; + + FadeInImage( checkBox, checkedImage ); + StartCheckInAnimation(); + break; + } + case CheckedUncheckedTransition: // FALLTHROUGH + case CheckedDimmedTransition: + { + float opacity = 1.f; + if( fadeOutCheckedImage ) + { + opacity = fadeOutCheckedImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + + // Replaces the button image. + checkedImage = image; + + checkBox.Add( checkedImage ); + FadeOutImage( checkBox, Foreground, checkedImage, opacity ); + + StartCheckOutAnimation( checkBox ); + break; + } + default: + { + checkedImage = image; + break; + } + } + + checkedImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + checkedImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyCheckedConstraint( checkedImage, FOREGROUND_DEPTH ); +} + +void CheckBoxButtonDefaultPainter::SetDimmedCheckedImage( Toolkit::CheckBoxButton& checkBox, Actor image ) +{ + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetImplementation( checkBox ); + Actor& dimmedCheckedImage = checkBoxImpl.GetDimmedCheckedImage(); + Actor& fadeOutCheckedImage = checkBoxImpl.GetFadeOutCheckedImage(); + + switch( mPaintState ) + { + case DimmedCheckedState: + { + if( dimmedCheckedImage && dimmedCheckedImage.GetParent() ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Foreground, dimmedCheckedImage ); + + dimmedCheckedImage = image; + + FadeInImage( checkBox, dimmedCheckedImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + } + else + { + dimmedCheckedImage = image; + checkBox.Add( dimmedCheckedImage ); + } + break; + } + case CheckedDimmedTransition: + { + StopCheckInAnimation(); + checkBox.Remove( dimmedCheckedImage ); + + dimmedCheckedImage = image; + + FadeInImage( checkBox, dimmedCheckedImage ); + StartCheckInAnimation(); + break; + } + case DimmedCheckedTransition: + { + float opacity = 1.f; + if( fadeOutCheckedImage ) + { + opacity = fadeOutCheckedImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + + // Replaces the button image. + dimmedCheckedImage = image; + + checkBox.Add( dimmedCheckedImage ); + FadeOutImage( checkBox, Foreground, dimmedCheckedImage, opacity ); + + StartCheckOutAnimation( checkBox ); + break; + } + default: + { + dimmedCheckedImage = image; + break; + } + } + + dimmedCheckedImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + dimmedCheckedImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( dimmedCheckedImage, FOREGROUND_DEPTH ); +} + +void CheckBoxButtonDefaultPainter::SetDimmedBackgroundImage( Toolkit::CheckBoxButton& checkBox, Actor image ) +{ + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetImplementation( checkBox ); + Actor& dimmedBackgroundImage = checkBoxImpl.GetDimmedBackgroundImage(); + Actor& fadeOutBackgroundImage = checkBoxImpl.GetFadeOutBackgroundImage(); + + switch( mPaintState ) + { + case DimmedCheckedState: // FALLTHROUGH + case DimmedUncheckedState: + { + if( dimmedBackgroundImage && dimmedBackgroundImage.GetParent() ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, dimmedBackgroundImage ); + + dimmedBackgroundImage = image; + + FadeInImage( checkBox, dimmedBackgroundImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + } + else + { + dimmedBackgroundImage = image; + checkBox.Add( dimmedBackgroundImage ); + } + break; + } + case UncheckedDimmedTransition: // FALLTHROUGH + case CheckedDimmedTransition: + { + StopCheckInAnimation(); + checkBox.Remove( dimmedBackgroundImage ); + + dimmedBackgroundImage = image; + + FadeInImage( checkBox, dimmedBackgroundImage ); + StartCheckInAnimation(); + break; + } + case DimmedUncheckedTransition: // FALLTHROUGH + case DimmedCheckedTransition: + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + + // Replaces the button image. + dimmedBackgroundImage = image; + + checkBox.Add( dimmedBackgroundImage ); + FadeOutImage( checkBox, Background, dimmedBackgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + break; + } + default: + { + dimmedBackgroundImage = image; + break; + } + } + + dimmedBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + dimmedBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( dimmedBackgroundImage, BACKGROUND_DEPTH ); +} + +void CheckBoxButtonDefaultPainter::Initialize( Toolkit::Button& button ) +{ + Toolkit::Internal::CheckBoxButton& buttonImpl = GetCheckBoxButtonImpl( button ); + Actor& backgroundImage = buttonImpl.GetBackgroundImage(); + Actor& checkedImage = buttonImpl.GetCheckedImage(); + Actor& dimmedBackgroundImage = buttonImpl.GetDimmedBackgroundImage(); + Actor& dimmedCheckedImage = buttonImpl.GetDimmedCheckedImage(); + + Toolkit::CheckBoxButton& checkBox = static_cast( button ); + + if( backgroundImage ) + { + SetBackgroundImage( checkBox, backgroundImage ); + } + + if( checkedImage ) + { + SetCheckedImage( checkBox, checkedImage ); + } + + if( dimmedBackgroundImage ) + { + SetDimmedBackgroundImage( checkBox, dimmedBackgroundImage ); + } + + if( dimmedCheckedImage ) + { + SetDimmedCheckedImage( checkBox, dimmedCheckedImage ); + } + + SetDimmed( button, mDimmed ); +} + +void CheckBoxButtonDefaultPainter::SetSize( Toolkit::Button& button, const Vector3& size ) +{ + Toolkit::Internal::CheckBoxButton& buttonImpl = GetCheckBoxButtonImpl( button ); + Actor& backgroundImage = buttonImpl.GetBackgroundImage(); + Actor& checkedImage = buttonImpl.GetCheckedImage(); + Actor& dimmedBackgroundImage = buttonImpl.GetDimmedBackgroundImage(); + Actor& dimmedCheckedImage = buttonImpl.GetDimmedCheckedImage(); + + ApplyCheckedConstraint( checkedImage, FOREGROUND_DEPTH ); + ApplyConstraint( backgroundImage, BACKGROUND_DEPTH ); + ApplyConstraint( dimmedCheckedImage, FOREGROUND_DEPTH ); + ApplyConstraint( dimmedBackgroundImage, BACKGROUND_DEPTH ); +} + +void CheckBoxButtonDefaultPainter::SetDimmed( Toolkit::Button& button, bool dimmed ) +{ + mDimmed = dimmed; + + Toolkit::Internal::CheckBoxButton& buttonImpl = GetCheckBoxButtonImpl( button ); + Actor& backgroundImage = buttonImpl.GetBackgroundImage(); + Actor& checkedImage = buttonImpl.GetCheckedImage(); + Actor& dimmedBackgroundImage = buttonImpl.GetDimmedBackgroundImage(); + Actor& dimmedCheckedImage = buttonImpl.GetDimmedCheckedImage(); + Actor& fadeOutCheckedImage = buttonImpl.GetFadeOutCheckedImage(); + Actor& fadeOutBackgroundImage = buttonImpl.GetFadeOutBackgroundImage(); + + Toolkit::CheckBoxButton& checkBox = static_cast( button ); + + switch( mPaintState ) + { + case UncheckedState: + { + if( dimmed ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, backgroundImage ); + FadeInImage( checkBox, dimmedBackgroundImage ); + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = UncheckedDimmedTransition; + } + break; + } + case CheckedState: + { + if( dimmed ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, backgroundImage ); + FadeOutImage( checkBox, Foreground, checkedImage ); + FadeInImage( checkBox, dimmedCheckedImage ); + FadeInImage( checkBox, dimmedBackgroundImage ); + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = CheckedDimmedTransition; + } + break; + } + case DimmedUncheckedState: + { + if( !dimmed ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, dimmedBackgroundImage ); + FadeInImage( checkBox, backgroundImage ); + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = DimmedUncheckedTransition; + } + break; + } + case DimmedCheckedState: + { + if( !dimmed ) + { + StopCheckOutAnimation( checkBox ); + FadeOutImage( checkBox, Background, dimmedBackgroundImage ); + FadeOutImage( checkBox, Foreground, dimmedCheckedImage ); + FadeInImage( checkBox, backgroundImage ); + FadeInImage( checkBox, checkedImage ); + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = DimmedCheckedTransition; + } + break; + } + case UncheckedCheckedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( checkedImage ) + { + opacity = checkedImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + StopCheckInAnimation(); + + FadeOutImage( checkBox, Foreground, checkedImage, opacity ); + FadeOutImage( checkBox, Background, backgroundImage ); + + FadeInImage( checkBox, dimmedCheckedImage ); + FadeInImage( checkBox, dimmedBackgroundImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = CheckedDimmedTransition; + } + break; + } + case CheckedUncheckedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutCheckedImage ) + { + opacity = fadeOutCheckedImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox ); + StopCheckInAnimation(); + + button.Add( dimmedCheckedImage ); + FadeOutImage( checkBox, Foreground, dimmedCheckedImage, opacity ); + FadeOutImage( checkBox, Background, backgroundImage ); + + FadeInImage( checkBox, dimmedBackgroundImage ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = UncheckedDimmedTransition; + } + break; + } + case UncheckedDimmedTransition: + { + if( !dimmed ) + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox, false ); + StopCheckInAnimation(); + + FadeOutImage( checkBox, Background, dimmedBackgroundImage, 1.f - opacity ); + FadeInImage( checkBox, backgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = DimmedUncheckedTransition; + } + break; + } + case DimmedUncheckedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox, false ); + StopCheckInAnimation(); + + FadeOutImage( checkBox, Background, backgroundImage, 1.f - opacity ); + FadeInImage( checkBox, dimmedBackgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = UncheckedDimmedTransition; + } + break; + } + case CheckedDimmedTransition: + { + if( !dimmed ) + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox, false ); + StopCheckInAnimation(); + + FadeOutImage( checkBox, Foreground, dimmedCheckedImage, 1.f - opacity ); + FadeOutImage( checkBox, Background, dimmedBackgroundImage, 1.f - opacity ); + FadeInImage( checkBox, checkedImage, opacity ); + FadeInImage( checkBox, backgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = DimmedCheckedTransition; + } + break; + } + case DimmedCheckedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( checkBox, false ); + StopCheckInAnimation(); + + FadeOutImage( checkBox, Foreground, checkedImage, 1.f - opacity ); + FadeOutImage( checkBox, Background, backgroundImage, 1.f - opacity ); + FadeInImage( checkBox, dimmedCheckedImage, opacity ); + FadeInImage( checkBox, dimmedBackgroundImage, opacity ); + + StartCheckOutAnimation( checkBox ); + StartCheckInAnimation(); + + mPaintState = CheckedDimmedTransition; + } + break; + } + default: + break; + } +} + +void CheckBoxButtonDefaultPainter::SetAnimationTime( float animationTime ) +{ + mAnimationTime = animationTime; +} + +float CheckBoxButtonDefaultPainter::GetAnimationTime() const +{ + return mAnimationTime; +} + +void CheckBoxButtonDefaultPainter::Checked( Toolkit::CheckBoxButton& button ) +{ + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetCheckBoxButtonImpl( button ); + Actor& checkedImage = checkBoxImpl.GetCheckedImage(); + Actor& fadeOutCheckedImage = checkBoxImpl.GetFadeOutCheckedImage(); + + switch( mPaintState ) + { + case UncheckedState: + { + // Fade in the 'check' actor. + FadeInImage( button, checkedImage ); + SetupCheckedAnimation( button, checkedImage ); // Animate in the check actor + StartCheckInAnimation(); + + mPaintState = UncheckedCheckedTransition; + break; + } + case CheckedState: + { + // Fade out the 'check' actor. + StopCheckOutAnimation( button ); + FadeOutImage( button, Foreground, checkedImage ); + StartCheckOutAnimation( button ); + + if( button.GetProperty( button.GetPropertyIndex( Toolkit::CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME ) ) ) + { + mPaintState = CheckedUncheckedTransition; + } + else + { + mPaintState = UncheckedState; + } + break; + } + case UncheckedCheckedTransition: + { + // Stop fade in and start fade out. + StopCheckOutAnimation( button ); + StopCheckInAnimation(); + + float opacity = 0.f; + if( checkedImage ) + { + opacity = checkedImage.GetCurrentOpacity(); + } + FadeOutImage( button, Foreground, checkedImage, opacity ); + StartCheckOutAnimation( button ); + + if( button.GetProperty( button.GetPropertyIndex( Toolkit::CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME ) ) ) + { + mPaintState = CheckedUncheckedTransition; + } + else + { + mPaintState = UncheckedState; + } + break; + } + case CheckedUncheckedTransition: + { + // Stop fade out and start fade in. + float opacity = 1.f; + if( fadeOutCheckedImage ) + { + opacity = fadeOutCheckedImage.GetCurrentOpacity(); + } + StopCheckOutAnimation( button ); + + FadeInImage( button, checkedImage, opacity ); + StartCheckInAnimation(); + + mPaintState = UncheckedCheckedTransition; + break; + } + default: + break; + } +} + +void CheckBoxButtonDefaultPainter::ApplyConstraint( Actor& actor, float depth ) +{ + if( actor ) + { + actor.RemoveConstraints(); + actor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + actor.SetZ( depth ); + } +} + +void CheckBoxButtonDefaultPainter::ApplyCheckedConstraint( Actor& actor, float depth ) +{ + if( actor ) + { + if( mPercentageParentSizeProperty == Property::INVALID_INDEX ) + { + mPercentageParentSizeProperty = actor.RegisterProperty( PERCENTAGE_PARENT_SIZE_PROPERTY_NAME, 1.0f ); + } + + actor.RemoveConstraints(); + actor.ApplyConstraint( Constraint::New( Actor::SIZE, + LocalSource( mPercentageParentSizeProperty ), + ParentSource( Actor::SIZE ), + EqualToPercentageWidthConstraint ) ); + actor.SetZ( depth ); + } +} + +void CheckBoxButtonDefaultPainter::AddToFadeInAnimation( const Actor& actor ) +{ + if( !mCheckInAnimation ) + { + mCheckInAnimation = Dali::Animation::New( mAnimationTime ); + } + + mCheckInAnimation.OpacityTo( actor, 1.f ); + +} + +void CheckBoxButtonDefaultPainter::StartCheckInAnimation() +{ + if( mCheckInAnimation ) + { + mCheckInAnimation.FinishedSignal().Connect( this, &CheckBoxButtonDefaultPainter::CheckInAnimationFinished ); + mCheckInAnimation.Play(); + } +} + +void CheckBoxButtonDefaultPainter::StopCheckInAnimation() +{ + if( mCheckInAnimation ) + { + mCheckInAnimation.Clear(); + mCheckInAnimation.Reset(); + } +} + +void CheckBoxButtonDefaultPainter::AddToFadeOutAnimation( const Actor& actor ) +{ + if( !mCheckOutAnimation ) + { + mCheckOutAnimation = Dali::Animation::New( mAnimationTime ); + } + + mCheckOutAnimation.OpacityTo( actor, 0.f ); +} + +void CheckBoxButtonDefaultPainter::StartCheckOutAnimation( Toolkit::CheckBoxButton& checkBox ) +{ + if( mCheckOutAnimation ) + { + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetCheckBoxButtonImpl( checkBox ); + mButton = &checkBoxImpl; + + mCheckOutAnimation.FinishedSignal().Connect( this, &CheckBoxButtonDefaultPainter::CheckOutAnimationFinished ); + mCheckOutAnimation.Play(); + } +} + +void CheckBoxButtonDefaultPainter::StopCheckOutAnimation( Toolkit::CheckBoxButton& checkBox, bool remove ) +{ + if( mCheckOutAnimation ) + { + mCheckOutAnimation.Clear(); + mCheckOutAnimation.Reset(); + } + + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetCheckBoxButtonImpl( checkBox ); + Actor& fadeOutCheckedImage = checkBoxImpl.GetFadeOutCheckedImage(); + Actor& fadeOutBackgroundImage = checkBoxImpl.GetFadeOutBackgroundImage(); + + if( remove ) + { + if( fadeOutCheckedImage && fadeOutCheckedImage.GetParent() ) + { + fadeOutCheckedImage.GetParent().Remove( fadeOutCheckedImage ); + } + + if( fadeOutBackgroundImage && fadeOutBackgroundImage.GetParent() ) + { + fadeOutBackgroundImage.GetParent().Remove( fadeOutBackgroundImage ); + } + + fadeOutCheckedImage.Reset(); + fadeOutBackgroundImage.Reset(); + } +} + +void CheckBoxButtonDefaultPainter::FadeInImage( Toolkit::CheckBoxButton& checkBox, Actor& image, float opacity ) +{ + if( image ) + { + if( !image.GetParent() ) + { + checkBox.Add( image ); + } + + if( checkBox.GetProperty( checkBox.GetPropertyIndex( Toolkit::CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME ) ) ) + { + image.SetOpacity( opacity ); + AddToFadeInAnimation( image ); + } + else + { + image.SetOpacity( 1.0f ); + } + } +} + +void CheckBoxButtonDefaultPainter::FadeOutImage( Toolkit::CheckBoxButton& checkBox, ImageLayer layer, Actor& image, float opacity ) +{ + if( image ) + { + Toolkit::Internal::CheckBoxButton& checkBoxImpl = GetCheckBoxButtonImpl( checkBox ); + Actor& fadeOutCheckedImage = checkBoxImpl.GetFadeOutCheckedImage(); + Actor& fadeOutBackgroundImage = checkBoxImpl.GetFadeOutBackgroundImage(); + + Actor& actorLayer = ( ( Background == layer ) ? fadeOutBackgroundImage : fadeOutCheckedImage ); + + actorLayer = image; + + if( checkBox.GetProperty( checkBox.GetPropertyIndex( Toolkit::CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME ) ) ) + { + actorLayer.SetOpacity( opacity ); + AddToFadeOutAnimation( actorLayer ); + } + else + { + actorLayer.SetOpacity( 0.0f ); + } + } +} + +void CheckBoxButtonDefaultPainter::AddToCheckInAnimation( const Actor& actor ) +{ + if( !mCheckInAnimation ) + { + mCheckInAnimation = Dali::Animation::New( mAnimationTime ); + } + + // UV anim + mCheckInAnimation.AnimateTo( Property( mTickUVEffect, mTickUVEffect.GetBottomRightPropertyName() ), Vector2( 1.0f, 1.0f ) ); + + // Actor size anim + Handle handle = actor; // Get rid of const + mCheckInAnimation.AnimateTo( Property( handle, mPercentageParentSizeProperty ), 1.0f ); +} + +void CheckBoxButtonDefaultPainter::SetupCheckedAnimation( Toolkit::CheckBoxButton& checkBox, Actor& image ) +{ + if( checkBox.GetProperty( checkBox.GetPropertyIndex( Toolkit::CheckBoxButton::USE_CHECK_ANIMATION_PROPERTY_NAME ) ) && image ) + { + if( !mTickUVEffect ) + { + ImageActor imageActor = ImageActor::DownCast( image ); + mTickUVEffect = ImageRegionEffect::New(); + imageActor.SetShaderEffect( mTickUVEffect ); + } + + // Register a custom property to animate size of tick over + if( mPercentageParentSizeProperty != Property::INVALID_INDEX ) + { + image.SetProperty( mPercentageParentSizeProperty, 0.0f ); + } + + mTickUVEffect.SetBottomRight( Vector2( 0.0f, 1.0f ) ); + + // Parent + if( !image.GetParent() ) + { + checkBox.Add( image ); + } + + AddToCheckInAnimation( image ); + } +} + +void CheckBoxButtonDefaultPainter::EndCheckOutAnimation() +{ + switch( mPaintState ) + { + case UncheckedCheckedTransition: + { + mPaintState = CheckedState; + break; + } + case CheckedUncheckedTransition: + { + mPaintState = UncheckedState; + break; + } + case UncheckedDimmedTransition: + { + mPaintState = DimmedUncheckedState; + break; + } + case DimmedUncheckedTransition: + { + mPaintState = UncheckedState; + break; + } + case CheckedDimmedTransition: + { + mPaintState = DimmedCheckedState; + break; + } + case DimmedCheckedTransition: + { + mPaintState = CheckedState; + break; + } + default: + { + break; + } + } +} + +void CheckBoxButtonDefaultPainter::CheckOutAnimationFinished( Dali::Animation& source ) +{ + EndCheckOutAnimation(); + + Toolkit::CheckBoxButton handle( mButton->GetOwner() ); + StopCheckOutAnimation( handle ); + mButton = NULL; +} + +void CheckBoxButtonDefaultPainter::CheckInAnimationFinished( Dali::Animation& source ) +{ + switch( mPaintState ) + { + case UncheckedCheckedTransition: + { + mPaintState = CheckedState; + break; + } + case CheckedUncheckedTransition: + { + mPaintState = UncheckedState; + break; + } + case UncheckedDimmedTransition: + { + mPaintState = DimmedUncheckedState; + break; + } + case DimmedUncheckedTransition: + { + mPaintState = UncheckedState; + break; + } + case CheckedDimmedTransition: + { + mPaintState = DimmedCheckedState; + break; + } + case DimmedCheckedTransition: + { + mPaintState = CheckedState; + break; + } + default: + { + break; + } + } + + StopCheckInAnimation(); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.h b/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.h new file mode 100644 index 0000000..951659c --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/check-box-button-default-painter-impl.h @@ -0,0 +1,340 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_DEFAULT_PAINTER_H__ +#define __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_DEFAULT_PAINTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +#include "check-box-button-painter-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +// Forward declarations + +class CheckBoxButton; +class CheckBoxButtonDefaultPainter; + +// Type definitions + +typedef IntrusivePtr CheckBoxButtonDefaultPainterPtr; + +/** + * CheckBoxButtonDefaultPainter controls the Dali::Toolkit::CheckBoxButton appearance. + * + * This class inherits from Dali::Toolkit::Internal::CheckBoxButtonPainter and is registered in a + * Dali::Toolkit::Internal::CheckBoxButton object in order to receive the state changes. + */ +class CheckBoxButtonDefaultPainter : public CheckBoxButtonPainter +{ +public: + /** + * Constructor. + * + * Set actors and animations to NULL. + */ + CheckBoxButtonDefaultPainter(); + + /** + * Destructor. + * + * It clears all fade in or fade out animations. + */ + ~CheckBoxButtonDefaultPainter(); + + /** + * Sets the background image. + * + * It adds the background image to the root actor and creates the image transition if needed. + * + * @param[inout] checkBox The button in which all actors that form its appearance are going to be added. + * @param[in] image The background image. + */ + void SetBackgroundImage( Toolkit::CheckBoxButton& checkBox, Actor image ); + + /** + * Sets the checked image. + * + * It adds the checked image to the root actor and creates the image transition if needed. + * + * @param[inout] checkBox The button in which all actors that form its appearance are going to be added. + * @param[in] image The checked image. + */ + void SetCheckedImage( Toolkit::CheckBoxButton& checkBox, Actor image ); + + /** + * Sets the dimmed backgroundimage. + * + * It adds the dimmed backgroundimage to the root actor and creates the image transition if needed. + * + * @param[inout] checkBox The button in which all actors that form its appearance are going to be added. + * @param[in] image The dimmed backgroundimage. + */ + void SetDimmedBackgroundImage( Toolkit::CheckBoxButton& checkBox, Actor image ); + + /** + * Sets the dimmed checked image. + * + * It adds the dimmed checked image to the root actor and creates the image transition if needed. + * + * @param[inout] checkBox The button in which all actors that form its appearance are going to be added. + * @param[in] image The dimmed checked image. + */ + void SetDimmedCheckedImage( Toolkit::CheckBoxButton& checkBox, Actor image ); + + ///////////////////////////////////////////////////////////////////////////// + // ButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * Initializes the painter by setting the default images. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + */ + void Initialize( Toolkit::Button& button ); + + /** + * Sets the new size. + * + * Resizes images. It applies size constraints. + * + * @param[inout] button The button which stores button's images. + * @param[in] size The new size. + */ + void SetSize( Toolkit::Button& button, const Vector3& size ); + + /** + * Changes the Vega::Toolkit::CheckBoxButton for the given dimmed state. + * + * It creates the image transition if needed. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + * @param[in] dimmed The dimmed state. + */ + void SetDimmed( Toolkit::Button& button, bool dimmed ); + + /** + * Sets the animation time. + * @param[in] animationTime The animation time. + */ + void SetAnimationTime( float animationTime ); + + /** + * Retrieves the animation time. + * @return The animation time. + */ + float GetAnimationTime() const; + + ///////////////////////////////////////////////////////////////////////////// + // CheckBoxButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * This method is called when the Dali::Toolkit::Internal::CheckBoxButton in which this object is registered + * changes its state. + * + * @param[inout] checkBox The Dali::Toolkit::CheckBoxButton in which this object is registered. + */ + void Checked( Toolkit::CheckBoxButton& checkBox ); + +private: + + // Undefined + CheckBoxButtonDefaultPainter( const CheckBoxButtonDefaultPainter& ); + + // Undefined + CheckBoxButtonDefaultPainter& operator=( const CheckBoxButtonDefaultPainter& ); + +private: + + /** + * Default check box button painter states. + */ + enum PaintState + { + UncheckedState, ///< The check box button is unchecked. + CheckedState, ///< The check box button is checked. + DimmedUncheckedState, ///< The check box button is dimmed and unchecked. + DimmedCheckedState, ///< The check box button is dimmed and checked. + UncheckedCheckedTransition, ///< The check box button is in transition from unchecked to checked. + CheckedUncheckedTransition, ///< The check box button is in transition from checked to unchecked. + UncheckedDimmedTransition, ///< The check box button is in transition from unchecked to dimmed. + DimmedUncheckedTransition, ///< The check box button is in transition from dimmed to unchecked. + CheckedDimmedTransition, ///< The check box button is in transition from checked to dimmed. + DimmedCheckedTransition ///< The check box button is in transition from dimmed to checked. + }; + + /** + * Used in the FadeOut functions. + */ + enum ImageLayer + { + Background, ///< Fade out the background. + Foreground ///< Fade out the foreground. + }; + +private: + /** + * Apply size and position constraints to painter actors. + * + * @param[inout] actor The actor. + * @param[in] depth Depth position. + */ + void ApplyConstraint( Actor& actor, float depth ); + + /** + * Apply size constraint to check tick + * + * @param[inout] actor The actor. + * @param[in] depth Depth position. + */ + void ApplyCheckedConstraint( Actor& actor, float depth ); + + /** + * Adds the actor to the fade in animation. It creates a fade in animation if needed. + * + * @param[in] actor The actor. + */ + void AddToFadeInAnimation( const Actor& actor ); + + /** + * Starts the check in animation. + * + * CheckBoxButtonDefaultPainter::CheckInAnimationFinished slot is called when the animation finishes. + */ + void StartCheckInAnimation(); + + /** + * Stops the check in animation. + */ + void StopCheckInAnimation(); + + /** + * Adds the actor to the fade out animation. It creates a fade out animation if needed. + * + * @param[in] actor The actor. + */ + void AddToFadeOutAnimation( const Actor& actor ); + + /** + * Starts the check out animation. + * + * CheckBoxButtonDefaultPainter::CheckOutAnimationFinished slot is called when the animation finishes. + * + * @param[inout] checkBox The button which holds images. + */ + void StartCheckOutAnimation( Toolkit::CheckBoxButton& checkBox ); + + /** + * Stops the fade out animation. + * + * It removes the actor stored in CheckBoxButtonDefaultPainter::mFadeOutBackgroundImage and + * CheckBoxButtonDefaultPainter::mFadeOutCheckedImage. + * + * @param[inout] checkBox The button which holds images. + * @param[in] remove If true, removes the fadeout actor from root. + */ + void StopCheckOutAnimation( Toolkit::CheckBoxButton& checkBox, bool remove = true ); + + /** + * It adds the actor to the root actor and to the fade in animation. + * + * @param[inout] checkBox The button which holds images. + * @param[inout] image The actor. + * @param[in] opacity The initial opacity. + */ + void FadeInImage( Toolkit::CheckBoxButton& checkBox, Actor& image, float opacity = 0.f ); + + /** + * It adds the actor fade out animation and stores it to be removed when the animation finishes. + * + * @param[inout] checkBox The button which holds images. + * @param[in] layer Defines if the actor is going to be stored in the mFadeOutBackgroundImage or mFadeOutCheckedImage member. + * @param[inout] image The actor. + * @param[in] opacity The initial opacity. + */ + void FadeOutImage( Toolkit::CheckBoxButton& checkBox, ImageLayer layer, Actor& image, float opacity = 1.f ); + + /** + * Adds the actor to the fade in animation. It creates a fade in animation if needed. + * + * @param[in] actor The actor. + */ + void AddToCheckInAnimation( const Actor& actor ); + + /** + * It adds the actor to the root actor and to the fade in animation. + * + * @param[inout] checkBox The button which holds images. + * @param[inout] image The actor. + */ + void SetupCheckedAnimation( Toolkit::CheckBoxButton& checkBox, Actor& image ); + + /** + * Signal end of check out animation + */ + void EndCheckOutAnimation(); + + // slots + + /** + * Called when the fade out animation finishes. + * + * It changes the check button painter state and removes actors from the root. + */ + void CheckOutAnimationFinished( Dali::Animation& source ); + + /** + * Called when the fade in animation finishes. + * + * It changes the check button painter state. + */ + void CheckInAnimationFinished( Dali::Animation& source ); + +private: + bool mDimmed; ///< Stores the dimmed property. + + PaintState mPaintState; ///< The painter state. + + Animation mCheckInAnimation; ///< Animation used in the state transitions. + Animation mCheckOutAnimation; ///< Animation used in the state transitions. + Internal::CheckBoxButton* mButton; ///< Temporary pointer used to remove fade out images from button. + float mAnimationTime; ///< The animation time. + + ImageRegionEffect mTickUVEffect; ///< ImageRegionEffect to expand the tick across + + Property::Index mPercentageParentSizeProperty; ///< Dynamic property on the image actor +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_DEFAULT_PAINTER_H__ diff --git a/dali-toolkit/internal/controls/buttons/check-box-button-impl.cpp b/dali-toolkit/internal/controls/buttons/check-box-button-impl.cpp new file mode 100644 index 0000000..4eb7831 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/check-box-button-impl.cpp @@ -0,0 +1,271 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "check-box-button-impl.h" + +// EXTERNAL INCLUDES + +#include + +// INTERNAL INCLUDES + +#include "check-box-button-default-painter-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +BaseHandle Create() +{ + return Toolkit::CheckBoxButton::New(); +} + +TypeRegistration mType( typeid(Toolkit::CheckBoxButton), typeid(Toolkit::Button), Create ); + +TypeAction a1(mType, Toolkit::CheckBoxButton::ACTION_CHECK_BOX_BUTTON_CLICK, &CheckBoxButton::DoAction); + +} + +namespace +{ + // Helper function used to cast a ButtonPainterPtr to CheckBoxButtonDefaultPainterPtr + CheckBoxButtonDefaultPainterPtr GetCheckBoxButtonPainter( ButtonPainterPtr painter ) + { + return static_cast( painter.Get() ); + } +} // namespace + +Dali::Toolkit::CheckBoxButton CheckBoxButton::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< CheckBoxButton > internalCheckBoxButton = new CheckBoxButton(); + + // Pass ownership to CustomActor + Dali::Toolkit::CheckBoxButton checkBoxButton( *internalCheckBoxButton ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalCheckBoxButton->Initialize(); + + return checkBoxButton; +} + +void CheckBoxButton::SetChecked( bool checked ) +{ + if( !mDimmed && ( checked != mChecked ) ) + { + // Stores the state. + mChecked = checked; + + Toolkit::CheckBoxButton handle( GetOwner() ); + + // Notifies the painter the checkbox has been checked. + GetCheckBoxButtonPainter( mPainter )->Checked( handle ); + + // Emit signal. + mClickedSignalV2.Emit( handle ); + } +} + +bool CheckBoxButton::IsChecked() const +{ + return mChecked; +} + +void CheckBoxButton::SetBackgroundImage( Image image ) +{ + SetBackgroundImage( ImageActor::New( image ) ); +} + +void CheckBoxButton::SetBackgroundImage( Actor image ) +{ + Toolkit::CheckBoxButton handle( GetOwner() ); + GetCheckBoxButtonPainter( mPainter )->SetBackgroundImage( handle, image ); +} + +Actor& CheckBoxButton::GetBackgroundImage() +{ + return mBackgroundImage; +} + +Actor CheckBoxButton::GetBackgroundImage() const +{ + return mBackgroundImage; +} + +void CheckBoxButton::SetCheckedImage( Image image ) +{ + SetCheckedImage( ImageActor::New( image ) ); +} + +void CheckBoxButton::SetCheckedImage( Actor image ) +{ + Toolkit::CheckBoxButton handle( GetOwner() ); + GetCheckBoxButtonPainter( mPainter )->SetCheckedImage( handle, image ); +} + +Actor& CheckBoxButton::GetCheckedImage() +{ + return mCheckedImage; +} + +Actor CheckBoxButton::GetCheckedImage() const +{ + return mCheckedImage; +} + +void CheckBoxButton::SetDimmedBackgroundImage( Image image ) +{ + SetDimmedBackgroundImage( ImageActor::New( image ) ); +} + +void CheckBoxButton::SetDimmedBackgroundImage( Actor image ) +{ + Toolkit::CheckBoxButton handle( GetOwner() ); + GetCheckBoxButtonPainter( mPainter )->SetDimmedBackgroundImage( handle, image ); +} + +Actor& CheckBoxButton::GetDimmedBackgroundImage() +{ + return mDimmedBackgroundImage; +} + +Actor CheckBoxButton::GetDimmedBackgroundImage() const +{ + return mDimmedBackgroundImage; +} + +void CheckBoxButton::SetDimmedCheckedImage( Image image ) +{ + SetDimmedCheckedImage( ImageActor::New( image ) ); +} + +void CheckBoxButton::SetDimmedCheckedImage( Actor image ) +{ + Toolkit::CheckBoxButton handle( GetOwner() ); + GetCheckBoxButtonPainter( mPainter )->SetDimmedCheckedImage( handle, image ); +} + +Actor& CheckBoxButton::GetDimmedCheckedImage() +{ + return mDimmedCheckedImage; +} + +Actor CheckBoxButton::GetDimmedCheckedImage() const +{ + return mDimmedCheckedImage; +} + +Actor& CheckBoxButton::GetFadeOutBackgroundImage() +{ + return mFadeOutBackgroundImage; +} + +Actor& CheckBoxButton::GetFadeOutCheckedImage() +{ + return mFadeOutCheckedImage; +} + +void CheckBoxButton::OnButtonInitialize() +{ + mUseFadeAnimationProperty = Self().RegisterProperty( Toolkit::CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME, false ); + mUseCheckAnimationProperty = Self().RegisterProperty( Toolkit::CheckBoxButton::USE_CHECK_ANIMATION_PROPERTY_NAME, true ); +} + +void CheckBoxButton::OnButtonUp() +{ + if( ButtonDown == mState ) + { + // Stores the state, notifies the painter and emits a signal. + SetChecked( !mChecked ); + } +} + +void CheckBoxButton::OnAnimationTimeSet( float animationTime ) +{ + GetCheckBoxButtonPainter( mPainter )->SetAnimationTime( animationTime ); +} + +float CheckBoxButton::OnAnimationTimeRequested() const +{ + return GetCheckBoxButtonPainter( mPainter )->GetAnimationTime(); +} + +void CheckBoxButton::OnActivated() +{ + // When the button is activated, it performs the click action + std::vector attributes; + DoClickAction(attributes); +} + +void CheckBoxButton::DoClickAction(const PropertyValueContainer& attributes) +{ + // Prevents the button signals from doing a recursive loop by sending an action + // and re-emitting the signals. + if(!mClickActionPerforming) + { + mClickActionPerforming = true; + SetChecked( !mChecked ); + mClickActionPerforming = false; + } +} + +bool CheckBoxButton::DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes) +{ + bool ret = false; + + Dali::BaseHandle handle(object); + + Toolkit::CheckBoxButton button = Toolkit::CheckBoxButton::DownCast(handle); + + if(Toolkit::CheckBoxButton::ACTION_CHECK_BOX_BUTTON_CLICK == actionName) + { + GetImplementation(button).DoClickAction(attributes); + ret = true; + } + + return ret; +} + +CheckBoxButton::CheckBoxButton() +: Button(), + mChecked( false ), + mClickActionPerforming(false) +{ + // Creates specific painter. + mPainter = new CheckBoxButtonDefaultPainter(); +} + +CheckBoxButton::~CheckBoxButton() +{ + mPainter = NULL; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/buttons/check-box-button-impl.h b/dali-toolkit/internal/controls/buttons/check-box-button-impl.h new file mode 100644 index 0000000..500cc09 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/check-box-button-impl.h @@ -0,0 +1,272 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_H__ +#define __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +#include "button-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * CheckBoxButton implementation class. + * + * \sa Dali::Toolkit::CheckBoxButton + */ +class CheckBoxButton : public Button +{ +public: + + /** + * Create a new CheckBoxButton. + * @return A smart-pointer to the newly allocated CheckBoxButton. + */ + static Dali::Toolkit::CheckBoxButton New(); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetChecked( bool checked ) + */ + void SetChecked( bool checked ); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::IsChecked() + */ + bool IsChecked() const; + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetBackgroundImage( Image image ) + */ + void SetBackgroundImage( Image image ); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetBackgroundImage( Actor image ) + */ + void SetBackgroundImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the background image. + */ + Actor& GetBackgroundImage(); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::GetBackgroundImage() + */ + Actor GetBackgroundImage() const; + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetCheckedImage( Image image ) + */ + void SetCheckedImage( Image image ); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetCheckedImage( Actor image ) + */ + void SetCheckedImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the checked image. + */ + Actor& GetCheckedImage(); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::GetCheckedImage() + */ + Actor GetCheckedImage() const; + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetDimmedBackgroundImage( Image image ) + */ + void SetDimmedBackgroundImage( Image image ); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetDimmedBackgroundImage( Actor image ) + */ + void SetDimmedBackgroundImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the dimmed background image. + */ + Actor& GetDimmedBackgroundImage(); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::GetDimmedBackgroundImage() + */ + Actor GetDimmedBackgroundImage() const; + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetDimmedCheckedImage( Image image ) + */ + void SetDimmedCheckedImage( Image image ); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::SetDimmedCheckedImage( Actor image ) + */ + void SetDimmedCheckedImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the dimmed checked image. + */ + Actor& GetDimmedCheckedImage(); + + /** + * @copydoc Dali::Toolkit::CheckBoxButton::GetDimmedCheckedImage() + */ + Actor GetDimmedCheckedImage() const; + + /** + * Used by the painter only. + * @return A reference to the background image that is fading out. + */ + Actor& GetFadeOutBackgroundImage(); + + /** + * Used by the painter only. + * @return A reference to the checked image that is fading out. + */ + Actor& GetFadeOutCheckedImage(); + +protected: // From ControlImpl + + /** + * Respond the activate notification. + */ + virtual void OnActivated(); + +private: + + /** + * Perform the click action to click the button. + * @param[in] attributes The attributes to perfrom this action. + */ + void DoClickAction(const PropertyValueContainer& attributes); + +public: + /** + * Performs actions as requested using the action name. + * @param[in] object The object on which to perform the action. + * @param[in] actionName The action to perform. + * @param[in] attributes The attributes with which to perfrom this action. + * @return true if action has been accepted by this control + */ + static bool DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes); + +protected: // From Button + + /** + * Registers properties + */ + virtual void OnButtonInitialize(); + + /** + * Emits signals and notifies the painter accordingly with the set button + * properties when the button is released. + */ + virtual void OnButtonUp(); + + /** + * Sets the push button animation time. + * @param animationTime The animation time in seconds. + */ + virtual void OnAnimationTimeSet( float animationTime ); + + /** + * Retrieves the animation time. + * @return The animation time in seconds. + */ + virtual float OnAnimationTimeRequested() const; + +protected: + + /** + * Construct a new CheckBoxButton. + */ + CheckBoxButton(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~CheckBoxButton(); + +private: + + // Undefined + CheckBoxButton( const CheckBoxButton& ); + + // Undefined + CheckBoxButton& operator=( const CheckBoxButton& ); + + +private: + bool mChecked; ///< Stores the check state. + + Actor mBackgroundImage; ///< Stores the background image. + Actor mCheckedImage; ///< Stores the checked image. + Actor mDimmedCheckedImage; ///< Stores the dimmed checked image. + Actor mDimmedBackgroundImage; ///< Stores the dimmed background image. + Actor mFadeOutBackgroundImage; ///< Stores a background image, which is in a fade out animation, to be removed when the animation finishes. + Actor mFadeOutCheckedImage; ///< Stores a foreground image, which is in a fade out animation, to be removed when the animation finishes. + + // Actions + bool mClickActionPerforming; + + // Properties + Property::Index mUseFadeAnimationProperty; + Property::Index mUseCheckAnimationProperty; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::CheckBoxButton& GetImplementation( Toolkit::CheckBoxButton& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::CheckBoxButton& GetImplementation( const Toolkit::CheckBoxButton& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + const Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_H__ diff --git a/dali-toolkit/internal/controls/buttons/check-box-button-painter-impl.h b/dali-toolkit/internal/controls/buttons/check-box-button-painter-impl.h new file mode 100644 index 0000000..c923344 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/check-box-button-painter-impl.h @@ -0,0 +1,99 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_PAINTER_H__ +#define __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_PAINTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "button-painter-impl.h" + +namespace Dali +{ + +// Forward declarations + +class Image; + +namespace Toolkit +{ + +// Forward declarations + +class CheckBoxButton; + +namespace Internal +{ + +// Forward declarations + +class CheckBoxButtonPainter; + +/** + * CheckBoxButtonPainter methods should be implemented in a subclass. + */ +class CheckBoxButtonPainter : public ButtonPainter +{ +public: + /** + * Destructor. + */ + virtual ~CheckBoxButtonPainter() {} + + /** + * This method is called when the Dali::Toolkit::Internal::CheckBoxButton, in which this + * object is registered, changes its state. + * @param[inout] button The Dali::Toolkit::CheckBoxButton, linked to the internal + * implementation, in which this object is registered. + */ + virtual void Checked( Toolkit::CheckBoxButton& button ) = 0; + + ///////////////////////////////////////////////////////////////////////////// + // ButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * @copydoc ButtonPainter::Initialize( Toolkit::Button& button ) + */ + virtual void Initialize( Toolkit::Button& button ) = 0; + + /** + * @copydoc ButtonPainter::SetSize( Toolkit::Button& button, const Vector3& size ) + */ + virtual void SetSize( Toolkit::Button& button, const Vector3& size ) = 0; + + /** + * @copydoc ButtonPainter::SetDimmed( Toolkit::Button& button, bool dimmed ) + */ + virtual void SetDimmed( Toolkit::Button& button, bool dimmed ) = 0; + + /** + * @copydoc ButtonPainter::SetAnimationTime() + */ + virtual void SetAnimationTime( float animationTime ) = 0; + + /** + * @copydoc ButtonPainter::GetAnimationTime() + */ + virtual float GetAnimationTime() const = 0; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_CHECK_BOX_BUTTON_PAINTER_H__ diff --git a/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.cpp b/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.cpp new file mode 100644 index 0000000..d44470d --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.cpp @@ -0,0 +1,1304 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "push-button-default-painter-impl.h" + +// INTERNAL INCLUDES + +#include +#include +#include +#include + +#include "push-button-impl.h" + +// EXTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +const float LABEL_DEPTH( 0.75f ); +const float FOREGROUND_DEPTH( 0.5f ); +const float BACKGROUND_DEPTH( 0.25f ); + +const float ANIMATION_TIME( 0.2f ); + +inline Toolkit::Internal::PushButton& GetPushButtonImpl( Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::PushButton& GetPushButtonImpl( const Toolkit::Button& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + const Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +} + +PushButtonDefaultPainter::PushButtonDefaultPainter() +: PushButtonPainter(), + mAutoRepeating( false ), + mDimmed( false ), + mPaintState( ReleasedState ), + mButton( NULL ), + mAnimationTime( ANIMATION_TIME ), + mSize() +{ +} + +PushButtonDefaultPainter::~PushButtonDefaultPainter() +{ + if( mFadeInAnimation ) + { + mFadeInAnimation.Clear(); + } + if( mFadeOutAnimation ) + { + mFadeOutAnimation.Clear(); + } +} + +void PushButtonDefaultPainter::SetButtonImage( Toolkit::PushButton& pushButton, Actor image ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case ReleasedState: + { + if( buttonImage && buttonImage.GetParent() ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Foreground, buttonImage ); + + buttonImage = image; + + FadeInImage( pushButton, buttonImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + } + else + { + buttonImage = image; + pushButton.Add( buttonImage ); + } + break; + } + case ReleasedPressedTransition: // FALLTHROUGH + case ReleasedDimmedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + + // Replaces the button image. + buttonImage = image; + + pushButton.Add( buttonImage ); + FadeOutImage( pushButton, Foreground, buttonImage, opacity ); + + StartFadeOutAnimation( pushButton ); + break; + } + case PressedReleasedTransition: // FALLTHROUGH + case DimmedReleasedTransition: + { + StopFadeInAnimation(); + pushButton.Remove( buttonImage ); + + buttonImage = image; + + FadeInImage( pushButton, buttonImage ); + StartFadeInAnimation(); + break; + } + default: + buttonImage = image; + break; + } + + buttonImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + buttonImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( buttonImage, FOREGROUND_DEPTH ); +} + +void PushButtonDefaultPainter::SetBackgroundImage( Toolkit::PushButton& pushButton, Actor image ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& backgroundImage = pushButtonImpl.GetBackgroundImage(); + Actor& fadeOutBackgroundImage = pushButtonImpl.GetFadeOutBackgroundImage(); + + switch( mPaintState ) + { + case ReleasedState: // FALLTHROUGH + case PressedState: + case ReleasedPressedTransition: + case PressedReleasedTransition: + { + if( backgroundImage && backgroundImage.GetParent() ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, backgroundImage ); + + backgroundImage = image; + + FadeInImage( pushButton, backgroundImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + } + else + { + backgroundImage = image; + pushButton.Add( backgroundImage ); + } + break; + } + case ReleasedDimmedTransition: // FALLTHROUGH + case PressedDimmedTransition: + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + + // Replaces the button image. + backgroundImage = image; + + pushButton.Add( backgroundImage ); + FadeOutImage( pushButton, Background, backgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + break; + } + case DimmedReleasedTransition: // FALLTHROUGH + case DimmedPressedTransition: + { + StopFadeInAnimation(); + pushButton.Remove( backgroundImage ); + + backgroundImage = image; + + FadeInImage( pushButton, backgroundImage ); + StartFadeInAnimation(); + break; + } + default: + backgroundImage = image; + break; + } + + backgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + backgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( backgroundImage, BACKGROUND_DEPTH ); +} + +void PushButtonDefaultPainter::SetPressedImage( Toolkit::PushButton& pushButton, Actor image ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case PressedState: + { + if( pressedImage && pressedImage.GetParent() ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Foreground, pressedImage ); + + pressedImage = image; + + FadeInImage( pushButton, pressedImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + } + else + { + pressedImage = image; + pushButton.Add( pressedImage ); + } + break; + } + case PressedReleasedTransition: // FALLTHROUGH + case PressedDimmedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + + // Replaces the button image. + pressedImage = image; + + pushButton.Add( pressedImage ); + FadeOutImage( pushButton, Foreground, pressedImage, opacity ); + + StartFadeOutAnimation( pushButton ); + break; + } + case ReleasedPressedTransition: // FALLTHROUGH + case DimmedPressedTransition: + { + StopFadeInAnimation(); + pushButton.Remove( pressedImage ); + + pressedImage = image; + + FadeInImage( pushButton, pressedImage ); + StartFadeInAnimation(); + break; + } + default: + pressedImage = image; + break; + } + + pressedImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + pressedImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( pressedImage, FOREGROUND_DEPTH ); +} + +void PushButtonDefaultPainter::SetDimmedImage( Toolkit::PushButton& pushButton, Actor image ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& dimmedImage = pushButtonImpl.GetDimmedImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case DimmedReleasedState: // FALLTHROUGH + case DimmedPressedState: + { + if( dimmedImage && dimmedImage.GetParent() ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Foreground, dimmedImage ); + + dimmedImage = image; + + FadeInImage( pushButton, dimmedImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + } + else + { + dimmedImage = image; + pushButton.Add( dimmedImage ); + } + break; + } + case ReleasedDimmedTransition: // FALLTHROUGH + case PressedDimmedTransition: + { + StopFadeInAnimation(); + pushButton.Remove( dimmedImage ); + + dimmedImage = image; + + FadeInImage( pushButton, dimmedImage ); + StartFadeInAnimation(); + break; + } + case DimmedReleasedTransition: // FALLTHROUGH + case DimmedPressedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + + // Replaces the button image. + dimmedImage = image; + + pushButton.Add( dimmedImage ); + FadeOutImage( pushButton, Foreground, dimmedImage, opacity ); + + StartFadeOutAnimation( pushButton ); + break; + } + default: + dimmedImage = image; + break; + } + + dimmedImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + dimmedImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( dimmedImage, FOREGROUND_DEPTH ); +} + +void PushButtonDefaultPainter::SetDimmedBackgroundImage( Toolkit::PushButton& pushButton, Actor image ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& dimmedBackgroundImage = pushButtonImpl.GetDimmedBackgroundImage(); + Actor& fadeOutBackgroundImage = pushButtonImpl.GetFadeOutBackgroundImage(); + + switch( mPaintState ) + { + case DimmedReleasedState: // FALLTHROUGH + case DimmedPressedState: + { + if( dimmedBackgroundImage && dimmedBackgroundImage.GetParent() ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage ); + + dimmedBackgroundImage = image; + + FadeInImage( pushButton, dimmedBackgroundImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + } + else + { + dimmedBackgroundImage = image; + pushButton.Add( dimmedBackgroundImage ); + } + break; + } + case ReleasedDimmedTransition: // FALLTHROUGH + case PressedDimmedTransition: + { + StopFadeInAnimation(); + pushButton.Remove( dimmedBackgroundImage ); + + dimmedBackgroundImage = image; + + FadeInImage( pushButton, dimmedBackgroundImage ); + StartFadeInAnimation(); + break; + } + case DimmedReleasedTransition: // FALLTHROUGH + case DimmedPressedTransition: + { + float opacity = 1.f; + if( fadeOutBackgroundImage ) + { + opacity = fadeOutBackgroundImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + + // Replaces the button image. + dimmedBackgroundImage = image; + + pushButton.Add( dimmedBackgroundImage ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + break; + } + default: + dimmedBackgroundImage = image; + break; + } + + dimmedBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + dimmedBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + ApplyConstraint( dimmedBackgroundImage, BACKGROUND_DEPTH ); +} + +void PushButtonDefaultPainter::SetLabelText( Toolkit::PushButton& pushButton, Actor text ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetImplementation( pushButton ); + Actor& label = pushButtonImpl.GetLabel(); + + if( label && label.GetParent() ) + { + label.GetParent().Remove( label ); + } + + label = text; + label.SetAnchorPoint( AnchorPoint::CENTER ); + label.SetParentOrigin( ParentOrigin::CENTER ); + + label.SetPosition( 0.f, 0.f, LABEL_DEPTH ); + label.SetSize( mSize ); + + pushButton.Add( label ); +} + +void PushButtonDefaultPainter::Initialize( Toolkit::Button& button ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& backgroundImage = pushButtonImpl.GetBackgroundImage(); + Actor& dimmedImage = pushButtonImpl.GetDimmedImage(); + Actor& dimmedBackgroundImage = pushButtonImpl.GetDimmedBackgroundImage(); + Actor& label = pushButtonImpl.GetLabel(); + + Toolkit::PushButton& pushButton = static_cast( button ); + + if( buttonImage ) + { + SetButtonImage( pushButton, buttonImage ); + } + + if( backgroundImage ) + { + SetBackgroundImage( pushButton, backgroundImage ); + } + + if( pressedImage ) + { + SetPressedImage( pushButton, pressedImage ); + } + + if( dimmedImage ) + { + SetDimmedImage( pushButton, dimmedImage ); + } + + if( dimmedBackgroundImage ) + { + SetDimmedBackgroundImage( pushButton, dimmedBackgroundImage ); + } + + if( label ) + { + SetLabelText( pushButton, label ); + } + + SetDimmed( pushButton, mDimmed ); +} + +void PushButtonDefaultPainter::SetSize( Toolkit::Button& button, const Vector3& size ) +{ + if( size != mSize ) + { + mSize = size; + + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& backgroundImage = pushButtonImpl.GetBackgroundImage(); + Actor& dimmedImage = pushButtonImpl.GetDimmedImage(); + Actor& dimmedBackgroundImage = pushButtonImpl.GetDimmedBackgroundImage(); + Actor& label = pushButtonImpl.GetLabel(); + + ApplyConstraint( buttonImage, FOREGROUND_DEPTH ); + ApplyConstraint( backgroundImage, BACKGROUND_DEPTH ); + ApplyConstraint( pressedImage, FOREGROUND_DEPTH ); + ApplyConstraint( dimmedImage, FOREGROUND_DEPTH ); + ApplyConstraint( dimmedBackgroundImage, BACKGROUND_DEPTH ); + + if( label ) + { + label.SetPosition( 0.f, 0.f, LABEL_DEPTH ); + label.SetSize( mSize ); + } + } +} + +void PushButtonDefaultPainter::SetDimmed( Toolkit::Button& button, bool dimmed ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& backgroundImage = pushButtonImpl.GetBackgroundImage(); + Actor& dimmedImage = pushButtonImpl.GetDimmedImage(); + Actor& dimmedBackgroundImage = pushButtonImpl.GetDimmedBackgroundImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + Toolkit::PushButton& pushButton = static_cast( button ); + + mDimmed = dimmed; + + switch( mPaintState ) + { + case ReleasedState: + { + if( dimmed ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, backgroundImage ); + FadeOutImage( pushButton, Foreground, buttonImage ); + FadeInImage( pushButton, dimmedBackgroundImage ); + FadeInImage( pushButton, dimmedImage ); + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( buttonImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = ReleasedDimmedTransition; + } + else + { + mPaintState = DimmedReleasedState; + } + } + break; + } + case PressedState: + { + if( dimmed ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, backgroundImage ); + FadeOutImage( pushButton, Foreground, pressedImage ); + FadeInImage( pushButton, dimmedBackgroundImage ); + FadeInImage( pushButton, dimmedImage ); + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( pressedImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = PressedDimmedTransition; + } + else + { + mPaintState = DimmedPressedState; + } + } + break; + } + case DimmedReleasedState: + { + if( !dimmed ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage ); + FadeOutImage( pushButton, Foreground, dimmedImage ); + FadeInImage( pushButton, backgroundImage ); + FadeInImage( pushButton, buttonImage ); + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( buttonImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = DimmedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + } + break; + } + case DimmedPressedState: + { + if( !dimmed ) + { + StopFadeOutAnimation( pushButton ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage ); + FadeOutImage( pushButton, Foreground, dimmedImage ); + FadeInImage( pushButton, backgroundImage ); + FadeInImage( pushButton, pressedImage ); + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( pressedImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = DimmedPressedTransition; + } + else + { + mPaintState = PressedState; + } + } + break; + } + case ReleasedPressedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, pressedImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, backgroundImage ); + + FadeInImage( pushButton, dimmedImage ); + FadeInImage( pushButton, dimmedBackgroundImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( pressedImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = PressedDimmedTransition; + } + else + { + mPaintState = DimmedPressedState; + } + } + break; + } + case PressedReleasedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, buttonImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, backgroundImage ); + + FadeInImage( pushButton, dimmedImage ); + FadeInImage( pushButton, dimmedBackgroundImage ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( buttonImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = ReleasedDimmedTransition; + } + else + { + mPaintState = DimmedReleasedState; + } + } + break; + } + case ReleasedDimmedTransition: + { + if( !dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton, false ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, dimmedImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage, 1.f - opacity ); + FadeInImage( pushButton, buttonImage, opacity ); + FadeInImage( pushButton, backgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( buttonImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = DimmedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + } + break; + } + case DimmedReleasedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton, false ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, buttonImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, backgroundImage, 1.f - opacity ); + FadeInImage( pushButton, dimmedImage, opacity ); + FadeInImage( pushButton, dimmedBackgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( buttonImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = ReleasedDimmedTransition; + } + else + { + mPaintState = DimmedReleasedState; + } + } + break; + } + case PressedDimmedTransition: + { + if( !dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton, false ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, dimmedImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, dimmedBackgroundImage, 1.f - opacity ); + FadeInImage( pushButton, pressedImage, opacity ); + FadeInImage( pushButton, backgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( pressedImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = DimmedPressedTransition; + } + else + { + mPaintState = PressedState; + } + } + break; + } + case DimmedPressedTransition: + { + if( dimmed ) + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( pushButton, false ); + StopFadeInAnimation(); + + FadeOutImage( pushButton, Foreground, pressedImage, 1.f - opacity ); + FadeOutImage( pushButton, Background, backgroundImage, 1.f - opacity ); + FadeInImage( pushButton, dimmedImage, opacity ); + FadeInImage( pushButton, dimmedBackgroundImage, opacity ); + + StartFadeOutAnimation( pushButton ); + StartFadeInAnimation(); + + if( pressedImage || dimmedImage || backgroundImage || dimmedBackgroundImage ) + { + mPaintState = PressedDimmedTransition; + } + else + { + mPaintState = DimmedPressedState; + } + } + break; + } + default: + break; + } +} + +void PushButtonDefaultPainter::SetAnimationTime( float animationTime ) +{ + mAnimationTime = animationTime; +} + +float PushButtonDefaultPainter::GetAnimationTime() const +{ + return mAnimationTime; +} + +void PushButtonDefaultPainter::SetAutoRepeating( bool autorepeating ) +{ + mAutoRepeating = autorepeating; +} + +void PushButtonDefaultPainter::Pressed( Toolkit::PushButton& button ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case ReleasedState: + { + StopFadeOutAnimation( button ); + FadeOutImage( button, Foreground, buttonImage ); + FadeInImage( button, pressedImage ); + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = ReleasedPressedTransition; + } + else + { + mPaintState = PressedState; + } + break; + } + case ReleasedPressedTransition: + { + if( !mAutoRepeating ) + { + mPaintState = PressedReleasedTransition; + } + break; + } + case PressedReleasedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( button, false ); + StopFadeInAnimation(); + + FadeOutImage( button, Foreground, buttonImage, 1.f - opacity ); + FadeInImage( button, pressedImage, opacity ); + + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = ReleasedPressedTransition; + } + else + { + mPaintState = PressedState; + } + break; + } + default: + break; + } +} + +void PushButtonDefaultPainter::Released( Toolkit::PushButton& button ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case PressedState: + { + StopFadeOutAnimation( button ); + FadeOutImage( button, Foreground, pressedImage ); + FadeInImage( button, buttonImage ); + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = PressedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + break; + } + case ReleasedPressedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( button, false ); + StopFadeInAnimation(); + + FadeOutImage( button, Foreground, pressedImage, 1.f - opacity ); + FadeInImage( button, buttonImage, opacity ); + + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = PressedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + break; + } + default: + { + break; + } + } +} + +void PushButtonDefaultPainter::Clicked( Toolkit::PushButton& button ) +{ + Released( button ); +} + +void PushButtonDefaultPainter::Toggled( Toolkit::PushButton& button ) +{ + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( button ); + Actor& pressedImage = pushButtonImpl.GetPressedImage(); + Actor& buttonImage = pushButtonImpl.GetButtonImage(); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + + switch( mPaintState ) + { + case ReleasedState: + { + StopFadeOutAnimation( button ); + FadeOutImage( button, Foreground, buttonImage ); + FadeInImage( button, pressedImage ); + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = ReleasedPressedTransition; + } + else + { + mPaintState = PressedState; + } + break; + } + case PressedState: + { + StopFadeOutAnimation( button ); + FadeOutImage( button, Foreground, pressedImage ); + FadeInImage( button, buttonImage ); + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = PressedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + break; + } + case ReleasedPressedTransition: + { + float opacity = 1.f; + if( fadeOutButtonImage ) + { + opacity = fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( button, false ); + StopFadeInAnimation(); + + FadeOutImage( button, Foreground, pressedImage, 1.f - opacity ); + FadeInImage( button, buttonImage, opacity ); + + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = PressedReleasedTransition; + } + else + { + mPaintState = ReleasedState; + } + break; + } + case PressedReleasedTransition: + { + float opacity = 0.f; + if( fadeOutButtonImage ) + { + opacity = 1.f - fadeOutButtonImage.GetCurrentOpacity(); + } + StopFadeOutAnimation( button, false ); + StopFadeInAnimation(); + + FadeOutImage( button, Foreground, buttonImage, 1.f - opacity ); + FadeInImage( button, pressedImage, opacity ); + + StartFadeOutAnimation( button ); + StartFadeInAnimation(); + + if( buttonImage || pressedImage ) + { + mPaintState = ReleasedPressedTransition; + } + else + { + mPaintState = PressedState; + } + break; + } + default: + { + break; + } + } +} + +void PushButtonDefaultPainter::ApplyConstraint( Actor& actor, float depth ) +{ + if( actor ) + { + actor.SetPosition( 0.f, 0.f, depth ); + actor.RemoveConstraints(); + actor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + } +} + +void PushButtonDefaultPainter::AddToFadeInAnimation( const Actor& actor ) +{ + if( !mFadeInAnimation ) + { + mFadeInAnimation = Dali::Animation::New( mAnimationTime ); + } + + mFadeInAnimation.OpacityTo( actor, 1.f ); +} + +void PushButtonDefaultPainter::StartFadeInAnimation() +{ + if( mFadeInAnimation ) + { + mFadeInAnimation.FinishedSignal().Connect( this, &PushButtonDefaultPainter::PressedReleasedFadeInAnimationFinished ); + mFadeInAnimation.Play(); + } +} + +void PushButtonDefaultPainter::StopFadeInAnimation() +{ + if( mFadeInAnimation ) + { + mFadeInAnimation.Clear(); + mFadeInAnimation.Reset(); + } +} + +void PushButtonDefaultPainter::AddToFadeOutAnimation( const Actor& actor ) +{ + if( !mFadeOutAnimation ) + { + mFadeOutAnimation = Dali::Animation::New( mAnimationTime ); + } + + mFadeOutAnimation.OpacityTo( actor, 0.f ); +} + +void PushButtonDefaultPainter::StartFadeOutAnimation( Toolkit::PushButton& pushButton ) +{ + if( mFadeOutAnimation ) + { + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( pushButton ); + mButton = &pushButtonImpl; + + mFadeOutAnimation.FinishedSignal().Connect( this, &PushButtonDefaultPainter::PressedReleasedFadeOutAnimationFinished ); + mFadeOutAnimation.Play(); + } +} + +void PushButtonDefaultPainter::StopFadeOutAnimation( Toolkit::PushButton& pushButton, bool remove ) +{ + if( mFadeOutAnimation ) + { + mFadeOutAnimation.Clear(); + } + + mFadeOutAnimation.Reset(); + + if( remove ) + { + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( pushButton ); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + Actor& fadeOutBackgroundImage = pushButtonImpl.GetFadeOutBackgroundImage(); + + if( fadeOutButtonImage && fadeOutButtonImage.GetParent() ) + { + fadeOutButtonImage.GetParent().Remove( fadeOutButtonImage ); + } + + if( fadeOutBackgroundImage && fadeOutBackgroundImage.GetParent() ) + { + fadeOutBackgroundImage.GetParent().Remove( fadeOutBackgroundImage ); + } + + fadeOutButtonImage.Reset(); + fadeOutBackgroundImage.Reset(); + } +} + +void PushButtonDefaultPainter::FadeInImage( Toolkit::PushButton& pushButton, Actor& image, float opacity ) +{ + if( image ) + { + image.SetOpacity( opacity ); + if( !image.GetParent() ) + { + pushButton.Add( image ); + } + + AddToFadeInAnimation( image ); + } +} + +void PushButtonDefaultPainter::FadeOutImage( Toolkit::PushButton& pushButton, ImageLayer layer, Actor& image, float opacity ) +{ + if( image ) + { + Toolkit::Internal::PushButton& pushButtonImpl = GetPushButtonImpl( pushButton ); + Actor& fadeOutButtonImage = pushButtonImpl.GetFadeOutButtonImage(); + Actor& fadeOutBackgroundImage = pushButtonImpl.GetFadeOutBackgroundImage(); + + Actor& actorLayer = ( ( Background == layer ) ? fadeOutBackgroundImage : fadeOutButtonImage ); + + actorLayer = image; + actorLayer.SetOpacity( opacity ); + + AddToFadeOutAnimation( actorLayer ); + } +} + +void PushButtonDefaultPainter::PressedReleasedFadeOutAnimationFinished( Dali::Animation& source ) +{ + switch( mPaintState ) + { + case ReleasedPressedTransition: + { + mPaintState = PressedState; + break; + } + case PressedReleasedTransition: + { + mPaintState = ReleasedState; + break; + } + case ReleasedDimmedTransition: + { + mPaintState = DimmedReleasedState; + break; + } + case DimmedReleasedTransition: + { + mPaintState = ReleasedState; + break; + } + case PressedDimmedTransition: + { + mPaintState = DimmedPressedState; + break; + } + case DimmedPressedTransition: + { + mPaintState = PressedState; + break; + } + default: + { + break; + } + } + + Toolkit::PushButton handle( mButton->GetOwner() ); + StopFadeOutAnimation( handle ); + mButton = NULL; +} + +void PushButtonDefaultPainter::PressedReleasedFadeInAnimationFinished( Dali::Animation& source ) +{ + switch( mPaintState ) + { + case ReleasedPressedTransition: + { + mPaintState = PressedState; + break; + } + case PressedReleasedTransition: + { + mPaintState = ReleasedState; + break; + } + case ReleasedDimmedTransition: + { + mPaintState = DimmedReleasedState; + break; + } + case DimmedReleasedTransition: + { + mPaintState = ReleasedState; + break; + } + case PressedDimmedTransition: + { + mPaintState = DimmedPressedState; + break; + } + case DimmedPressedTransition: + { + mPaintState = PressedState; + break; + } + default: + { + break; + } + } + + StopFadeInAnimation(); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.h b/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.h new file mode 100644 index 0000000..c1ea567 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/push-button-default-painter-impl.h @@ -0,0 +1,355 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_DEFAULT_PAINTER_H__ +#define __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_DEFAULT_PAINTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +#include "push-button-painter-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +// Forward declarations + +class PushButton; +class PushButtonDefaultPainter; + +// Type definitions + +typedef IntrusivePtr PushButtonDefaultPainterPtr; + +/** + * PushButtonDefaultPainter controls the Dali::Toolkit::PushButton appearance. + * + * This class inherits from Dali::Toolkit::Internal::PushButtonPainter and is registered in a + * Dali::Toolkit::Internal::PushButton object in order to receive the state changes. + */ +class PushButtonDefaultPainter : public PushButtonPainter +{ +public: + + /** + * Constructor. + * + * Set actors and animations to NULL. + */ + PushButtonDefaultPainter(); + + /** + * Destructor. + * + * It clears all fade in or fade out animations. + */ + ~PushButtonDefaultPainter(); + + /** + * Sets the button image. + * + * It adds the button image to the root actor and creates the image transition if needed. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] image The button image. + */ + void SetButtonImage( Toolkit::PushButton& pushButton, Actor image ); + + /** + * Sets the background image. + * + * It adds the background image to the root actor and creates the image transition if needed. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] image The background image. + */ + void SetBackgroundImage( Toolkit::PushButton& pushButton, Actor image ); + + /** + * Sets the pressed image. + * + * It adds the pressed image to the root actor and creates the image transition if needed. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] image The pressed image. + */ + void SetPressedImage( Toolkit::PushButton& pushButton, Actor image ); + + /** + * Sets the dimmed background image. + * + * It adds the dimmed background image to the root actor and creates the image transition if needed. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] image The dimmed background image. + */ + void SetDimmedBackgroundImage( Toolkit::PushButton& pushButton, Actor image ); + + /** + * Sets the dimmed image. + * + * It adds the dimmed image to the root actor and creates the image transition if needed. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] image The image. + */ + void SetDimmedImage( Toolkit::PushButton& pushButton, Actor image ); + + /** + * Sets the text label. + * + * It adds the text to the root actor. + * + * @param[inout] pushButton The button in which all actors that form its appearance are going to be added. + * @param[in] text Label text. + */ + void SetLabelText( Toolkit::PushButton& pushButton, Actor text ); + + ///////////////////////////////////////////////////////////////////////////// + // ButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * Initializes the painter by setting the default images. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + */ + void Initialize( Toolkit::Button& button ); + + /** + * Sets the new size. + * + * Resizes actors. It applies size constraints. + * + * @param[inout] button The button which stores button's images. + * @param[in] size The new size. + */ + void SetSize( Toolkit::Button& button, const Vector3& size ); + + /** + * This method is called when the \e dimmed property in the Dali::Toolkit::PushButton changes. + * + * Creates image transitions if needed. + * + * @param[inout] button The button in which all actors that form its appearance are going to be added. + * @param[in] dimmed property. + */ + void SetDimmed( Toolkit::Button& button, bool dimmed ); + + /** + * Sets the animation time. + * @param[in] animationTime The animation time. + */ + void SetAnimationTime( float animationTime ); + + /** + * Retrieves the animation time. + * @return The animation time. + */ + float GetAnimationTime() const; + + ///////////////////////////////////////////////////////////////////////////// + // PushButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * This method is called when the \e autorepeating property in the Dali::Toolkit::PushButton changes. + * @param[in] autorepeating property. + */ + void SetAutoRepeating( bool autorepeating ); + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton in which this object is registered + * is pressed. It changes to the pressed image with a transition. + * + * @param[inout] button The Dali::Toolkit::PushButton in which this object is registered. + */ + void Pressed( Toolkit::PushButton& button ); + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton in which this object is registered + * is released. It changes to the button image with a transition. + * + * @param[inout] button The Dali::Toolkit::PushButton in which this object is registered. + */ + void Released( Toolkit::PushButton& button ); + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton in which this object is registered + * is clicked. + * + * @param[inout] button The Dali::Toolkit::PushButton in which this object is registered. + */ + void Clicked( Toolkit::PushButton& button ); + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton in which this object is registered + * is toggled. + * + * @param[inout] button The Dali::Toolkit::PushButton in which this object is registered. + */ + void Toggled( Toolkit::PushButton& button ); + +private: + + // Undefined + PushButtonDefaultPainter( const PushButtonDefaultPainter& ); + + // Undefined + PushButtonDefaultPainter& operator=( const PushButtonDefaultPainter& ); + +private: + + /** + * Default push button painter states. + */ + enum PaintState + { + ReleasedState, ///< The push button is released. + PressedState, ///< The push button is pressed. + DimmedReleasedState, ///< The push button is dimmed and released. + DimmedPressedState, ///< The push button is dimemd and pressed. + ReleasedPressedTransition, ///< The push button is in transition from released to pressed. + PressedReleasedTransition, ///< The push button is in transition from pressed to released. + ReleasedDimmedTransition, ///< The push button is in transition from released to dimmed. + DimmedReleasedTransition, ///< The push button is in transition from dimmed to released. + PressedDimmedTransition, ///< The push button is in transition from pressed to dimmed. + DimmedPressedTransition ///< The push button is in transition from dimmed to pressed. + }; + + /** + * Used in the FadeOut functions. + */ + enum ImageLayer + { + Background, ///< Fade out the background. + Foreground ///< Fade out the foreground. + }; + +private: + /** + * Apply size and position constraints to painter actors. + * + * @param[inout] actor The actor. + * @param[in] depth Depth position. + */ + void ApplyConstraint( Actor& actor, float depth ); + + /** + * Adds the actor to the fade in animation. It creates a fade in animation if needed. + * + * @param[in] actor The actor. + */ + void AddToFadeInAnimation( const Actor& actor ); + + /** + * Starts the fade in animation. + * + * PushButtonDefaultPainter::PressedReleasedFadeInAnimationFinished slot is called when the animation finishes. + */ + void StartFadeInAnimation(); + + /** + * Stops the fade in animation. + */ + void StopFadeInAnimation(); + + /** + * Adds the actor to the fade out animation. It creates a fade out animation if needed. + */ + void AddToFadeOutAnimation( const Actor& actor ); + + /** + * Starts the fade out animation. + * + * PushButtonDefaultPainter::PressedReleasedFadeOutAnimationFinished slot is called when the animation finishes. + * @param[inout] pushButton The button which holds images. + */ + void StartFadeOutAnimation( Toolkit::PushButton& pushButton ); + + /** + * Stops the fade out animation. + * + * It removes the actor stored in PushButtonDefaultPainter::mFadeOutBackgroundImage and + * PushButtonDefaultPainter::mFadeOutCheckedImage. + * + * @param[inout] pushButton The button which holds images. + * @param[in] remove If true, removes the fadeout actor from root. + */ + void StopFadeOutAnimation( Toolkit::PushButton& pushButton, bool remove = true ); + + /** + * It adds the actor to the root actor and to the fade in animation. + * + * @param[inout] pushButton The button which holds images. + * @param[inout] image The actor. + * @param[in] opacity The initial opacity. + */ + void FadeInImage( Toolkit::PushButton& pushButton, Actor& image, float opacity = 0.f ); + + /** + * It adds the actor fade out animation and stores it to be removed when the animation finishes. + * @param[inout] pushButton The button which holds images. + * @param[in] layer Defines if the actor is going to be stored in the mFadeOutBackgroundImage or mFadeOutCheckedImage member. + * @param[inout] image The actor. + * @param[in] opacity The initial opacity. + */ + void FadeOutImage( Toolkit::PushButton& pushButton, ImageLayer layer, Actor& image, float opacity = 1.f ); + + // slots + + /** + * Called when the fade out animation finishes. + * + * It changes the check button painter state and removes actors from the root. + */ + void PressedReleasedFadeOutAnimationFinished( Dali::Animation& source ); + + /** + * Called when the fade in animation finishes. + * + * It changes the check button painter state. + */ + void PressedReleasedFadeInAnimationFinished( Dali::Animation& source ); + +private: + bool mAutoRepeating; ///< Stores the autorepeating property. + bool mDimmed; ///< Stores the dimmed property. + + PaintState mPaintState; ///< The painter state. + Animation mFadeInAnimation; ///< Animation used in the state transitions. + Animation mFadeOutAnimation; ///< Animation used in the state transitions. + Internal::PushButton* mButton; ///< Temporary pointer used to remove fade out images from button. + float mAnimationTime; ///< The animation time. + Vector3 mSize; ///< The button's size. +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_DEFAULT_PAINTER_H__ diff --git a/dali-toolkit/internal/controls/buttons/push-button-impl.cpp b/dali-toolkit/internal/controls/buttons/push-button-impl.cpp new file mode 100644 index 0000000..c5e078f --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/push-button-impl.cpp @@ -0,0 +1,553 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "push-button-impl.h" + +// EXTERNAL INCLUDES + +#include + +// INTERNAL INCLUDES + +#include "push-button-default-painter-impl.h" + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +using namespace Dali; + +BaseHandle Create() +{ + return Toolkit::PushButton::New(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::PushButton), typeid(Toolkit::Button), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::PushButton::SIGNAL_TOGGLED , &PushButton::DoConnectSignal ); +SignalConnectorType signalConnector2( typeRegistration, Toolkit::PushButton::SIGNAL_PRESSED , &PushButton::DoConnectSignal ); +SignalConnectorType signalConnector3( typeRegistration, Toolkit::PushButton::SIGNAL_RELEASED, &PushButton::DoConnectSignal ); + +TypeAction action1( typeRegistration, Toolkit::PushButton::ACTION_PUSH_BUTTON_CLICK, &PushButton::DoAction ); + +} + + +namespace +{ + const unsigned int INITIAL_AUTOREPEATING_DELAY( 0.15f ); + const unsigned int NEXT_AUTOREPEATING_DELAY( 0.05f ); + + // Helper function used to cast a ButtonPainter to PushButtonDefaultPainter + PushButtonDefaultPainterPtr GetPushButtonPainter( Dali::Toolkit::Internal::ButtonPainterPtr painter ) + { + return static_cast( painter.Get() ); + } +} // namespace + +Dali::Toolkit::PushButton PushButton::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< PushButton > internalPushButton = new PushButton(); + + // Pass ownership to CustomActor + Dali::Toolkit::PushButton pushButton( *internalPushButton ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalPushButton->Initialize(); + + return pushButton; +} + +void PushButton::SetAutoRepeating( bool autoRepeating ) +{ + mAutoRepeating = autoRepeating; + + // An autorepeating button can't be a toggle button. + if( autoRepeating ) + { + mToggleButton = false; + if( mToggled ) + { + // Emit a signal is not wanted, only change the appearance. + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->Toggled( handle ); + mToggled = false; + } + } + + // Notifies the painter. + GetPushButtonPainter( mPainter )->SetAutoRepeating( mAutoRepeating ); +} + +bool PushButton::IsAutoRepeating() const +{ + return mAutoRepeating; +} + +void PushButton::SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay ) +{ + DALI_ASSERT_ALWAYS( initialAutoRepeatingDelay > 0.f ); + mInitialAutoRepeatingDelay = initialAutoRepeatingDelay; +} + +float PushButton::GetInitialAutoRepeatingDelay() const +{ + return mInitialAutoRepeatingDelay; +} + +void PushButton::SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay ) +{ + DALI_ASSERT_ALWAYS( nextAutoRepeatingDelay > 0.f ); + mNextAutoRepeatingDelay = nextAutoRepeatingDelay; +} + +float PushButton::GetNextAutoRepeatingDelay() const +{ + return mNextAutoRepeatingDelay; +} + +void PushButton::SetToggleButton( bool toggle ) +{ + mToggleButton = toggle; + + // A toggle button can't be an autorepeating button. + if( toggle ) + { + mAutoRepeating = false; + + // Notifies the painter. + GetPushButtonPainter( mPainter )->SetAutoRepeating( mAutoRepeating ); + } +} + +bool PushButton::IsToggleButton() const +{ + return mToggleButton; +} + +void PushButton::SetToggled( bool toggle ) +{ + if( !mDimmed && mToggleButton && ( toggle != mToggled ) ) + { + mToggled = toggle; + + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been toggled. + GetPushButtonPainter( mPainter )->Toggled( handle ); + + // Emit signal. + mToggledSignalV2.Emit( handle, mToggled ); + } +} + +bool PushButton::IsToggled() const +{ + return mToggleButton && mToggled; +} + +void PushButton::SetButtonImage( Image image ) +{ + SetButtonImage( ImageActor::New( image ) ); +} + +void PushButton::SetButtonImage( Actor image ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetButtonImage( handle, image ); +} + +Actor& PushButton::GetButtonImage() +{ + return mButtonImage; +} + +Actor PushButton::GetButtonImage() const +{ + return mButtonImage; +} + +void PushButton::SetBackgroundImage( Image image ) +{ + SetBackgroundImage( ImageActor::New( image ) ); +} + +void PushButton::SetBackgroundImage( Actor image ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetBackgroundImage( handle, image ); +} + +Actor& PushButton::GetBackgroundImage() +{ + return mBackgroundImage; +} + +Actor PushButton::GetBackgroundImage() const +{ + return mBackgroundImage; +} + +void PushButton::SetPressedImage( Image image ) +{ + SetPressedImage( ImageActor::New( image ) ); +} + +void PushButton::SetPressedImage( Actor image ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetPressedImage( handle, image ); +} + +Actor& PushButton::GetPressedImage() +{ + return mPressedImage; +} + +Actor PushButton::GetPressedImage() const +{ + return mPressedImage; +} + +void PushButton::SetDimmedBackgroundImage( Image image ) +{ + SetDimmedBackgroundImage( ImageActor::New( image ) ); +} + +void PushButton::SetDimmedBackgroundImage( Actor image ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetDimmedBackgroundImage( handle, image ); +} + +Actor& PushButton::GetDimmedBackgroundImage() +{ + return mDimmedBackgroundImage; +} + +Actor PushButton::GetDimmedBackgroundImage() const +{ + return mDimmedBackgroundImage; +} + +void PushButton::SetDimmedImage( Image image ) +{ + SetDimmedImage( ImageActor::New( image ) ); +} + +void PushButton::SetDimmedImage( Actor image ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetDimmedImage( handle, image ); +} + +Actor& PushButton::GetDimmedImage() +{ + return mDimmedImage; +} + +Actor PushButton::GetDimmedImage() const +{ + return mDimmedImage; +} + +void PushButton::SetLabelText( const std::string& text ) +{ + Toolkit::TextView textView ( Toolkit::TextView::New( text ) ); + textView.SetWidthExceedPolicy( Toolkit::TextView::ShrinkToFit ); // Make sure our text always fits inside the button + SetLabelText( textView ); +} + +void PushButton::SetLabelText( Actor text ) +{ + Toolkit::PushButton handle( GetOwner() ); + GetPushButtonPainter( mPainter )->SetLabelText( handle, text ); +} + +Actor& PushButton::GetLabel() +{ + return mLabel; +} + +Actor PushButton::GetLabelText() const +{ + return mLabel; +} + +Actor& PushButton::GetFadeOutBackgroundImage() +{ + return mFadeOutBackgroundImage; +} + +Actor& PushButton::GetFadeOutButtonImage() +{ + return mFadeOutButtonImage; +} + +Toolkit::PushButton::ToggledSignalV2& PushButton::ToggledSignal() +{ + return mToggledSignalV2; +} + +Toolkit::PushButton::PressedSignalV2& PushButton::PressedSignal() +{ + return mPressedSignalV2; +} + +Toolkit::PushButton::ReleasedSignalV2& PushButton::ReleasedSignal() +{ + return mReleasedSignalV2; +} + +bool PushButton::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::PushButton button = Toolkit::PushButton::DownCast(handle); + + if( Toolkit::PushButton::SIGNAL_TOGGLED == signalName ) + { + button.ToggledSignal().Connect( tracker, functor ); + } + else if( Toolkit::PushButton::SIGNAL_PRESSED == signalName ) + { + button.PressedSignal().Connect( tracker, functor ); + } + else if( Toolkit::PushButton::SIGNAL_RELEASED == signalName ) + { + button.ReleasedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +void PushButton::OnButtonInitialize() +{ + // Push button requires the Leave event. + Actor root = Self(); + root.SetLeaveRequired( true ); +} + +void PushButton::OnButtonDown() +{ + if( !mToggleButton ) + { + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been pressed. + GetPushButtonPainter( mPainter )->Pressed( handle ); + + if( mAutoRepeating ) + { + SetUpTimer( mInitialAutoRepeatingDelay ); + } + + //Emit signal. + mPressedSignalV2.Emit( handle ); + } +} + +void PushButton::OnButtonUp() +{ + if( ButtonDown == mState ) + { + if( mToggleButton ) + { + mToggled = !mToggled; + + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been toggled. + GetPushButtonPainter( mPainter )->Toggled( handle ); + + //Emit signal. + mToggledSignalV2.Emit( handle, mToggled ); + } + else + { + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been clicked. + GetPushButtonPainter( mPainter )->Released( handle ); + GetPushButtonPainter( mPainter )->Clicked( handle ); + + if( mAutoRepeating ) + { + mAutoRepeatingTimer.Reset(); + } + + //Emit signal. + mReleasedSignalV2.Emit( handle ); + mClickedSignalV2.Emit( handle ); + } + } +} + +void PushButton::OnTouchPointLeave() +{ + if( ButtonDown == mState ) + { + if( !mToggleButton ) + { + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been released. + GetPushButtonPainter( mPainter )->Released( handle ); + + if( mAutoRepeating ) + { + mAutoRepeatingTimer.Reset(); + } + + //Emit signal. + mReleasedSignalV2.Emit( handle ); + } + } +} + +void PushButton::OnTouchPointInterrupted() +{ + OnTouchPointLeave(); +} + +void PushButton::OnAnimationTimeSet( float animationTime ) +{ + GetPushButtonPainter( mPainter )->SetAnimationTime( animationTime ); +} + +float PushButton::OnAnimationTimeRequested() const +{ + return GetPushButtonPainter( mPainter )->GetAnimationTime(); +} + +PushButton::PushButton() +: Button(), + mAutoRepeating( false ), + mInitialAutoRepeatingDelay( INITIAL_AUTOREPEATING_DELAY ), + mNextAutoRepeatingDelay( NEXT_AUTOREPEATING_DELAY ), + mToggleButton( false ), + mAutoRepeatingTimer(), + mToggled( false ), + mClickActionPerforming(false) +{ + // Creates specific painter. + mPainter = PushButtonDefaultPainterPtr( new PushButtonDefaultPainter() ); +} + +PushButton::~PushButton() +{ + if( mAutoRepeatingTimer ) + { + mAutoRepeatingTimer.Reset(); + } + + mPainter = NULL; +} + +void PushButton::SetUpTimer( float delay ) +{ + mAutoRepeatingTimer = Dali::Timer::New( static_cast( 1000.f * delay ) ); + mAutoRepeatingTimer.TickSignal().Connect( this, &PushButton::AutoRepeatingSlot ); + mAutoRepeatingTimer.Start(); +} + +bool PushButton::AutoRepeatingSlot() +{ + bool consumed = false; + if( !mDimmed ) + { + // Restart the autorepeat timer. + SetUpTimer( mNextAutoRepeatingDelay ); + + Toolkit::PushButton handle( GetOwner() ); + + // Notifies the painter the button has been pressed. + GetPushButtonPainter( mPainter )->Pressed( handle ); + + //Emit signal. + consumed = mReleasedSignalV2.Emit( handle ); + consumed |= mClickedSignalV2.Emit( handle ); + consumed |= mPressedSignalV2.Emit( handle ); + } + + return consumed; +} + +void PushButton::OnActivated() +{ + // When the button is activated, it performs the click action + std::vector attributes; + DoClickAction(attributes); +} + +void PushButton::DoClickAction(const PropertyValueContainer& attributes) +{ + // Prevents the button signals from doing a recursive loop by sending an action + // and re-emitting the signals. + if(!mClickActionPerforming) + { + mClickActionPerforming = true; + OnButtonDown(); + mState = ButtonDown; + OnButtonUp(); + mClickActionPerforming = false; + } +} + +bool PushButton::DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes) +{ + bool ret = false; + + Dali::BaseHandle handle(object); + + Toolkit::PushButton button = Toolkit::PushButton::DownCast(handle); + + DALI_ASSERT_ALWAYS(button); + + if(Toolkit::PushButton::ACTION_PUSH_BUTTON_CLICK == actionName) + { + GetImplementation(button).DoClickAction(attributes); + ret = true; + } + + return ret; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/buttons/push-button-impl.h b/dali-toolkit/internal/controls/buttons/push-button-impl.h new file mode 100644 index 0000000..dff5fc5 --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/push-button-impl.h @@ -0,0 +1,422 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_H__ +#define __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +#include "button-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * PushButton implementation class. + * + * \sa Dali::Toolkit::PushButton + */ +class PushButton : public Button +{ +public: + + /** + * Create a new PushButton. + * @return A smart-pointer to the newly allocated PushButton. + */ + static Dali::Toolkit::PushButton New(); + + /** + * @copydoc Dali::Toolkit::PushButton::SetAutoRepeating( bool autoRepeating ) + */ + void SetAutoRepeating( bool autoRepeating ); + + /** + * @copydoc Dali::Toolkit::PushButton::IsAutoRepeating() const + */ + bool IsAutoRepeating() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay ) + */ + void SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay ); + + /** + * @copydoc Dali::Toolkit::PushButton::GetInitialAutoRepeatingDelay() const + */ + float GetInitialAutoRepeatingDelay() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay ) + */ + void SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay ); + + /** + * @copydoc Dali::Toolkit::PushButton::GetNextAutoRepeatingDelay() const + */ + float GetNextAutoRepeatingDelay() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetToggleButton( bool toggle ) + */ + void SetToggleButton( bool toggle ); + + /** + * @copydoc Dali::Toolkit::PushButton::IsToggleButton() const + */ + bool IsToggleButton() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetToggled( bool toggle ) + */ + void SetToggled( bool toggle ); + + /** + * @copydoc Dali::Toolkit::PushButton::IsToggled() const + */ + bool IsToggled() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetButtonImage( const Image image ) + */ + void SetButtonImage( Image image ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetButtonImage( Actor image ) + */ + void SetButtonImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the button image. + */ + Actor& GetButtonImage(); + + /** + * @copydoc Dali::Toolkit::PushButton:: + */ + Actor GetButtonImage() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetBackgroundImage( const Image image ) + */ + void SetBackgroundImage( Image image ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetBackgroundImage( Actor image ) + */ + void SetBackgroundImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the background image. + */ + Actor& GetBackgroundImage(); + + /** + * @copydoc Dali::Toolkit::PushButton::GetBackgroundImage() + */ + Actor GetBackgroundImage() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetPressedImage( const Image image ) + */ + void SetPressedImage( Image image ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetPressedImage( Actor image ) + */ + void SetPressedImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the pressed image. + */ + Actor& GetPressedImage(); + + /** + * @copydoc Dali::Toolkit::PushButton::GetPressedImage() + */ + Actor GetPressedImage() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetDimmedBackgroundImage( Image image ) + */ + void SetDimmedBackgroundImage( Image image ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetDimmedBackgroundImage( Actor image ) + */ + void SetDimmedBackgroundImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the dimmed background image. + */ + Actor& GetDimmedBackgroundImage(); + + /** + * @copydoc Dali::Toolkit::PushButton::GetDimmedBackgroundImage() + */ + Actor GetDimmedBackgroundImage() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetDimmedImage( Image image ) + */ + void SetDimmedImage( Image image ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetDimmedImage( Actor image ) + */ + void SetDimmedImage( Actor image ); + + /** + * Used by the painter only. + * @return A reference to the dimmed button image. + */ + Actor& GetDimmedImage(); + + /** + * @copydoc Dali::Toolkit::PushButton::GetDimmedImage() + */ + Actor GetDimmedImage() const; + + /** + * @copydoc Dali::Toolkit::PushButton::SetLabelText( const std::string& text ) + */ + void SetLabelText( const std::string& text ); + + /** + * @copydoc Dali::Toolkit::PushButton::SetLabelText( Actor text ) + */ + void SetLabelText( Actor text ); + + /** + * Used by the painter only. + * @return A reference to the label actor. + */ + Actor& GetLabel(); + + /** + * @copydoc Dali::Toolkit::PushButton::GetLabelText() + */ + Actor GetLabelText() const; + + /** + * Used by the painter only. + * @return A reference to the background image that is fading out. + */ + Actor& GetFadeOutBackgroundImage(); + + /** + * Used by the painter only. + * @return A reference to the button image that is fading out. + */ + Actor& GetFadeOutButtonImage(); + +public: // Signals + + /** + * @copydoc Dali::Toolkit::PushButton::ToggledSignal() + */ + Toolkit::PushButton::ToggledSignalV2& ToggledSignal(); + + /** + * @copydoc Dali::Toolkit::PushButton::PressedSignal() + */ + Toolkit::PushButton::PressedSignalV2& PressedSignal(); + + /** + * @copydoc Dali::Toolkit::PushButton::ReleasedSignal() + */ + Toolkit::PushButton::ReleasedSignalV2& ReleasedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: // From Button + + /** + * Sets the Leave signal. + */ + virtual void OnButtonInitialize(); + + /** + * Emits signals and notifies the painter accordingly with the set button + * properties when the button is pressed. + */ + virtual void OnButtonDown(); + + /** + * Emits signals and notifies the painter accordingly with the set button + * properties when the button is released. + */ + virtual void OnButtonUp(); + + /** + * Emits signals and notifies the painter accordingly with the set button + * properties when the touch point leaves the boundary of the button. + */ + virtual void OnTouchPointLeave(); + + /** + * Currently it doesn't need different behaviour than @see OnTouchPointLeave() + */ + virtual void OnTouchPointInterrupted(); + + /** + * Sets the push button animation time. + * @param animationTime The animation time in seconds. + */ + virtual void OnAnimationTimeSet( float animationTime ); + + /** + * Retrieves the animation time. + * @return The animation time in seconds. + */ + virtual float OnAnimationTimeRequested() const; + +protected: // From ControlImpl + + /** + * Respond the activate notification. + */ + virtual void OnActivated(); + +private: + + /** + * Perform the click action to click the button. + * @param[in] attributes The attributes to perfrom this action. + */ + void DoClickAction(const PropertyValueContainer& attributes); + +public: + + /** + * Performs actions as requested using the action name. + * @param[in] object The object on which to perform the action. + * @param[in] actionName The action to perform. + * @param[in] attributes The attributes with which to perfrom this action. + * @return true if action has been accepted by this control + */ + static bool DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes); + + /** + * Construct a new PushButton. + */ + PushButton(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~PushButton(); + +private: + + // Undefined + PushButton( const PushButton& ); + + // Undefined + PushButton& operator=( const PushButton& ); + + /** + * Sets up the autorepeating timer. + * @param[in] delay The delay time in seconds. + */ + void SetUpTimer( float delay ); + + /** + * Slot called when Dali::Timer::SignalTick signal. Resets the autorepeating timer. + */ + bool AutoRepeatingSlot(); + +private: + bool mAutoRepeating; ///< Stores the autorepeating property. + float mInitialAutoRepeatingDelay; ///< Stores the initial autorepeating delay in seconds. + float mNextAutoRepeatingDelay; ///< Stores the next autorepeating delay in seconds. + bool mToggleButton; ///< Stores the toggle property. + + // AutoRepeating + Timer mAutoRepeatingTimer; ///< Timer used to implement the autorepeating property. + + // Toggle + bool mToggled; ///< Stores the toggle state. + + // Signals + Toolkit::PushButton::ToggledSignalV2 mToggledSignalV2; ///< Signal emitted when the button is toggled. + Toolkit::PushButton::PressedSignalV2 mPressedSignalV2; ///< Signal emitted when the button is pressed. + Toolkit::PushButton::ReleasedSignalV2 mReleasedSignalV2; ///< Signal emitted when the button is released. + + Actor mButtonImage; ///< Stores the released image. + Actor mBackgroundImage; ///< Stores the background image. + Actor mPressedImage; ///< Stores the pressed image. + Actor mDimmedImage; ///< Stores the dimmed image. + Actor mDimmedBackgroundImage; ///< Stores the dimmed background image. + + Actor mLabel; ///< Stores the text label. + + Actor mFadeOutBackgroundImage; ///< Stores a background image, which is in a fade out animation, to be removed when the animation finishes. + Actor mFadeOutButtonImage; ///< Stores a foreground image, which is in a fade out animation, to be removed when the animation finishes. + + // Actions + bool mClickActionPerforming; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::PushButton& GetImplementation( Toolkit::PushButton& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::PushButton& GetImplementation( const Toolkit::PushButton& button ) +{ + DALI_ASSERT_ALWAYS( button ); + + const Dali::RefObject& handle = button.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_H__ diff --git a/dali-toolkit/internal/controls/buttons/push-button-painter-impl.h b/dali-toolkit/internal/controls/buttons/push-button-painter-impl.h new file mode 100644 index 0000000..8f0574a --- /dev/null +++ b/dali-toolkit/internal/controls/buttons/push-button-painter-impl.h @@ -0,0 +1,131 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_PAINTER_H__ +#define __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_PAINTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "button-painter-impl.h" + +namespace Dali +{ + +// Forward declarations + +class Image; + +namespace Toolkit +{ + +// Forward declarations + +class PushButton; + +namespace Internal +{ + +// Forward declarations + +class PushButtonPainter; + +/** + * PushButtonPainter methods should be implemented in a subclass. + */ +class PushButtonPainter : public ButtonPainter +{ +public: + /** + * Destructor. + * + */ + virtual ~PushButtonPainter() {} + + /** + * This method is called from the Dali::Toolkit::Internal::PushButton when the + * \e autorepeating property changes. + * @param[in] autorepeating property. + */ + virtual void SetAutoRepeating( bool autorepeating ) = 0; + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton, in which this + * object is registered, is pressed. + * @param[inout] button The Dali::Toolkit::PushButton, linked to the internal + * implementation, in which this object is registered. + */ + virtual void Pressed( Toolkit::PushButton& button ) = 0; + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton, in which this + * object is registered, is released. + * @param[inout] button The Dali::Toolkit::PushButton, linked to the internal + * implementation, in which this object is registered. + */ + virtual void Released( Toolkit::PushButton& button ) = 0; + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton, in which this + * object is registered, is clicked. + * @param[inout] button The Dali::Toolkit::PushButton, linked to the internal + * implementation, in which this object is registered. + */ + virtual void Clicked( Toolkit::PushButton& button ) = 0; + + /** + * This method is called when the Dali::Toolkit::Internal::PushButton, in which this + * object is registered, is toggled. + * @param[inout] button The Dali::Toolkit::PushButton, linked to the internal + * implementation, in which this object is registered. + */ + virtual void Toggled( Toolkit::PushButton& button ) = 0; + + ///////////////////////////////////////////////////////////////////////////// + // ButtonPainter interface + ///////////////////////////////////////////////////////////////////////////// + + /** + * @copydoc ButtonPainter::Initialize( Toolkit::Button& button ) + */ + virtual void Initialize( Toolkit::Button& button ) = 0; + + /** + * @copydoc ButtonPainter::SetSize( Toolkit::Button& button, const Vector3& size ) + */ + virtual void SetSize( Toolkit::Button& button, const Vector3& size ) = 0; + + /** + * @copydoc ButtonPainter::SetDimmed( Toolkit::Button& button, bool dimmed ) + */ + virtual void SetDimmed( Toolkit::Button& button, bool dimmed ) = 0; + + /** + * @copydoc ButtonPainter::SetAnimationTime() + */ + virtual void SetAnimationTime( float animationTime ) = 0; + + /** + * @copydoc ButtonPainter::GetAnimationTime() + */ + virtual float GetAnimationTime() const = 0; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_PUSH_BUTTON_PAINTER_H__ diff --git a/dali-toolkit/internal/controls/cluster/cluster-impl.cpp b/dali-toolkit/internal/controls/cluster/cluster-impl.cpp new file mode 100644 index 0000000..1c4a249 --- /dev/null +++ b/dali-toolkit/internal/controls/cluster/cluster-impl.cpp @@ -0,0 +1,563 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include + +using namespace std; +using namespace Dali; + +namespace // unnamed namespace +{ + +const float CLUSTER_STYLE_CONSTRAINT_DURATION = 1.0f; + +} + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +BaseHandle Create() +{ + Toolkit::ClusterStyleStandard s = Toolkit::ClusterStyleStandard::New(Toolkit::ClusterStyleStandard::ClusterStyle1); + return Toolkit::Cluster::New( s ); +} + +TypeRegistration mType( typeid(Toolkit::Cluster), typeid(Toolkit::Control), Create ); + +TypeAction a1(mType, Toolkit::Cluster::ACTION_EXPAND , &Cluster::DoAction); +TypeAction a2(mType, Toolkit::Cluster::ACTION_COLLAPSE , &Cluster::DoAction); +TypeAction a3(mType, Toolkit::Cluster::ACTION_TRANSFORM, &Cluster::DoAction); + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Cluster +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::Cluster Cluster::New(Toolkit::ClusterStyle& style) +{ + // Create the implementation + ClusterPtr cluster(new Cluster(style)); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::Cluster handle(*cluster); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + cluster->Initialize(); + + return handle; +} + +Cluster::Cluster(Toolkit::ClusterStyle& style) +: ControlImpl(true/*requires touch*/), + mClusterStyle(style), + mExpandedCount(0) +{ +} + +void Cluster::OnInitialize() +{ +} + +Cluster::~Cluster() +{ +} + +void Cluster::AddChild( Actor child ) +{ + // automatically add child with a position at end. + AddChild( child, mChildren.size() ); +} + +void Cluster::AddChild( Actor child, unsigned int positionIndex ) +{ + AddChildInfo( ChildInfo(child, positionIndex) ); +} + +void Cluster::AddChildAt( Actor child, unsigned int index ) +{ + // automatically add child with a position at end. + AddChild( child, mChildren.size() ); +} + +void Cluster::AddChildAt( Actor child, unsigned int positionIndex, unsigned int index ) +{ + AddChildInfoAt( ChildInfo(child, positionIndex), index ); +} + +void Cluster::AddChildInfo( ChildInfo childInfo ) +{ + AddChildInfoAt(childInfo, mChildren.size()); +} + +void Cluster::AddChildInfoAt( ChildInfo childInfo, unsigned int index ) +{ + // check that the child is valid + DALI_ASSERT_ALWAYS( childInfo.mActor ); + + ChildInfoIter offset = index < mChildren.size() ? (mChildren.begin() + index) : mChildren.end(); + // now perform customization on this child. + + // adopt the child + if(childInfo.mActor.GetParent() != Self()) + { + Actor& child = childInfo.mActor; + const float depth = std::distance(mChildren.begin(), offset); + + Property::Index depthProperty = child.GetPropertyIndex(Toolkit::Cluster::CLUSTER_ACTOR_DEPTH); + if(depthProperty == Property::INVALID_INDEX) + { + depthProperty = child.RegisterProperty(Toolkit::Cluster::CLUSTER_ACTOR_DEPTH, depth); + } + + // not added prior + Self().Add( childInfo.mActor ); + mChildren.insert( offset, childInfo ); + + // Use parent position plus relative position. + child.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION ); + + // remove old constraints + child.RemoveConstraints(); + + // apply new constraints to the child + mClusterStyle.ApplyStyle(child, childInfo.mPositionIndex, AlphaFunctions::EaseOut, 0.0f); + } + else + { + // already added. + ChildInfoContainer mNewChildren; + ChildInfoIter iter = mChildren.begin(); + float depth = 0.0f; + + for( ; iter != mChildren.end(); ++iter) + { + if(iter == offset) + { + SetDepth(childInfo, depth); + depth++; + // insert the new childInfo before offset. + mNewChildren.push_back(childInfo); + } + // copy all children except the one that we wish to move. + if((*iter).mActor != childInfo.mActor) + { + SetDepth(*iter, depth); + depth++; + mNewChildren.push_back(*iter); + } + } // end for. + + if(iter == offset) + { + SetDepth(childInfo, depth); + // insert the new childInfo before offset (end). + mNewChildren.push_back(childInfo); + } + + mChildren = mNewChildren; + + // Todo somehow adjust their perceived depth. + } +} + +void Cluster::SetDepth( ChildInfo& childInfo, float depth ) +{ + Property::Index depthProperty = childInfo.mActor.GetPropertyIndex(Toolkit::Cluster::CLUSTER_ACTOR_DEPTH); + childInfo.mActor.SetProperty( depthProperty, depth ); +} + +ChildInfo Cluster::GetChildInfoAt( unsigned int index ) +{ + // check if we have this position in the cluster + if( index < mChildren.size() ) + { + // return the child handle + return mChildren[ index ]; + } + + // return an empty handle + return ChildInfo(); +} + +Actor Cluster::GetChildAt( unsigned int index ) +{ + // check if we have this position in the cluster + if( index < mChildren.size() ) + { + // return the child handle + return mChildren[ index ].mActor; + } + + // return an empty handle + return Actor(); +} + +Actor Cluster::RemoveChildAt( unsigned int index ) +{ + DALI_ASSERT_ALWAYS( index < mChildren.size() ); + + ChildInfoIter iter = mChildren.begin() + index; + Actor child = (*iter).mActor; + mChildren.erase( iter ); + Self().Remove(child); + // note: constraints will automatically be removed in OnControlChildRemove + + // update depths. + float depth = 0.0f; + + for(ChildInfoIter iter = mChildren.begin(); iter != mChildren.end(); ++iter) + { + SetDepth(*iter, depth); + depth++; + } // end for. + + return child; +} + +void Cluster::ExpandChild( unsigned int index ) +{ + if( index < mChildren.size() ) + { + ChildInfo& childInfo = mChildren[ index ]; + DALI_ASSERT_ALWAYS(childInfo.mActor); + + if(!childInfo.mExpanded) + { + // expand child to a random position/angle. + const Vector3 clusterSize = Self().GetCurrentSize(); + const float length = clusterSize.Length() * 0.1f; + const float zOffset = 50.0f; + const float angle = (rand()%360) * Math::PI / 180.0f; + Vector3 position(sin(angle) * length, -cos(angle) * length, zOffset); + const float scale(1.2f); + const float rotate = ((rand()%30) - 15) * Math::PI / 180.0f; + + position += childInfo.mActor.GetCurrentPosition(); + + TransformChild(index, + position, + Vector3::ONE * scale, + Quaternion(rotate, Vector3::ZAXIS), + AlphaFunctions::EaseOut, + 0.5f); + } + } +} + +void Cluster::ExpandAllChildren() +{ + for(unsigned int index = 0;index < mChildren.size(); index++) + { + ExpandChild( index ); + } +} + +void Cluster::CollapseChild( unsigned int index, bool front ) +{ + if( index < mChildren.size() ) + { + RestoreChild(index, + AlphaFunctions::EaseOut, + 0.25f, + front); + } +} + +void Cluster::CollapseAllChildren( bool front ) +{ + for(unsigned int index = 0;index < mChildren.size(); index++) + { + RestoreChild(index, + AlphaFunctions::EaseOut, + 0.25f, + front); + } +} + +void Cluster::TransformChild( unsigned int index, const Vector3& position, const Vector3& scale, const Quaternion& rotation, AlphaFunction alpha, const TimePeriod& period ) +{ + if( index < mChildren.size() ) + { + ChildInfo& childInfo = mChildren[ index ]; + DALI_ASSERT_ALWAYS(childInfo.mActor); + + if(!childInfo.mExpanded) + { + Actor child = childInfo.mActor; + childInfo.mExpanded = true; + mExpandedCount++; + + child.RemoveConstraints(); + Animation animation = Animation::New(period.delaySeconds + period.durationSeconds); + animation.AnimateTo( Property(child, Actor::POSITION), position, AlphaFunctions::EaseOut, period); + animation.AnimateTo( Property(child, Actor::SCALE), scale, AlphaFunctions::EaseOut, period); + animation.AnimateTo( Property(child, Actor::ROTATION), rotation, AlphaFunctions::EaseOut, period); + animation.Play(); + } + } +} + +void Cluster::RestoreChild( unsigned int index, AlphaFunction alpha, const TimePeriod& period, bool front ) +{ + if( index < mChildren.size() ) + { + ChildInfo& childInfo = mChildren[ index ]; + DALI_ASSERT_ALWAYS(childInfo.mActor); + + if(childInfo.mExpanded) + { + Actor child = childInfo.mActor; + childInfo.mExpanded = false; + mExpandedCount--; + mClusterStyle.ApplyStyle( child, childInfo.mPositionIndex, alpha, period ); + + const unsigned int hideIndex = front ? mChildren.size() : 0; + AddChildInfoAt(childInfo, hideIndex); // move child info to the back or front of the pack. + } + } +} + +void Cluster::SetBackgroundImage( Actor image ) +{ + // Replaces the background image. + if(mBackgroundImage && mBackgroundImage.GetParent()) + { + mBackgroundImage.GetParent().Remove(mBackgroundImage); + } + + mBackgroundImage = image; + Self().Add(mBackgroundImage); + + mBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + + UpdateBackground(0.0f); +} + +void Cluster::SetTitle( Actor text ) +{ + // Replaces the title actor. + if(mTitle && mTitle.GetParent()) + { + mTitle.GetParent().Remove( mTitle ); + } + + mTitle = text; + Self().Add( mTitle ); + + mTitle.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mTitle.SetParentOrigin( ParentOrigin::TOP_LEFT ); + + UpdateTitle(0.0f); +} + +void Cluster::SetStyle(Toolkit::ClusterStyle style) +{ + unsigned int previousChildrenNum = mChildren.size(); + mClusterStyle = style; + unsigned int newChildrenNum = mClusterStyle.GetMaximumNumberOfChildren(); + + // New style supports less children (remove those that no longer belong) + if(newChildrenNum < previousChildrenNum) + { + ChildInfoIter removeStart = mChildren.begin() + newChildrenNum; + + for(ChildInfoIter iter = removeStart; iter != mChildren.end(); ++iter) + { + Actor child = (*iter).mActor; + child.RemoveConstraints(); + Self().Remove(child); + } + + mChildren.erase( removeStart, mChildren.end() ); + } + + // Remove constraints from previous style, and apply new style's constraints. + for(ChildInfoIter iter = mChildren.begin(); iter != mChildren.end(); ++iter) + { + + if((*iter).mActor) + { + (*iter).mActor.RemoveConstraints(); + style.ApplyStyle( (*iter).mActor, + (*iter).mPositionIndex, + AlphaFunctions::EaseOut, + CLUSTER_STYLE_CONSTRAINT_DURATION ); + } + } + + UpdateBackground(CLUSTER_STYLE_CONSTRAINT_DURATION); + UpdateTitle(CLUSTER_STYLE_CONSTRAINT_DURATION); +} + +Toolkit::ClusterStyle Cluster::GetStyle() const +{ + return mClusterStyle; +} + +unsigned int Cluster::GetExpandedCount() const +{ + return mExpandedCount; +} + +unsigned int Cluster::GetTotalCount() const +{ + return mChildren.size(); +} + +void Cluster::UpdateBackground(float duration) +{ + if (mBackgroundImage) + { + mBackgroundImage.RemoveConstraints(); + mClusterStyle.ApplyStyleToBackground(mBackgroundImage, AlphaFunctions::EaseOut, duration); + } +} + +void Cluster::UpdateTitle(float duration) +{ + if (mTitle) + { + mTitle.RemoveConstraints(); + mClusterStyle.ApplyStyleToTitle(mTitle, AlphaFunctions::EaseOut, duration); + } +} + +void Cluster::DoExpandAction(const PropertyValueContainer& attributes) +{ + if(attributes.size() >= 1) + { + for(PropertyValueConstIter iter = attributes.begin(); iter != attributes.end(); ++iter) + { + const Property::Value& value = *iter; + + DALI_ASSERT_ALWAYS(value.GetType() == Property::FLOAT); + unsigned int index = value.Get(); + ExpandChild( index ); + } + } + else + { + ExpandAllChildren(); + } +} + +void Cluster::DoCollapseAction(const PropertyValueContainer& attributes) +{ + if(attributes.size() >= 1) + { + for(PropertyValueConstIter iter = attributes.begin(); iter != attributes.end(); ++iter) + { + const Property::Value& value = *iter; + + DALI_ASSERT_ALWAYS(value.GetType() == Property::FLOAT); + unsigned int index = value.Get(); + CollapseChild( index, false ); + } + } + else + { + CollapseAllChildren( false ); + } +} + +void Cluster::DoTransformAction(const PropertyValueContainer& attributes) +{ + DALI_ASSERT_ALWAYS(attributes.size() >= 2); + + DALI_ASSERT_ALWAYS(attributes[0].GetType() == Property::FLOAT); + unsigned int index = attributes[0].Get(); + Vector3 position; + Vector3 scale(Vector3::ONE); + Quaternion rotation(0.0f, Vector3::ZAXIS); + + DALI_ASSERT_ALWAYS(attributes[1].GetType() == Property::VECTOR3); + attributes[1].Get(position); + + if(attributes.size()>2) + { + attributes[2].Get(scale); + } + + if(attributes.size()>3) + { + attributes[3].Get(rotation); + } + + // wrap index around -1 => size - 1 + index%= mChildren.size(); + + TransformChild(index, position, scale, rotation, AlphaFunctions::EaseOut, 0.5f); +} + +void Cluster::OnControlChildRemove(Actor& child) +{ + child.RemoveConstraints(); +} + +bool Cluster::DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes) +{ + bool ret = false; + + Dali::BaseHandle handle(object); + + Toolkit::Cluster cluster = Toolkit::Cluster::DownCast(handle); + + DALI_ASSERT_ALWAYS(cluster); + + if(Toolkit::Cluster::ACTION_EXPAND == actionName) + { + GetImpl(cluster).DoExpandAction(attributes); + ret = true; + } + else if(Toolkit::Cluster::ACTION_COLLAPSE == actionName) + { + GetImpl(cluster).DoCollapseAction(attributes); + ret = true; + } + else if(Toolkit::Cluster::ACTION_TRANSFORM == actionName) + { + GetImpl(cluster).DoTransformAction(attributes); + ret = true; + } + + return ret; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/cluster/cluster-impl.h b/dali-toolkit/internal/controls/cluster/cluster-impl.h new file mode 100644 index 0000000..4d62606 --- /dev/null +++ b/dali-toolkit/internal/controls/cluster/cluster-impl.h @@ -0,0 +1,316 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CLUSTER_H__ +#define __DALI_TOOLKIT_INTERNAL_CLUSTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class Cluster; + +typedef IntrusivePtr ClusterPtr; + +class ChildInfo +{ + +public: + + ChildInfo() + : mExpanded(false) + { + } + + ChildInfo(Actor actor, unsigned int positionIndex) + : mActor(actor), + mExpanded(false), + mPositionIndex(positionIndex) + { + } + + Actor mActor; + bool mExpanded; + unsigned int mPositionIndex; +}; + +typedef std::vector ChildInfoContainer; +typedef ChildInfoContainer::iterator ChildInfoIter; +typedef ChildInfoContainer::const_iterator ChildInfoConstIter; + +/** + * Cluster is a container of grouped actors positioned in different cluster styles. + * Multiple cluster styles may be provided, to determine the position, size, rotation, scale, color and visibility + * of the child actors in the cluster. + */ +class Cluster : public ControlImpl +{ +public: + + /** + * Create a new Cluster. + * @param[in] style of the cluster + * @return A public handle to the newly allocated Cluster. + */ + static Dali::Toolkit::Cluster New(Toolkit::ClusterStyle& style); + + /** + * @copydoc Toolkit::Cluster::AddChild( Actor child ) + */ + void AddChild( Actor child ); + + /** + * @copydoc Toolkit::Cluster::AddChild( Actor child, unsigned int positionIndex ) + */ + void AddChild( Actor child, unsigned int positionIndex ); + + /** + * @copydoc Toolkit::Cluster::AddChildAt( Actor child, unsigned int index ); + */ + void AddChildAt( Actor child, unsigned int index ); + + /** + * @copydoc Toolkit::Cluster::AddChildAt( Actor child, unsigned int positionIndex, unsigned int index ); + */ + void AddChildAt( Actor child, unsigned int positionIndex, unsigned int index ); + + /** + * Adds a ChildInfo struct to the end of the children list. + * @param[in] childInfo the child info to that to children list. + */ + void AddChildInfo( ChildInfo childInfo ); + + /** + * Adds a ChildInfo struct before the specified index. + * @param[in] childInfo the child info to that to children list. + * @param[in] index the index within the children list to insert + * ChildInfo + */ + void AddChildInfoAt( ChildInfo childInfo, unsigned int index ); + + /** + * @copydoc Toolkit::Cluster::GetChildAt + */ + Actor GetChildAt( unsigned int index ); + + /** + * @copydoc Toolkit::Cluster::RemoveChildAt + */ + Actor RemoveChildAt( unsigned int index ); + + /** + * @copydoc Toolkit::Cluster::ExpandChild + */ + void ExpandChild( unsigned int index ); + + /** + * @copydoc Toolkit::Cluster::ExpandAllChildren + */ + void ExpandAllChildren(); + + /** + * @copydoc Toolkit::Cluster::CollapseChild + */ + void CollapseChild( unsigned int index, bool front ); + + /** + * @copydoc Toolkit::Cluster::CollapseAllChildren + */ + void CollapseAllChildren( bool front ); + + /** + * @copydoc Toolkit::Cluster::TransformChild + */ + void TransformChild( unsigned int index, const Vector3& position, const Vector3& scale, const Quaternion& rotation, AlphaFunction alpha, const TimePeriod& period ); + + /** + * @copydoc Toolkit::Cluster::RestoreChild + */ + void RestoreChild( unsigned int index, AlphaFunction alpha, const TimePeriod& period, bool front ); + + /** + * @copydoc Toolkit::Cluster::SetBackgroundImage + */ + void SetBackgroundImage( Actor image ); + + /** + * @copydoc Toolkit::Cluster::SetTitle + */ + void SetTitle( Actor text ); + + /** + * @copydoc Toolkit::Cluster::SetStyle + */ + void SetStyle(Toolkit::ClusterStyle style); + + /** + * @copydoc Toolkit::Cluster::GetStyle + */ + Toolkit::ClusterStyle GetStyle() const; + + /** + * @copydoc Toolkit::Cluster::GetExpandedCount + */ + unsigned int GetExpandedCount() const; + + /** + * @copydoc Toolkit::Cluster::GetTotalCount + */ + unsigned int GetTotalCount() const; + +private: + + ChildInfo GetChildInfoAt( unsigned int index ); + + void SetDepth( ChildInfo& childInfo, float depth ); + + /** + * Updates the style of the Background + * (occurs when either background changes or style changes) + * @param[in] duration apply duration for style + */ + void UpdateBackground(float duration); + + /** + * Updates the style of the Title + * (occurs when either background changes or style changes) + * @param[in] duration apply duration for style + */ + void UpdateTitle(float duration); + + /** + * Action: Expand + * Expands one or more actors. + * + * @param[in] attributes list of indices of actors to expand. + * (if no attributes specifies, then all actors expand) + */ + void DoExpandAction(const PropertyValueContainer& attributes); + + /** + * Action: Collapse + * Collapses one or more actors. + * + * @param[in] attributes list of indices of actors to collapse. + * (if no attributes specifies, then all actors collapse) + */ + void DoCollapseAction(const PropertyValueContainer& attributes); + + /** + * Action: Transform + * Transforms one actor (index) to a specified position (Vector3), + * scale (Vector3), and rotation (Quaternion). + * + * @param[in] attributes index and transform values. + */ + void DoTransformAction(const PropertyValueContainer& attributes); + +private: // From ControlImpl + /** + * From Toolkit::ControlImpl; called shortly before a child is removed from the owning actor. + * @param[in] child The child being removed.Ptr + */ + virtual void OnControlChildRemove(Actor& child); + +public: + + /** + * Performs actions as requested using the action name. + * @param[in] object The object on which to perform the action. + * @param[in] actionName The action to perform. + * @param[in] attributes The attributes with which to perfrom this action. + * @return true if action has been accepted by this control + */ + static bool DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + +protected: + + /** + * Construct a new Cluster. + * @param[in] style of the cluster + */ + Cluster(Toolkit::ClusterStyle& style); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Cluster(); + +private: + + // Undefined + Cluster(const Cluster&); + + // Undefined + Cluster& operator=(const Cluster& rhs); + +private: + + Toolkit::ClusterStyle mClusterStyle; + ChildInfoContainer mChildren; + + Actor mBackgroundImage; ///< Stores the background image. + Actor mTitle; ///< Stores the text title. + unsigned int mExpandedCount; ///< A count of how many children have been expanded. + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Cluster& GetImpl(Toolkit::Cluster& cluster) +{ + DALI_ASSERT_ALWAYS(cluster); + + Dali::RefObject& handle = cluster.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::Cluster& GetImpl(const Toolkit::Cluster& cluster) +{ + DALI_ASSERT_ALWAYS(cluster); + + const Dali::RefObject& handle = cluster.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_CLUSTER_H__ diff --git a/dali-toolkit/internal/controls/cluster/cluster-style-impl.cpp b/dali-toolkit/internal/controls/cluster/cluster-style-impl.cpp new file mode 100644 index 0000000..a71652a --- /dev/null +++ b/dali-toolkit/internal/controls/cluster/cluster-style-impl.cpp @@ -0,0 +1,641 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace // unnamed namespace +{ +// Cluster style one + +const unsigned int STYLE_1_CHILDREN_NUMBER = 8; + +const float STYLE_1_BACKGROUND_IMAGE_OFFSET_Z = 1.0f; + +const float STYLE_1_CHILD_OFFSET_Z = 2.0f; +const float STYLE_1_CHILD_GAP_FACTOR = 0.03f; +const float STYLE_1_CHILD_SIZE_FACTOR[] = { 0.4f, 0.15f, 0.25f, 0.15f, 0.4f, 0.15f, 0.25f, 0.15f }; +const Vector3 STYLE_1_CHILD_POSITION_FACTOR[] = { Vector3(0.5f - STYLE_1_CHILD_SIZE_FACTOR[0] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_1_CHILD_SIZE_FACTOR[0] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z), + Vector3(0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_1_CHILD_SIZE_FACTOR[1] - STYLE_1_CHILD_SIZE_FACTOR[2] - STYLE_1_CHILD_GAP_FACTOR * 1.5f, + STYLE_1_CHILD_OFFSET_Z + 0.5f), + Vector3(0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_1_CHILD_SIZE_FACTOR[2] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z + 1.0f), + Vector3(0.5f + STYLE_1_CHILD_SIZE_FACTOR[2] + STYLE_1_CHILD_GAP_FACTOR * 1.5f, + 0.5f - STYLE_1_CHILD_SIZE_FACTOR[3] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z + 1.5f), + Vector3(0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z + 2.0f), + Vector3(0.5f - STYLE_1_CHILD_SIZE_FACTOR[5] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_1_CHILD_SIZE_FACTOR[6] + STYLE_1_CHILD_GAP_FACTOR * 1.5f, + STYLE_1_CHILD_OFFSET_Z + 2.5f), + Vector3(0.5f - STYLE_1_CHILD_SIZE_FACTOR[6] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z + 3.0f), + Vector3(0.5f - STYLE_1_CHILD_SIZE_FACTOR[6] - STYLE_1_CHILD_SIZE_FACTOR[7] - STYLE_1_CHILD_GAP_FACTOR * 1.5f, + 0.5f + STYLE_1_CHILD_GAP_FACTOR * 0.5f, + STYLE_1_CHILD_OFFSET_Z + 3.5f) }; + +const Vector3 STYLE_1_TITLE_SIZE_FACTOR = Vector3(0.3f, 0.11f, 1.0f); +const Vector3 STYLE_1_TITLE_POSITION_FACTOR = Vector3(0.5f - STYLE_1_CHILD_SIZE_FACTOR[0] - STYLE_1_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_1_CHILD_SIZE_FACTOR[0] - STYLE_1_CHILD_GAP_FACTOR * 0.5f - STYLE_1_TITLE_SIZE_FACTOR.height + 0.02f, + 0.0f); +const Vector3 STYLE_1_TITLE_POSITION_OFFSET = Vector3(0.0f, 0.0f, 8.0f); + +// Cluster style two + +const unsigned int STYLE_2_CHILDREN_NUMBER = 6; + +const float STYLE_2_BACKGROUND_IMAGE_OFFSET_Z = 1.0f; + +const float STYLE_2_CHILD_OFFSET_Z = 2.0f; +const float STYLE_2_CHILD_GAP_FACTOR = 0.03f; +const float STYLE_2_CHILD_SIZE_FACTOR[] = { 0.4f, 0.25f, 0.15f, 0.4f, 0.25f, 0.15f }; +const Vector3 STYLE_2_CHILD_POSITION_FACTOR[] = { Vector3(0.5f - STYLE_2_CHILD_SIZE_FACTOR[0] - STYLE_2_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_2_CHILD_SIZE_FACTOR[0] * 0.75f, + STYLE_2_CHILD_OFFSET_Z), + Vector3(0.5f + STYLE_2_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_2_CHILD_SIZE_FACTOR[1] - STYLE_2_CHILD_SIZE_FACTOR[3] * 0.25f - STYLE_2_CHILD_GAP_FACTOR, + STYLE_2_CHILD_OFFSET_Z + 0.5f), + Vector3(0.5f + STYLE_2_CHILD_SIZE_FACTOR[1] + STYLE_2_CHILD_GAP_FACTOR * 1.5f, + 0.5f - STYLE_2_CHILD_SIZE_FACTOR[2] - STYLE_2_CHILD_SIZE_FACTOR[3] * 0.25f - STYLE_2_CHILD_GAP_FACTOR, + STYLE_2_CHILD_OFFSET_Z + 1.0f), + Vector3(0.5f + STYLE_2_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_2_CHILD_SIZE_FACTOR[3] * 0.25f, + STYLE_2_CHILD_OFFSET_Z + 1.5f), + Vector3(0.5f - STYLE_2_CHILD_SIZE_FACTOR[4] - STYLE_2_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_2_CHILD_SIZE_FACTOR[0] * 0.25f + STYLE_2_CHILD_GAP_FACTOR, + STYLE_2_CHILD_OFFSET_Z + 2.0f), + Vector3(0.5f - STYLE_2_CHILD_SIZE_FACTOR[4] - STYLE_2_CHILD_SIZE_FACTOR[5] - STYLE_2_CHILD_GAP_FACTOR * 1.5f, + 0.5f + STYLE_2_CHILD_SIZE_FACTOR[0] * 0.25f + STYLE_2_CHILD_GAP_FACTOR, + STYLE_2_CHILD_OFFSET_Z + 2.5f) }; + +const Vector3 STYLE_2_TITLE_SIZE_FACTOR = Vector3(0.3f, 0.11f, 1.0f); +const Vector3 STYLE_2_TITLE_POSITION_FACTOR = Vector3(0.5f - STYLE_2_CHILD_SIZE_FACTOR[0] - STYLE_2_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_2_CHILD_SIZE_FACTOR[0] * 0.75f - STYLE_2_TITLE_SIZE_FACTOR.height + 0.02f, + 0.0f); +const Vector3 STYLE_2_TITLE_POSITION_OFFSET = Vector3(0.0f, 0.0f, 8.0f); + +// Cluster style three + +const unsigned int STYLE_3_CHILDREN_NUMBER = 6; + +const Vector3 STYLE_3_TITLE_SIZE_FACTOR = Vector3(0.4f, 0.15f, 1.0f); + +const float STYLE_3_BACKGROUND_IMAGE_OFFSET_Z = 1.0f; + +const float STYLE_3_CHILD_OFFSET_Z = 2.0f; +const float STYLE_3_CHILD_GAP_FACTOR = 0.03f; +const float STYLE_3_CHILD_SIZE_FACTOR[] = { 0.4f, 0.4f, 0.15f, 0.25f, 0.25f, 0.15f }; +const float STYLE_3_CHILD_POSITION_OFFSET_Y = (1.0f - STYLE_3_CHILD_SIZE_FACTOR[0] - STYLE_3_CHILD_SIZE_FACTOR[3] - STYLE_3_CHILD_GAP_FACTOR - STYLE_3_TITLE_SIZE_FACTOR.height) * 0.5f; +const Vector3 STYLE_3_CHILD_POSITION_FACTOR[] = { Vector3(0.5f - STYLE_3_CHILD_SIZE_FACTOR[0] - STYLE_3_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_3_CHILD_SIZE_FACTOR[0] - STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z), + Vector3(0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_3_CHILD_SIZE_FACTOR[1] - STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z + 0.5f), + Vector3(0.5f + STYLE_3_CHILD_SIZE_FACTOR[3] + STYLE_3_CHILD_GAP_FACTOR * 1.5f, + 0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z + 1.0f), + Vector3(0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z + 1.5f), + Vector3(0.5f - STYLE_3_CHILD_SIZE_FACTOR[4] - STYLE_3_CHILD_GAP_FACTOR * 0.5f, + 0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z + 2.0f), + Vector3(0.5f - STYLE_3_CHILD_SIZE_FACTOR[4] - STYLE_3_CHILD_SIZE_FACTOR[5] - STYLE_3_CHILD_GAP_FACTOR * 1.5f, + 0.5f + STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y, + STYLE_3_CHILD_OFFSET_Z + 2.5f) }; + +const Vector3 STYLE_3_TITLE_POSITION_FACTOR = Vector3(0.5f - STYLE_3_CHILD_SIZE_FACTOR[0] - STYLE_3_CHILD_GAP_FACTOR * 0.5f, + 0.5f - STYLE_3_CHILD_SIZE_FACTOR[0] - STYLE_3_CHILD_GAP_FACTOR * 0.5f + STYLE_3_CHILD_POSITION_OFFSET_Y - STYLE_3_TITLE_SIZE_FACTOR.height + 0.02f, + 0.0f); +const Vector3 STYLE_3_TITLE_POSITION_OFFSET = Vector3(0.0f, 0.0f, 8.0f); + +// Cluster style four + +const unsigned int STYLE_4_CHILDREN_NUMBER = 6; + +const float STYLE_4_BACKGROUND_IMAGE_OFFSET_Z = 1.0f; + +const float STYLE_4_CHILD_OFFSET_Z = 2.0f; +const float STYLE_4_CHILD_GAP_FACTOR = 0.03f; +const float STYLE_4_CHILD_SIZE_FACTOR[] = { 0.4f, 0.22f, 0.13f, 0.4f, 0.22f, 0.13f }; +const Vector3 STYLE_4_CHILD_POSITION_FACTOR[] = { Vector3(0.5f - STYLE_4_CHILD_SIZE_FACTOR[0] * 0.9f, + 0.5f - STYLE_4_CHILD_SIZE_FACTOR[0] - STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z), + Vector3(0.5f + STYLE_4_CHILD_SIZE_FACTOR[0] * 0.1f + STYLE_4_CHILD_GAP_FACTOR, + 0.5f - STYLE_4_CHILD_SIZE_FACTOR[1] - STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z + 0.5f), + Vector3(0.5f + STYLE_4_CHILD_SIZE_FACTOR[0] * 0.1f + STYLE_4_CHILD_SIZE_FACTOR[1] + STYLE_4_CHILD_GAP_FACTOR * 2.0f, + 0.5f - STYLE_4_CHILD_SIZE_FACTOR[2] - STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z + 1.0f), + Vector3(0.5f - STYLE_4_CHILD_SIZE_FACTOR[3] * 0.1f, + 0.5f + STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z + 1.5f), + Vector3(0.5f - STYLE_4_CHILD_SIZE_FACTOR[3] * 0.1f - STYLE_4_CHILD_SIZE_FACTOR[4] - STYLE_4_CHILD_GAP_FACTOR, + 0.5f + STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z + 2.0f), + Vector3(0.5f - STYLE_4_CHILD_SIZE_FACTOR[3] * 0.1f - STYLE_4_CHILD_SIZE_FACTOR[4] - STYLE_4_CHILD_SIZE_FACTOR[5] - STYLE_4_CHILD_GAP_FACTOR * 2.0f, + 0.5f + STYLE_4_CHILD_GAP_FACTOR * 0.5f, + STYLE_4_CHILD_OFFSET_Z + 2.5f) }; + +const Vector3 STYLE_4_TITLE_SIZE_FACTOR = Vector3(0.3f, 0.11f, 1.0f); +const Vector3 STYLE_4_TITLE_POSITION_FACTOR = Vector3(0.5f - STYLE_4_CHILD_SIZE_FACTOR[0] * 0.9f, + 0.5f - STYLE_4_CHILD_SIZE_FACTOR[0] - STYLE_4_CHILD_GAP_FACTOR * 0.5f - STYLE_4_TITLE_SIZE_FACTOR.height + 0.02f, + 0.0f); +const Vector3 STYLE_4_TITLE_POSITION_OFFSET = Vector3(0.0f, 0.0f, 8.0f); +const unsigned int CLUSTER_RANDOM_SEED(0x17eac9f3); ///< Random seed for cluster data. + +const int STYLE_RANDOM_CHILDREN_NUMBER = 16; + +// Constraints + +/** + * First order equation of the form y = Mx + C + * current' = current * relative + offset + */ +struct FirstOrderEquationConstraint +{ + /** + * @param relative The relative multiplier of the source property + * @param offset The offset to add onto the result. + */ + FirstOrderEquationConstraint(Vector3 relative, Vector3 offset = Vector3::ZERO) + : mRelative(relative), + mOffset(offset) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& sourceProperty) + { + const Vector3 source = sourceProperty.GetVector3(); + + return source * mRelative + mOffset; + } + +public: + + Vector3 mRelative; + Vector3 mOffset; +}; + +/** + * Depth Constraint. + * current' = current.xy | + Vector3::ONE.z + */ +struct DepthConstraint +{ + /** + * constructor + */ + DepthConstraint() + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& depthProperty) + { + Vector3 position(current); + position.z = depthProperty.GetFloat(); + return position; + } +}; + + +/** + * Position Constraint. + * current' = current * relative + offset + */ +struct PositionConstraint +{ + /** + * @param relative The relative multiplier of the source property + * @param offset The offset to add onto the result. + */ + PositionConstraint(Vector3 relative, Vector3 offset = Vector3::ZERO) + : mRelative(relative), + mOffset(offset) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& sourceProperty, + const PropertyInput& depthProperty) + { + const Vector3 source = sourceProperty.GetVector3(); + + Vector3 position(source * mRelative + mOffset); + position.z += depthProperty.GetFloat(); + return position; + } + +public: + + Vector3 mRelative; + Vector3 mOffset; +}; + +template +struct SetConstraint +{ + SetConstraint(T value) + : mValue(value) + { + + } + + T operator()(const T& current) + { + return mValue; + } + + T mValue; +}; + +// random data generator ////////////////////////////////////////////////////// + +const unsigned int GEN_RAND_CONST = 0x15d9a373; + +unsigned int genRandom(unsigned int& seed, unsigned int offset) +{ + unsigned int shft = offset&31; + + offset++; + + seed^= (seed << (shft) | seed >> (32 - shft)) * (offset * GEN_RAND_CONST); + + return seed; +} + +float genRandomFloat(unsigned int& seed, unsigned int offset) +{ + return static_cast(genRandom(seed, offset)) / 0xffffffff; +} + +float genRandomFloat(unsigned int& seed, unsigned int offset, float min, float max) +{ + const float f = static_cast(genRandom(seed, offset)) / 0xffffffff; + return f * (max - min) + min; +} + +} // unnamed namespace + +namespace Toolkit +{ + +namespace Internal +{ + +// ClusterStyle /////////////////////////////////////////////////////////////// + +ClusterStyle::ClusterStyle() +: mMaxChildren(0), + mTitlePositionRelative(Vector3::ONE), + mTitlePositionOffset(Vector3::ZERO), + mTitleSize(Vector3::ONE), + mBackgroundPositionRelative(Vector3::ONE), + mBackgroundPositionOffset(Vector3::ZERO), + mBackgroundSize(Vector3::ONE) +{ +} + +ClusterStyle::~ClusterStyle() +{ + +} + +void ClusterStyle::SetMaximumNumberOfChildren(unsigned int maxChildren) +{ + mMaxChildren = maxChildren; +} + +unsigned int ClusterStyle::GetMaximumNumberOfChildren() const +{ + return mMaxChildren; +} + +void ClusterStyle::SetTitleProperties(const Vector3& relativePosition, + const Vector3& offsetPosition, + const Vector3& size) +{ + mTitlePositionRelative = relativePosition; + mTitlePositionOffset = offsetPosition; + mTitleSize = size; +} + +void ClusterStyle::SetBackgroundProperties(const Vector3& relativePosition, + const Vector3& offsetPosition, + const Vector3& size) +{ + mBackgroundPositionRelative = relativePosition; + mBackgroundPositionOffset = offsetPosition; + mBackgroundSize = size; +} + +// ClusterStyleStandard /////////////////////////////////////////////////////// + +ClusterStylePtr ClusterStyleStandard::New(StyleType style) +{ + ClusterStylePtr impl( new ClusterStyleStandard(style) ); + + return impl; +} + +ClusterStyleStandard::ClusterStyleStandard(StyleType style) +: ClusterStyle(), + mSizes(NULL), + mPositions(NULL) +{ + switch(style) + { + case Toolkit::ClusterStyleStandard::ClusterStyle1: + { + SetMaximumNumberOfChildren(STYLE_1_CHILDREN_NUMBER); + SetSizes(STYLE_1_CHILD_SIZE_FACTOR); + SetPositions(STYLE_1_CHILD_POSITION_FACTOR); + SetTitleProperties(STYLE_1_TITLE_POSITION_FACTOR, + STYLE_1_TITLE_POSITION_OFFSET, + STYLE_1_TITLE_SIZE_FACTOR); + SetBackgroundProperties(Vector3::ZERO, + Vector3(0.0f, 0.0f, STYLE_1_BACKGROUND_IMAGE_OFFSET_Z), + Vector3::ONE); + break; + } + case Toolkit::ClusterStyleStandard::ClusterStyle2: + { + SetMaximumNumberOfChildren(STYLE_2_CHILDREN_NUMBER); + SetSizes(STYLE_2_CHILD_SIZE_FACTOR); + SetPositions(STYLE_2_CHILD_POSITION_FACTOR); + SetTitleProperties(STYLE_2_TITLE_POSITION_FACTOR, + STYLE_2_TITLE_POSITION_OFFSET, + STYLE_2_TITLE_SIZE_FACTOR); + SetBackgroundProperties(Vector3::ZERO, + Vector3(0.0f, 0.0f, STYLE_2_BACKGROUND_IMAGE_OFFSET_Z), + Vector3::ONE); + break; + } + case Toolkit::ClusterStyleStandard::ClusterStyle3: + { + SetMaximumNumberOfChildren(STYLE_3_CHILDREN_NUMBER); + SetSizes(STYLE_3_CHILD_SIZE_FACTOR); + SetPositions(STYLE_3_CHILD_POSITION_FACTOR); + SetTitleProperties(STYLE_3_TITLE_POSITION_FACTOR, + STYLE_3_TITLE_POSITION_OFFSET, + STYLE_3_TITLE_SIZE_FACTOR); + SetBackgroundProperties(Vector3::ZERO, + Vector3(0.0f, 0.0f, STYLE_3_BACKGROUND_IMAGE_OFFSET_Z), + Vector3::ONE); + break; + } + case Toolkit::ClusterStyleStandard::ClusterStyle4: + { + SetMaximumNumberOfChildren(STYLE_4_CHILDREN_NUMBER); + SetSizes(STYLE_4_CHILD_SIZE_FACTOR); + SetPositions(STYLE_4_CHILD_POSITION_FACTOR); + SetTitleProperties(STYLE_4_TITLE_POSITION_FACTOR, + STYLE_4_TITLE_POSITION_OFFSET, + STYLE_4_TITLE_SIZE_FACTOR); + SetBackgroundProperties(Vector3::ZERO, + Vector3(0.0f, 0.0f, STYLE_4_BACKGROUND_IMAGE_OFFSET_Z), + Vector3::ONE); + break; + } + default: + { + DALI_ASSERT_ALWAYS(false && "Invalid Style"); + break; + } + } // end switch +} + +void ClusterStyleStandard::SetSizes(const float *sizes) +{ + mSizes = sizes; +} + +void ClusterStyleStandard::SetPositions(const Vector3 *positions) +{ + mPositions = positions; +} + +void ClusterStyleStandard::ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + if(mPositions) + { + const float& size = mSizes[index]; + // counter top-left parent origin and top-left anchor point. + const Vector3 position = mPositions[index] - Vector3(0.5f, 0.5f, 0.0f) + Vector3(size, size, 0.0f) * 0.5f; + + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(Vector3(position.x, position.y, 0.0f), + Vector3(0.0f, 0.0f, position.z)) ); + + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(Vector3::ONE * size) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::ROTATION, + SetConstraint(Quaternion()) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SCALE, + SetConstraint(Vector3::ONE) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + } +} + +void ClusterStyleStandard::ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mBackgroundPositionRelative, mBackgroundPositionOffset) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + background.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mBackgroundSize) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + background.ApplyConstraint(constraint); +} + +void ClusterStyleStandard::ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mTitlePositionRelative, mTitlePositionOffset) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + title.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mTitleSize) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + title.ApplyConstraint(constraint); +} + +// ClusterStyleRandom ///////////////////////////////////////////////////////// + +ClusterStylePtr ClusterStyleRandom::New() +{ + ClusterStylePtr impl( new ClusterStyleRandom() ); + + return impl; +} + +ClusterStyleRandom::ClusterStyleRandom() +: ClusterStyle() +{ + SetMaximumNumberOfChildren(STYLE_RANDOM_CHILDREN_NUMBER); + SetTitleProperties(Vector3::ZERO, + Vector3::ZERO, + Vector3::ONE); + SetBackgroundProperties(Vector3::ZERO, + Vector3(0.0f, 0.0f, 0.0f), + Vector3::ONE); +} + +void ClusterStyleRandom::ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + unsigned int seed = CLUSTER_RANDOM_SEED; + const float size = 0.5f; + const float rotation = genRandomFloat(seed, index, -1.0f, 1.0f) * Math::PI * 0.1; // +/- 18 degrees + const Vector3 position(genRandomFloat(seed, index, -0.1f, 0.1f), + genRandomFloat(seed, index, -0.1f, 0.1f), + 0.0f); + + Property::Index depthProperty = child.GetPropertyIndex(Toolkit::Cluster::CLUSTER_ACTOR_DEPTH); + + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint( Vector3(position.x, position.y, 0.0f), + Vector3(0.0f, 0.0f, position.z) ) ); + + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + // this constraint overrides the Z position. setting it to cluster-actor-depth + constraint = Constraint::New( Actor::POSITION, + LocalSource( depthProperty ), + DepthConstraint() ); + + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(Vector3::ONE * size) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::ROTATION, + SetConstraint(Quaternion(rotation, Vector3::ZAXIS)) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SCALE, + SetConstraint(Vector3::ONE) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + child.ApplyConstraint(constraint); +} + +void ClusterStyleRandom::ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mBackgroundPositionRelative, mBackgroundPositionOffset) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + background.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mBackgroundSize) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + background.ApplyConstraint(constraint); +} + +void ClusterStyleRandom::ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mTitlePositionRelative, mTitlePositionOffset) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + title.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + FirstOrderEquationConstraint(mTitleSize) ); + constraint.SetApplyTime(durationSeconds); + constraint.SetAlphaFunction(alpha); + constraint.SetRemoveAction(Constraint::Bake); + title.ApplyConstraint(constraint); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/cluster/cluster-style-impl.h b/dali-toolkit/internal/controls/cluster/cluster-style-impl.h new file mode 100644 index 0000000..39f4e66 --- /dev/null +++ b/dali-toolkit/internal/controls/cluster/cluster-style-impl.h @@ -0,0 +1,246 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +struct Vector3; + +namespace Toolkit +{ + +namespace Internal +{ + +class ClusterStyle; + +typedef IntrusivePtr ClusterStylePtr; + +/** + * ClusterStyle internal implementation + */ +class ClusterStyle : public Dali::BaseObject +{ +public: + + /** + * @copydoc Toolkit::ClusterStyle::GetMaximumNumberOfChildren + */ + unsigned int GetMaximumNumberOfChildren() const; + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyle + */ + virtual void ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds) = 0; + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToBackground + */ + virtual void ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds) = 0; + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToTitle + */ + virtual void ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds) = 0; + +protected: + + /** + * Set the maximum number of children this Style can handle. + * @param[in] The maximum number of children. + */ + void SetMaximumNumberOfChildren(unsigned int children); + + /** + * Set the title properties + * @param[in] relativePosition Relative position of the title + * @param[in] offsetPosition Offset position of the title + * @param[in] size The size of the title + */ + void SetTitleProperties(const Vector3& relativePosition, + const Vector3& offsetPosition, + const Vector3& size); + + /** + * Set the background properties + * @param[in] relativePosition Relative position of the background + * @param[in] offsetPosition Offset position of the background + * @param[in] size The size of the title + */ + void SetBackgroundProperties(const Vector3& relativePosition, + const Vector3& offsetPosition, + const Vector3& size); + +protected: + + /** + * Protected constructor see ClusterStyle::New(). + */ + ClusterStyle(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ClusterStyle(); + +protected: + + unsigned int mMaxChildren; ///< Maximum number of children that this style supports + Vector3 mTitlePositionRelative; ///< Title's position relative to size of cluster + Vector3 mTitlePositionOffset; ///< Title's position offset + Vector3 mTitleSize; ///< Title's size relative to size of cluster + Vector3 mBackgroundPositionRelative; ///< Background's position relative to size of cluster + Vector3 mBackgroundPositionOffset; ///< Background's position offset + Vector3 mBackgroundSize; ///< Background's size relative to size of cluster + +}; + +/** + * ClusterStyleStandard internal implementation + */ +class ClusterStyleStandard : public ClusterStyle +{ +public: + + typedef Toolkit::ClusterStyleStandard::StyleType StyleType; + +public: + + /** + * Create a new cluster style. + * @param[in] style The style type to create. + * @return A smart-pointer to the newly allocated ClusterStyle. + */ + static ClusterStylePtr New(StyleType style); + +public: + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyle + */ + void ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToBackground + */ + void ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToTitle + */ + void ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds); + +private: + + /** + * Set the relative sizes of the children + * @param[in] size The list of sizes for the children + */ + void SetSizes(const float *sizes); + + /** + * Set the relative positions of the children + * @param[in] positions The list of positions for the children + */ + void SetPositions(const Vector3 *positions); + +protected: + + /** + * Protected constructor see ClusterStyleRandom::New(). + */ + ClusterStyleStandard(StyleType style); + +private: + + const float *mSizes; ///< List of sizes + const Vector3 *mPositions; ///< List of positions + +}; + +/** + * ClusterStyleRandom internal implementation + */ +class ClusterStyleRandom : public ClusterStyle +{ +public: + + /** + * Create a new cluster style. + * @return A smart-pointer to the newly allocated ClusterStyle. + */ + static ClusterStylePtr New(); + +public: + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyle + */ + void ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToBackground + */ + void ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds); + + /** + * @copydoc Toolkit::ClusterStyle::ApplyStyleToTitle + */ + void ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds); + +protected: + + /** + * Protected constructor see ClusterStyleRandom::New(). + */ + ClusterStyleRandom(); + +private: + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ClusterStyle& GetImpl(Toolkit::ClusterStyle& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ClusterStyle& GetImpl(const Toolkit::ClusterStyle& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/effects-view/effects-view-impl.cpp b/dali-toolkit/internal/controls/effects-view/effects-view-impl.cpp new file mode 100644 index 0000000..a513c55 --- /dev/null +++ b/dali-toolkit/internal/controls/effects-view/effects-view-impl.cpp @@ -0,0 +1,524 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "effects-view-impl.h" + +// INTERNAL INCLUDES +#include "../../filters/blur-two-pass-filter.h" +#include "../../filters/emboss-filter.h" +#include "../../filters/spread-filter.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +Dali::BaseHandle Create() +{ + return Toolkit::EffectsView::New(); +} + +Dali::TypeRegistration mType( typeid(Dali::Toolkit::EffectsView), typeid(Dali::Toolkit::Control), Create ); + +const Pixel::Format EFFECTS_VIEW_DEFAULT_PIXEL_FORMAT = Pixel::RGBA8888; +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; +const Vector4 EFFECTS_VIEW_DEFAULT_BACKGROUND_COLOR( 1.0f, 1.0f, 1.0f, 0.0 ); +const bool EFFECTS_VIEW_REFRESH_ON_DEMAND(false); +// custom properties +const float EFFECT_SIZE_DEFAULT( 1.0f ); +const std::string EFFECT_SIZE_PROPERTY_NAME( "EffectSize" ); +const float EFFECT_STRENGTH_DEFAULT( 0.5f ); +const std::string EFFECT_STRENGTH_PROPERTY_NAME( "EffectStrength" ); +const Vector3 EFFECT_OFFSET_DEFAULT( 0.0f, 0.0f, 0.0f ); +const std::string EFFECT_OFFSET_PROPERTY_NAME( "EffectOffset" ); +const Vector4 EFFECT_COLOR_DEFAULT( Color::WHITE ); +const std::string EFFECT_COLOR_PROPERTY_NAME( "EffectColor" ); + +const char* const EFFECTS_VIEW_FRAGMENT_SOURCE = + "void main()\n" + "{\n" + " gl_FragColor = uColor;\n" + " gl_FragColor.a *= texture2D( sTexture, vTexCoord).a;\n" + "}\n"; + +const float BLUR_KERNEL0[] = { 12.0f/16.0f, + 2.0f/16.0f, 2.0f/16.0f }; + +const float BLUR_KERNEL1[] = { 8.0f/16.0f, + 4.0f/16.0f, 4.0f/16.0f }; + +const float BLUR_KERNEL2[] = { 6.0f/16.0f, + 2.5f/16.0f, 2.5f/16.0f, + 1.5f/16.0f, 1.5f/16.0f, + 1.0f/16.0f, 1.0f/16.0f }; + +const float BLUR_KERNEL3[] = { 4.0f/16.0f, + 3.0f/16.0f, 2.0f/16.0f, + 2.0f/16.0f, 2.0f/16.0f, + 1.0f/16.0f, 1.0f/16.0f }; + +const float BLUR_KERNEL4[] = { 3.0f/16.0f, + 2.5f/16.0f, 2.5f/16.0f, + 1.75f/16.0f, 1.75f/16.0f, + 1.25f/16.0f, 1.25f/16.0f, + 1.0f/16.0f, 1.0f/16.0f }; + +} // namespace + +Toolkit::EffectsView EffectsView::New() +{ + EffectsView* effectsView = new EffectsView; + + Toolkit::EffectsView handle = Toolkit::EffectsView( *effectsView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + effectsView->Initialize(); + + return handle; +} + +EffectsView::EffectsView() +: ControlImpl( false ), + mEffectType( Toolkit::EffectsView::INVALID_TYPE ), + mPixelFormat( EFFECTS_VIEW_DEFAULT_PIXEL_FORMAT ), + mSpread(0.0f), + mBackgroundColor( EFFECTS_VIEW_DEFAULT_BACKGROUND_COLOR ), + mTargetSize( Vector2::ZERO ), + mLastSize( Vector2::ZERO ), + mRefreshOnDemand(EFFECTS_VIEW_REFRESH_ON_DEMAND), + mEffectSizePropertyIndex(Property::INVALID_INDEX), + mEffectStrengthPropertyIndex(Property::INVALID_INDEX), + mEffectOffsetPropertyIndex(Property::INVALID_INDEX), + mEffectColorPropertyIndex(Property::INVALID_INDEX) +{ +} + +EffectsView::~EffectsView() +{ + RemoveFilters(); +} + +void EffectsView::SetType( Toolkit::EffectsView::EffectType type ) +{ + if( mEffectType != type ) + { + mEffectType = type; + + RemoveFilters(); + + switch( mEffectType ) + { + case Toolkit::EffectsView::DROP_SHADOW: + { + mFilters.push_back( new SpreadFilter ); + mFilters.push_back( new BlurTwoPassFilter ); + break; + } + case Toolkit::EffectsView::EMBOSS: + { + mFilters.push_back( new SpreadFilter ); + mFilters.push_back( new EmbossFilter ); + mFilters.push_back( new BlurTwoPassFilter ); + mActorPostFilter.RemoveShaderEffect(); + break; + } + default: + { + break; + } + } + } +} + +Toolkit::EffectsView::EffectType EffectsView::GetType() const +{ + return mEffectType; +} + +void EffectsView::Enable() +{ + // make sure resources are allocated and start the render tasks processing + AllocateResources(); + CreateRenderTasks(); +} + +void EffectsView::Disable() +{ + // stop render tasks processing + // Note: render target resources are automatically freed since we set the Image::Unused flag + RemoveRenderTasks(); +} + +void EffectsView::Refresh() +{ + RefreshRenderTasks(); +} + +void EffectsView::SetRefreshOnDemand( bool onDemand ) +{ + mRefreshOnDemand = onDemand; + + RefreshRenderTasks(); +} + +void EffectsView::SetPixelFormat( Pixel::Format pixelFormat ) +{ + mPixelFormat = pixelFormat; +} + +void EffectsView::SetOutputImage( FrameBufferImage image ) +{ + CustomActor self = Self(); + + if( mImageForResult != image ) + { + if( !image ) + { + if( mImageForResult ) + { + self.Remove( mActorForResult ); + mActorForResult.Reset(); + + self.Add( mActorPostFilter ); + self.Add( mActorForChildren ); + } + } + else + { + if( mImageForResult ) + { + self.Remove( mActorForResult ); + } + mActorForResult = Actor::New(); + mActorForResult.SetParentOrigin( ParentOrigin::CENTER ); + mActorForResult.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForResult.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + Self().Add( mActorForResult ); + mActorForResult.Add( mActorPostFilter ); + mActorForResult.Add( mActorForChildren ); + } + mImageForResult = image; + } +} + +FrameBufferImage EffectsView::GetOutputImage() +{ + return mImageForResult; +} + +Property::Index EffectsView::GetEffectSizePropertyIndex() const +{ + return mEffectSizePropertyIndex; +} + +Property::Index EffectsView::GetEffectStrengthPropertyIndex() const +{ + return mEffectStrengthPropertyIndex; +} + +Property::Index EffectsView::GetEffectOffsetPropertyIndex() const +{ + return mEffectOffsetPropertyIndex; +} + +Property::Index EffectsView::GetEffectColorPropertyIndex() const +{ + return mEffectColorPropertyIndex; +} + +void EffectsView::SetupProperties() +{ + CustomActor self = Self(); + + // Register a property that the user can control the drop shadow offset + mEffectSizePropertyIndex = self.RegisterProperty(EFFECT_SIZE_PROPERTY_NAME, EFFECT_SIZE_DEFAULT, Property::READ_WRITE); + mEffectStrengthPropertyIndex = self.RegisterProperty(EFFECT_STRENGTH_PROPERTY_NAME, EFFECT_STRENGTH_DEFAULT, Property::READ_WRITE); + mEffectOffsetPropertyIndex = self.RegisterProperty(EFFECT_OFFSET_PROPERTY_NAME, EFFECT_OFFSET_DEFAULT); + mEffectColorPropertyIndex = self.RegisterProperty(EFFECT_COLOR_PROPERTY_NAME, EFFECT_COLOR_DEFAULT); + mActorPostFilter.ApplyConstraint( Constraint::New( Actor::POSITION, Source( self, mEffectOffsetPropertyIndex ), EqualToConstraint() ) ); + mActorPostFilter.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorPostFilter.ApplyConstraint( Constraint::New( Actor::COLOR, Source( self, mEffectColorPropertyIndex ), EqualToConstraint() ) ); +} + +void EffectsView::SetBackgroundColor( const Vector4& color ) +{ + mBackgroundColor = color; +} + +Vector4 EffectsView::GetBackgroundColor() const +{ + return mBackgroundColor; +} + +// From ControlImpl +void EffectsView::OnInitialize() +{ + ////////////////////////////////////////////////////// + // Create cameras + mCameraForChildren = CameraActor::New(); + mCameraForChildren.SetParentOrigin(ParentOrigin::CENTER); + + mActorForChildren = ImageActor::New(); + mActorForChildren.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mActorForChildren.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as EffectsView object + mActorForChildren.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + mActorPostFilter = ImageActor::New(); + mActorPostFilter.SetParentOrigin( ParentOrigin::CENTER ); + mActorPostFilter.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as EffectsView object + mActorPostFilter.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + mActorPostFilter.SetShaderEffect( ShaderEffect::New( "", EFFECTS_VIEW_FRAGMENT_SOURCE ) ); + + // Connect to actor tree + Self().Add( mActorPostFilter ); + Self().Add( mActorForChildren ); + Self().Add( mCameraForChildren ); + + SetupProperties(); +} + +void EffectsView::OnControlSizeSet(const Vector3& targetSize) +{ + mTargetSize = Vector2(targetSize); + + // if we are already on stage, need to update render target sizes now to reflect the new size of this actor + if(Self().OnStage()) + { + AllocateResources(); + } +} + +void EffectsView::OnStageDisconnection() +{ + const size_t numFilters( mFilters.size() ); + for( size_t i = 0; i < numFilters; ++i ) + { + mFilters[i]->Disable(); + } +} + +void EffectsView::SetupFilters() +{ + int effectSize = static_cast< int >( Self().GetProperty( mEffectSizePropertyIndex ).Get() ); + switch( mEffectType ) + { + case Toolkit::EffectsView::DROP_SHADOW: + { + SpreadFilter* spreadFilter = static_cast< SpreadFilter* >( mFilters[0] ); + spreadFilter->SetInputImage( mImageForChildren ); + spreadFilter->SetOutputImage( mImagePostFilter ); + spreadFilter->SetRootActor( Self() ); + spreadFilter->SetBackgroundColor( mBackgroundColor ); + spreadFilter->SetPixelFormat( mPixelFormat ); + spreadFilter->SetSize( mTargetSize ); + spreadFilter->SetSpread( effectSize ); + + BlurTwoPassFilter* blurFilter = static_cast< BlurTwoPassFilter* >( mFilters[1] ); + blurFilter->SetInputImage( mImagePostFilter ); + blurFilter->SetOutputImage( mImagePostFilter ); + blurFilter->SetRootActor( Self() ); + blurFilter->SetBackgroundColor( mBackgroundColor ); + blurFilter->SetPixelFormat( mPixelFormat ); + blurFilter->SetSize( mTargetSize ); + + const float* kernel(NULL); + size_t kernelSize(0); + switch( effectSize ) + { + case 4: { kernel = BLUR_KERNEL4; kernelSize = sizeof(BLUR_KERNEL4)/sizeof(BLUR_KERNEL4[0]); break; } + case 3: { kernel = BLUR_KERNEL3; kernelSize = sizeof(BLUR_KERNEL3)/sizeof(BLUR_KERNEL3[0]); break; } + case 2: { kernel = BLUR_KERNEL2; kernelSize = sizeof(BLUR_KERNEL2)/sizeof(BLUR_KERNEL2[0]); break; } + case 1: { kernel = BLUR_KERNEL1; kernelSize = sizeof(BLUR_KERNEL1)/sizeof(BLUR_KERNEL1[0]); break; } + case 0: + default: { kernel = BLUR_KERNEL0; kernelSize = sizeof(BLUR_KERNEL0)/sizeof(BLUR_KERNEL0[0]); break; } + } + blurFilter->CreateKernel( kernel, kernelSize ); + break; + } + case Toolkit::EffectsView::EMBOSS: + { + SpreadFilter* spreadFilter = static_cast< SpreadFilter* >( mFilters[0] ); + spreadFilter->SetInputImage( mImageForChildren ); + spreadFilter->SetOutputImage( mImagePostFilter ); + spreadFilter->SetRootActor( Self() ); + spreadFilter->SetBackgroundColor( mBackgroundColor ); + spreadFilter->SetPixelFormat( Pixel::RGBA8888 ); + spreadFilter->SetSize( mTargetSize ); + spreadFilter->SetSpread( effectSize ); + + EmbossFilter* embossFilter = static_cast< EmbossFilter* >( mFilters[1] ); + embossFilter->SetInputImage( mImagePostFilter ); + embossFilter->SetOutputImage( mImagePostFilter ); + embossFilter->SetRootActor( Self() ); + embossFilter->SetBackgroundColor( mBackgroundColor ); + embossFilter->SetPixelFormat( Pixel::RGBA8888 ); + embossFilter->SetSize( mTargetSize ); + + BlurTwoPassFilter* blurFilter = static_cast< BlurTwoPassFilter* >( mFilters[2] ); + blurFilter->SetInputImage( mImagePostFilter ); + blurFilter->SetOutputImage( mImagePostFilter ); + blurFilter->SetRootActor( Self() ); + blurFilter->SetBackgroundColor( Vector4( 0.5f, 0.5f, 0.5f, 0.0 ) ); + blurFilter->SetPixelFormat( Pixel::RGBA8888 ); + blurFilter->SetSize( mTargetSize ); + blurFilter->CreateKernel( BLUR_KERNEL0, sizeof(BLUR_KERNEL0)/sizeof(BLUR_KERNEL0[0]) ); + + break; + } + default: + { + break; + } + } +} +void EffectsView::AllocateResources() +{ + if(mTargetSize != mLastSize) + { + mLastSize = mTargetSize; + + SetupCameras(); + + mImageForChildren = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Dali::Image::Unused ); + mActorForChildren.SetImage(mImageForChildren); + + mImagePostFilter = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Dali::Image::Unused ); + mActorPostFilter.SetImage(mImagePostFilter); + + SetupFilters(); + } +} + +void EffectsView::SetupCameras() +{ + const float cameraPosConstraintScale( 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f) ); + + // Create and place a camera for the children render, corresponding to its render target size + mCameraForChildren.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it + mCameraForChildren.SetNearClippingPlane(1.0f); + mCameraForChildren.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mCameraForChildren.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mCameraForChildren.SetPosition(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale); + mCameraForChildren.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + + // Children render camera must move when EffectsView object is resized. + // This is since we cannot change render target size - so we need to remap the child actors' rendering + // accordingly so they still exactly fill the render target. + // Note that this means the effective resolution of the child render changes as the EffectsView object + // changes size, this is the trade off for not being able to modify render target size + // Change camera z position based on EffectsView actor height + mCameraForChildren.RemoveConstraints(); + mCameraForChildren.ApplyConstraint( Constraint::New( Actor::POSITION_Z, ParentSource( Actor::SIZE_HEIGHT ), RelativeToConstraintFloat(cameraPosConstraintScale) ) ); +} + +void EffectsView::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + // create render task to render our child actors to offscreen buffer + mRenderTaskForChildren = taskList.CreateTask(); + mRenderTaskForChildren.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForChildren.SetSourceActor( Self() ); + mRenderTaskForChildren.SetExclusive(true); + mRenderTaskForChildren.SetInputEnabled( false ); + mRenderTaskForChildren.SetClearColor( mBackgroundColor ); + mRenderTaskForChildren.SetClearEnabled( true ); + mRenderTaskForChildren.SetTargetFrameBuffer( mImageForChildren ); + mRenderTaskForChildren.SetCameraActor(mCameraForChildren); // use camera that covers render target exactly + + // Enable image filters + const size_t numFilters( mFilters.size() ); + for( size_t i = 0; i < numFilters; ++i ) + { + mFilters[i]->Enable(); + } + + // create render task to render result of the image filters to the final offscreen + if( mImageForResult ) + { + mRenderTaskForResult = taskList.CreateTask(); + mRenderTaskForResult.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForResult.SetSourceActor( mActorForResult ); + mRenderTaskForResult.SetExclusive(true); + mRenderTaskForResult.SetInputEnabled( false ); + mRenderTaskForResult.SetClearColor( mBackgroundColor ); + mRenderTaskForResult.SetClearEnabled( true ); + mRenderTaskForResult.SetTargetFrameBuffer( mImageForResult ); + mRenderTaskForResult.SetCameraActor(mCameraForChildren); // use camera that covers render target exactly + } +} + +void EffectsView::RemoveRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + taskList.RemoveTask(mRenderTaskForChildren); + taskList.RemoveTask(mRenderTaskForResult); + + const size_t numFilters( mFilters.size() ); + for( size_t i = 0; i < numFilters; ++i ) + { + mFilters[i]->Disable(); + } +} + +void EffectsView::RefreshRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + if( mRenderTaskForChildren ) + { + mRenderTaskForChildren.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + + if( mRenderTaskForResult ) + { + mRenderTaskForResult.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + + const size_t numFilters( mFilters.size() ); + for( size_t i = 0; i < numFilters; ++i ) + { + mFilters[i]->Refresh(); + } +} + +void EffectsView::RemoveFilters() +{ + const size_t numFilters( mFilters.size() ); + for( size_t i = 0; i < numFilters; ++i ) + { + delete mFilters[i]; + } + mFilters.clear(); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/effects-view/effects-view-impl.h b/dali-toolkit/internal/controls/effects-view/effects-view-impl.h new file mode 100644 index 0000000..560a71b --- /dev/null +++ b/dali-toolkit/internal/controls/effects-view/effects-view-impl.h @@ -0,0 +1,275 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_EFFECTS_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_EFFECTS_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class GaussianBlurView; +class ImageFilter; + +/** + * EffectsView implementation class + * @copydoc Dali::Toolkit::EffectsView + */ +class EffectsView : public ControlImpl +{ +public: + /// @copydoc Dali::Toolkit::EffectsView New() + static Toolkit::EffectsView New(); + + /** + * Construct a new EffectsView. + * @copydoc Toolkit::EffectsView New() + */ + EffectsView(); + + /** + * Constructor. + * @copydoc Toolkit::EffectsView New(const unsigned int,const float,const Pixel::Format,const float,const float) + */ + EffectsView(const unsigned int numSamples, const float blurBellCurveWidth, const int spread, + const Pixel::Format pixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + FrameBufferImage image); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~EffectsView(); + +public: + + /// @copydoc Dali::Toolkit::EffectsView::SetType + void SetType( Toolkit::EffectsView::EffectType type ); + + /// @copydoc Dali::Toolkit::EffectsView::GetType + Toolkit::EffectsView::EffectType GetType() const; + + /// @copydoc Dali::Toolkit::EffectsView::Enable + void Enable(); + + /// @copydoc Dali::Toolkit::EffectsView::Disable + void Disable(); + + /// @copydoc Dali::Toolkit::EffectsView::Refresh + void Refresh(); + + /// @copydoc Dali::Toolkit::EffectsView::SetRefreshOnDemand + void SetRefreshOnDemand( bool onDemand ); + + /// @copydoc Dali::Toolkit::EffectsView::SetPixelFormat + void SetPixelFormat( Pixel::Format pixelFormat ); + + /// @copydoc Dali::Toolkit::EffectsView::SetOutputImage + void SetOutputImage( FrameBufferImage image ); + + /// @copydoc Dali::Toolkit::EffectsView::GetOutputImage + FrameBufferImage GetOutputImage(); + + /// @copydoc Dali::Toolkit::EffectsView::GetEffectSizePropertyIndex + Property::Index GetEffectSizePropertyIndex() const; + + /// @copydoc Dali::Toolkit::EffectsView::GetEffectStrengthPropertyIndex + Property::Index GetEffectStrengthPropertyIndex() const; + + /// @copydoc Dali::Toolkit::EffectsView::GetEffectOffsetPropertyIndex + Property::Index GetEffectOffsetPropertyIndex() const; + + /// @copydoc Dali::Toolkit::EffectsView::GetEffectColorPropertyIndex + Property::Index GetEffectColorPropertyIndex() const; + + /// @copydoc Dali::Toolkit::EffectsView::SetBackgroundColor(const Vector4&) + void SetBackgroundColor( const Vector4& color ); + + /// @copydoc Dali::Toolkit::GaussianBlurView::GetBackgroundColor + Vector4 GetBackgroundColor() const; + +private: + /** + * Register and setup indices for EffectsView properties + */ + void SetupProperties(); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnControlSizeSet( const Vector3& targetSize ) + */ + virtual void OnControlSizeSet( const Vector3& targetSize ); + +private: + + /** + * Callback received when the control is disconnected from the stage. + */ + void OnStageDisconnection(); + + /** + * Setup image filters + */ + void SetupFilters(); + + /** + * Allocate resources + */ + void AllocateResources(); + + /** + * Setup cameras + */ + void SetupCameras(); + + /** + * Create render tasks for internal jobs + */ + void CreateRenderTasks(); + + /** + * Remove render tasks + */ + void RemoveRenderTasks(); + + /** + * Refresh render tasks + */ + void RefreshRenderTasks(); + + /** + * Remove ImageFilters + */ + void RemoveFilters(); + +private: + + // Undefined + EffectsView( const EffectsView& ); + + // Undefined + EffectsView& operator = ( const EffectsView& ); + +private: // attributes/properties + Toolkit::EffectsView::EffectType mEffectType; + + ///////////////////////////////////////////////////////////// + // for rendering all user added children to offscreen target + FrameBufferImage mImageForChildren; + ImageActor mActorForChildren; + RenderTask mRenderTaskForChildren; + CameraActor mCameraForChildren; + + ///////////////////////////////////////////////////////////// + Pixel::Format mPixelFormat; ///< pixel format used by render targets + + ///////////////////////////////////////////////////////////// + // downsampling is used for the separated blur passes to get increased blur with the same number of samples and also to make rendering quicker + float mSpread; + + ///////////////////////////////////////////////////////////// + // background fill color + Vector4 mBackgroundColor; + + ///////////////////////////////////////////////////////////// + // for checking if we need to reallocate render targets + Vector2 mTargetSize; + Vector2 mLastSize; + + bool mRefreshOnDemand; + + ///////////////////////////////////////////////////////////// + // horizontal spread objects + FrameBufferImage mImageForHorzSpread; + ImageActor mActorForHorzSpread; + RenderTask mRenderTaskForHorzSpread; + + ///////////////////////////////////////////////////////////// + // vertical spread objects + FrameBufferImage mImageForVertSpread; + ImageActor mActorForVertSpread; + RenderTask mRenderTaskForVertSpread; + + CameraActor mCameraForSpread; + + ///////////////////////////////////////////////////////////// + // post blur image + FrameBufferImage mImagePostFilter; + ImageActor mActorPostFilter; + + ///////////////////////////////////////////////////////////// + // final image + FrameBufferImage mImageForResult; + Actor mActorForResult; + RenderTask mRenderTaskForResult; + + Property::Index mEffectSizePropertyIndex; + Property::Index mEffectStrengthPropertyIndex; + Property::Index mEffectOffsetPropertyIndex; + Property::Index mEffectColorPropertyIndex; + + std::vector mFilters; +}; // class EffectsView + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::EffectsView& GetImpl( Toolkit::EffectsView& effectsView ) +{ + DALI_ASSERT_ALWAYS( effectsView ); + + Dali::RefObject& handle = effectsView.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::EffectsView& GetImpl( const Toolkit::EffectsView& effectsView ) +{ + DALI_ASSERT_ALWAYS( effectsView ); + + const Dali::RefObject& handle = effectsView.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_EFFECTS_VIEW_H__ + diff --git a/dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp b/dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp new file mode 100644 index 0000000..1ffa7af --- /dev/null +++ b/dali-toolkit/internal/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp @@ -0,0 +1,661 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "gaussian-blur-view-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include + +#include + +// TODO: +// pixel format / size - set from JSON +// aspect ratio property needs to be able to be constrained also for cameras, not possible currently. Therefore changing aspect ratio of GaussianBlurView won't currently work +// default near clip value +// mChildrenRoot Add()/Remove() overloads - better solution +// Manager object - re-use render targets if there are multiple GaussianBlurViews created + + + +///////////////////////////////////////////////////////// +// IMPLEMENTATION NOTES + +// As the GaussianBlurView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:- +// OnSetSize() does not get called when GaussianBlurView object size is modified using a Constraint. +// OnSizeAnimation() only gets called once per AnimateTo/By() and if an Animation has N such calls then only the final one will end up being used. Therefore we can't use +// OnSizeAnimation() to alter render target sizes. +// To get around the above problems, we use fixed sized render targets, from the last SetSize() call (which calls OnSetSize()), then we adjust the internal cameras / actors +// to take account of the changed GaussianBlurView object size, projecting to the unchanged render target sizes. This is done relative to the fixed render target / actor sizes +// by using constraints relative to the GaussianBlurView actor size. + + +// 2 modes: +// 1st mode, this control has a tree of actors (use Add() to add children) that are rendered and blurred. +// mRenderChildrenTask renders children to FB mRenderTargetForRenderingChildren +// mHorizBlurTask renders mImageActorHorizBlur Actor showing FB mRenderTargetForRenderingChildren into FB mRenderTarget2 +// mVertBlurTask renders mImageActorVertBlur Actor showing FB mRenderTarget2 into FB mRenderTarget1 +// mCompositeTask renders mImageActorComposite Actor showing FB mRenderTarget1 into FB mRenderTargetForRenderingChildren +// +// 2nd mode, an image is blurred and rendered to a supplied target framebuffer +// mHorizBlurTask renders mImageActorHorizBlur Actor showing mUserInputImage into FB mRenderTarget2 +// mVertBlurTask renders mImageActorVertBlur Actor showing mRenderTarget2 into FB mUserOutputRenderTarget +// +// Only this 2nd mode handles ActivateOnce + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +using namespace Dali; + +BaseHandle Create() +{ + return Toolkit::GaussianBlurView::New(); +} + +TypeRegistration mType( typeid(Toolkit::GaussianBlurView), typeid(Toolkit::Control), Create ); + + +const unsigned int GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES = 5; +const float GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH = 1.5f; +const Pixel::Format GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT = Pixel::RGBA8888; +const float GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH = 1.0f; // default, fully blurred +const std::string GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME("GaussianBlurStrengthPropertyName"); +const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE = 0.5f; +const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE = 0.5f; + +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; + +const char* const GAUSSIAN_BLUR_FRAGMENT_SOURCE = + "uniform vec2 uSampleOffsets[NUM_SAMPLES];\n" + "uniform float uSampleWeights[NUM_SAMPLES];\n" + + "void main()\n" + "{\n" + " mediump vec4 col;\n" + " col = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y) + uSampleOffsets[0]) * uSampleWeights[0]; \n" + " for (int i=1; iInitialize(); + + return handle; +} + +Toolkit::GaussianBlurView GaussianBlurView::New(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + bool blurUserImage) +{ + GaussianBlurView* impl = new GaussianBlurView( numSamples, blurBellCurveWidth, renderTargetPixelFormat, + downsampleWidthScale, downsampleHeightScale, + blurUserImage); + + Dali::Toolkit::GaussianBlurView handle = Dali::Toolkit::GaussianBlurView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +///////////////////////////////////////////////////////////// +// for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks +// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root. +void GaussianBlurView::Add(Actor child) +{ + mChildrenRoot.Add(child); +} + +void GaussianBlurView::Remove(Actor child) +{ + mChildrenRoot.Remove(child); +} + +void GaussianBlurView::SetUserImageAndOutputRenderTarget(Image inputImage, FrameBufferImage outputRenderTarget) +{ + // can only do this if the GaussianBlurView object was created with this parameter set + DALI_ASSERT_ALWAYS(mBlurUserImage); + + mUserInputImage = inputImage; + mImageActorHorizBlur.SetImage( mUserInputImage ); + + mUserOutputRenderTarget = outputRenderTarget; +} + +FrameBufferImage GaussianBlurView::GetBlurredRenderTarget() const +{ + if(!mUserOutputRenderTarget) + { + return mRenderTargetForRenderingChildren; + } + + return mUserOutputRenderTarget; +} + +void GaussianBlurView::SetBackgroundColor( const Vector4& color ) +{ + mBackgroundColor = color; +} + +Vector4 GaussianBlurView::GetBackgroundColor() const +{ + return mBackgroundColor; +} + +/////////////////////////////////////////////////////////// +// +// Private methods +// + +/** + * EqualToConstraintFloat + * + * f(current, property) = property + */ +struct EqualToConstraintFloat +{ + EqualToConstraintFloat(){} + + float operator()(const float current, const PropertyInput& property) {return property.GetFloat();} +}; + +void GaussianBlurView::OnInitialize() +{ + // root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task + mChildrenRoot.SetParentOrigin(ParentOrigin::CENTER); + mChildrenRoot.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as GaussianBlurView object + + + ////////////////////////////////////////////////////// + // Create shaders + + // horiz + std::ostringstream horizFragmentShaderStringStream; + horizFragmentShaderStringStream << "#define NUM_SAMPLES " << mNumSamples << "\n"; + horizFragmentShaderStringStream << GAUSSIAN_BLUR_FRAGMENT_SOURCE; + mHorizBlurShader = ShaderEffect::New( "", horizFragmentShaderStringStream.str() ); + // vert + std::ostringstream vertFragmentShaderStringStream; + vertFragmentShaderStringStream << "#define NUM_SAMPLES " << mNumSamples << "\n"; + vertFragmentShaderStringStream << GAUSSIAN_BLUR_FRAGMENT_SOURCE; + mVertBlurShader = ShaderEffect::New( "", vertFragmentShaderStringStream.str() ); + + + ////////////////////////////////////////////////////// + // Create actors + + // Create an ImageActor for performing a horizontal blur on the texture + mImageActorHorizBlur = ImageActor::New(); + mImageActorHorizBlur.SetParentOrigin(ParentOrigin::CENTER); + mImageActorHorizBlur.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + mImageActorHorizBlur.SetShaderEffect( mHorizBlurShader ); + + // Create an ImageActor for performing a vertical blur on the texture + mImageActorVertBlur = ImageActor::New(); + mImageActorVertBlur.SetParentOrigin(ParentOrigin::CENTER); + mImageActorVertBlur.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + mImageActorVertBlur.SetShaderEffect( mVertBlurShader ); + + // Register a property that the user can control to fade the blur in / out via the GaussianBlurView object + mBlurStrengthPropertyIndex = Self().RegisterProperty(GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH); + + // Create an ImageActor for compositing the blur and the original child actors render + if(!mBlurUserImage) + { + mImageActorComposite = ImageActor::New(); + mImageActorComposite.SetParentOrigin(ParentOrigin::CENTER); + mImageActorComposite.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as GaussianBlurView object + mImageActorComposite.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + mImageActorComposite.SetOpacity(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH); // ensure alpha is enabled for this object and set default value + + Constraint blurStrengthConstraint = Constraint::New( Actor::COLOR_ALPHA, ParentSource(mBlurStrengthPropertyIndex), EqualToConstraintFloat()); + mImageActorComposite.ApplyConstraint(blurStrengthConstraint); + + // Create an ImageActor for holding final result, i.e. the blurred image. This will get rendered to screen later, via default / user render task + mTargetActor = ImageActor::New(); + mTargetActor.SetParentOrigin(ParentOrigin::CENTER); + mTargetActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); // same size as GaussianBlurView object + mTargetActor.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); // FIXME + + + ////////////////////////////////////////////////////// + // Create cameras for the renders corresponding to the view size + mRenderFullSizeCamera = CameraActor::New(); + mRenderFullSizeCamera.SetParentOrigin(ParentOrigin::CENTER); + + + ////////////////////////////////////////////////////// + // Connect to actor tree + Self().Add( mImageActorComposite ); + Self().Add( mTargetActor ); + Self().Add( mRenderFullSizeCamera ); + } + + + ////////////////////////////////////////////////////// + // Create camera for the renders corresponding to the (potentially downsampled) render targets' size + mRenderDownsampledCamera = CameraActor::New(); + mRenderDownsampledCamera.SetParentOrigin(ParentOrigin::CENTER); + + + ////////////////////////////////////////////////////// + // Connect to actor tree + Self().Add( mChildrenRoot ); + Self().Add( mImageActorHorizBlur ); + Self().Add( mImageActorVertBlur ); + Self().Add( mRenderDownsampledCamera ); +} + + +/** + * ZrelativeToYconstraint + * + * f(current, property, scale) = Vector3(current.x, current.y, property.y * scale) + */ +struct ZrelativeToYconstraint +{ + ZrelativeToYconstraint( float scale ) + : mScale( scale ) + {} + + Vector3 operator()(const Vector3& current, + const PropertyInput& property) + { + Vector3 v; + + v.x = current.x; + v.y = current.y; + v.z = property.GetVector3().y * mScale; + + return v; + } + + float mScale; +}; + +void GaussianBlurView::OnControlSizeSet(const Vector3& targetSize) +{ + mTargetSize = Vector2(targetSize); + + // if we are already on stage, need to update render target sizes now to reflect the new size of this actor + if(Self().OnStage()) + { + AllocateResources(); + } +} + +void GaussianBlurView::AllocateResources() +{ + // size of render targets etc is based on the size of this actor, ignoring z + if(mTargetSize != mLastSize) + { + mLastSize = mTargetSize; + + // get size of downsampled render targets + mDownsampledWidth = mTargetSize.width * mDownsampleWidthScale; + mDownsampledHeight = mTargetSize.height * mDownsampleHeightScale; + + // Create and place a camera for the renders corresponding to the (potentially downsampled) render targets' size + mRenderDownsampledCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it + mRenderDownsampledCamera.SetNearClippingPlane(1.0f); + mRenderDownsampledCamera.SetAspectRatio(mDownsampledWidth / mDownsampledHeight); + mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + // Point the camera back into the scene + mRenderDownsampledCamera.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + + mRenderDownsampledCamera.SetPosition(0.0f, 0.0f, ((mDownsampledHeight * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))); + + // setup for normal operation + if(!mBlurUserImage) + { + // Create and place a camera for the children render, corresponding to its render target size + mRenderFullSizeCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it + mRenderFullSizeCamera.SetNearClippingPlane(1.0f); + mRenderFullSizeCamera.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mRenderFullSizeCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + // Point the camera back into the scene + mRenderFullSizeCamera.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + + float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f); + mRenderFullSizeCamera.SetPosition(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale); + + // Children render camera must move when GaussianBlurView object is resized. This is since we cannot change render target size - so we need to remap the child actors' rendering + // accordingly so they still exactly fill the render target. Note that this means the effective resolution of the child render changes as the GaussianBlurView object changes + // size, this is the trade off for not being able to modify render target size + // Change camera z position based on GaussianBlurView actor height + mRenderFullSizeCamera.RemoveConstraints(); + mRenderFullSizeCamera.ApplyConstraint( Constraint::New( Actor::POSITION, ParentSource( Actor::SIZE ), ZrelativeToYconstraint(cameraPosConstraintScale) ) ); + + // create offscreen buffer of new size to render our child actors to + mRenderTargetForRenderingChildren = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Dali::Image::Unused ); + + // Set ImageActor for performing a horizontal blur on the texture + mImageActorHorizBlur.SetImage( mRenderTargetForRenderingChildren ); + + // Create offscreen buffer for vert blur pass + mRenderTarget1 = FrameBufferImage::New( mDownsampledWidth, mDownsampledHeight, mPixelFormat, Dali::Image::Unused ); + + // use the completed blur in the first buffer and composite with the original child actors render + mImageActorComposite.SetImage( mRenderTarget1 ); + + // set up target actor for rendering result, i.e. the blurred image + mTargetActor.SetImage(mRenderTargetForRenderingChildren); + } + + // Create offscreen buffer for horiz blur pass + mRenderTarget2 = FrameBufferImage::New( mDownsampledWidth, mDownsampledHeight, mPixelFormat, Dali::Image::Unused ); + + // size needs to match render target + mImageActorHorizBlur.SetSize(mDownsampledWidth, mDownsampledHeight); + + // size needs to match render target + mImageActorVertBlur.SetImage( mRenderTarget2 ); + mImageActorVertBlur.SetSize(mDownsampledWidth, mDownsampledHeight); + + // set gaussian blur up for new sized render targets + SetShaderConstants(); + } +} + +void GaussianBlurView::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + if(!mBlurUserImage) + { + // create render task to render our child actors to offscreen buffer + mRenderChildrenTask = taskList.CreateTask(); + mRenderChildrenTask.SetSourceActor( mChildrenRoot ); + mRenderChildrenTask.SetExclusive(true); + mRenderChildrenTask.SetInputEnabled( false ); + mRenderChildrenTask.SetClearEnabled( true ); + mRenderChildrenTask.SetClearColor( mBackgroundColor ); + + mRenderChildrenTask.SetCameraActor(mRenderFullSizeCamera); + mRenderChildrenTask.SetTargetFrameBuffer( mRenderTargetForRenderingChildren ); + } + + // perform a horizontal blur targeting the second buffer + mHorizBlurTask = taskList.CreateTask(); + mHorizBlurTask.SetSourceActor( mImageActorHorizBlur ); + mHorizBlurTask.SetExclusive(true); + mHorizBlurTask.SetInputEnabled( false ); + mHorizBlurTask.SetClearEnabled( true ); + mHorizBlurTask.SetClearColor( mBackgroundColor ); + if( mRenderOnce && mBlurUserImage ) + { + mHorizBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE); + } + + // use the second buffer and perform a horizontal blur targeting the first buffer + mVertBlurTask = taskList.CreateTask(); + mVertBlurTask.SetSourceActor( mImageActorVertBlur ); + mVertBlurTask.SetExclusive(true); + mVertBlurTask.SetInputEnabled( false ); + mVertBlurTask.SetClearEnabled( true ); + mVertBlurTask.SetClearColor( mBackgroundColor ); + if( mRenderOnce && mBlurUserImage ) + { + mVertBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE); + mVertBlurTask.FinishedSignal().Connect( this, &GaussianBlurView::OnRenderTaskFinished ); + } + + // use the completed blur in the first buffer and composite with the original child actors render + if(!mBlurUserImage) + { + mCompositeTask = taskList.CreateTask(); + mCompositeTask.SetSourceActor( mImageActorComposite ); + mCompositeTask.SetExclusive(true); + mCompositeTask.SetInputEnabled( false ); + + mCompositeTask.SetCameraActor(mRenderFullSizeCamera); + mCompositeTask.SetTargetFrameBuffer( mRenderTargetForRenderingChildren ); + } + + mHorizBlurTask.SetCameraActor(mRenderDownsampledCamera); + mVertBlurTask.SetCameraActor(mRenderDownsampledCamera); + + mHorizBlurTask.SetTargetFrameBuffer( mRenderTarget2 ); + if(mUserOutputRenderTarget) + { + mVertBlurTask.SetTargetFrameBuffer( mUserOutputRenderTarget ); + } + else + { + mVertBlurTask.SetTargetFrameBuffer( mRenderTarget1 ); + } +} + +void GaussianBlurView::RemoveRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + taskList.RemoveTask(mRenderChildrenTask); + taskList.RemoveTask(mHorizBlurTask); + taskList.RemoveTask(mVertBlurTask); + taskList.RemoveTask(mCompositeTask); +} + +void GaussianBlurView::OnStageDisconnection() +{ + // TODO: can't call this here, since SetImage() calls fails similarly to above + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Deactivate(); +} + +void GaussianBlurView::OnControlStageConnection() +{ + // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Activate(); +} + +void GaussianBlurView::Activate() +{ + // make sure resources are allocated and start the render tasks processing + AllocateResources(); + CreateRenderTasks(); +} + +void GaussianBlurView::ActivateOnce() +{ + DALI_ASSERT_ALWAYS(mBlurUserImage); // Only works with blurring image mode. + mRenderOnce = true; + Activate(); +} + +void GaussianBlurView::Deactivate() +{ + // stop render tasks processing + // Note: render target resources are automatically freed since we set the Image::Unused flag + RemoveRenderTasks(); + mRenderOnce = false; +} + +void GaussianBlurView::SetBlurBellCurveWidth(float blurBellCurveWidth) +{ + // a value of zero leads to undefined Gaussian weights, do not allow user to do this + mBlurBellCurveWidth = std::max( blurBellCurveWidth, 0.001f ); +} + +float GaussianBlurView::CalcGaussianWeight(float x) +{ + return (1.0f / sqrt(2.0f * Math::PI * mBlurBellCurveWidth)) * exp(-(x * x) / (2.0f * mBlurBellCurveWidth * mBlurBellCurveWidth)); +} + +void GaussianBlurView::SetShaderConstants() +{ + Vector2 *uvOffsets; + float ofs; + float *weights; + float w, totalWeights; + unsigned int i; + + uvOffsets = new Vector2[mNumSamples + 1]; + weights = new float[mNumSamples + 1]; + + totalWeights = weights[0] = CalcGaussianWeight(0); + uvOffsets[0].x = 0.0f; + uvOffsets[0].y = 0.0f; + + for(i=0; i> 1; i++) + { + w = CalcGaussianWeight((float)(i + 1)); + weights[(i << 1) + 1] = w; + weights[(i << 1) + 2] = w; + totalWeights += w * 2.0f; + + // offset texture lookup to between texels, that way the bilinear filter in the texture hardware will average two samples with one lookup + ofs = ((float)(i << 1)) + 1.5f; + + // get offsets from units of pixels into uv coordinates in [0..1] + float ofsX = ofs / mDownsampledWidth; + float ofsY = ofs / mDownsampledHeight; + uvOffsets[(i << 1) + 1].x = ofsX; + uvOffsets[(i << 1) + 1].y = ofsY; + + uvOffsets[(i << 1) + 2].x = -ofsX; + uvOffsets[(i << 1) + 2].y = -ofsY; + } + + for(i=0; i +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class GaussianBlurView; + +namespace Internal +{ + +/** + * GaussianBlurView implementation class + */ +class GaussianBlurView : public ControlImpl +{ +public: + + /** + * @copydoc Dali::Toolkit::GaussianBlurView::GaussianBlurView + */ + GaussianBlurView(); + + /** + * @copydoc Dali::Toolkit::GaussianBlurView::GaussianBlurView + */ + GaussianBlurView(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + bool blurUserImage); + + /** + * @copydoc Dali::Toolkit::GaussianBlurView::~GaussianBlurView + */ + virtual ~GaussianBlurView(); + + /** + * @copydoc Dali::Toolkit::GaussianBlurView::New + */ + static Dali::Toolkit::GaussianBlurView New(); + static Dali::Toolkit::GaussianBlurView New( const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + bool blurUserImage); + + void Add(Actor child); + void Remove(Actor child); + + void Activate(); + void ActivateOnce(); + void Deactivate(); + + void SetUserImageAndOutputRenderTarget(Image inputImage, FrameBufferImage outputRenderTarget); + + Property::Index GetBlurStrengthPropertyIndex() const {return mBlurStrengthPropertyIndex;} + FrameBufferImage GetBlurredRenderTarget() const; + + /// @copydoc Dali::Toolkit::GaussianBlurView::SetBackgroundColor(const Vector4&) + void SetBackgroundColor( const Vector4& color ); + + /// @copydoc Dali::Toolkit::GaussianBlurView::GetBackgroundColor + Vector4 GetBackgroundColor() const; + + void AllocateResources(); + void CreateRenderTasks(); + void RemoveRenderTasks(); + Dali::Toolkit::GaussianBlurView::GaussianBlurViewSignal& FinishedSignal(); + +private: + + virtual void OnInitialize(); + virtual void OnControlSizeSet(const Vector3& targetSize); + virtual void OnStageDisconnection(); + + virtual void OnControlStageConnection(); + + void SetBlurBellCurveWidth(float blurBellCurveWidth); + float CalcGaussianWeight(float x); + void SetShaderConstants(); + std::string GetSampleOffsetsPropertyName( unsigned int index ) const; + std::string GetSampleWeightsPropertyName( unsigned int index ) const; + + void OnRenderTaskFinished(Dali::RenderTask& renderTask); + + ///////////////////////////////////////////////////////////// + unsigned int mNumSamples; // number of blur samples in each of horiz/vert directions + float mBlurBellCurveWidth; // constant used when calculating the gaussian weights + Pixel::Format mPixelFormat; // pixel format used by render targets + + ///////////////////////////////////////////////////////////// + // downsampling is used for the separated blur passes to get increased blur with the same number of samples and also to make rendering quicker + float mDownsampleWidthScale; + float mDownsampleHeightScale; + float mDownsampledWidth; + float mDownsampledHeight; + + ///////////////////////////////////////////////////////////// + // if this is set to true, we blur a user supplied image rather than rendering and blurring children + bool mBlurUserImage:1; + + ///////////////////////////////////////////////////////////// + // if this is set to true, set the render tasks to refresh once + bool mRenderOnce:1; + + ///////////////////////////////////////////////////////////// + // background fill color + Vector4 mBackgroundColor; + + ///////////////////////////////////////////////////////////// + // for checking if we need to reallocate render targets + Vector2 mTargetSize; + Vector2 mLastSize; + + ///////////////////////////////////////////////////////////// + // for creating a subtree for all user added child actors, so that we can have them exclusive to the mRenderChildrenTask and our other actors exclusive to our other tasks + Actor mChildrenRoot; + + ///////////////////////////////////////////////////////////// + // for mapping offscreen renders to render target sizes + CameraActor mRenderFullSizeCamera; + CameraActor mRenderDownsampledCamera; + + ///////////////////////////////////////////////////////////// + // for rendering all user added children to offscreen target + FrameBufferImage mRenderTargetForRenderingChildren; + RenderTask mRenderChildrenTask; + + ///////////////////////////////////////////////////////////// + // for rendering separated blur passes to offscreen targets + FrameBufferImage mRenderTarget1; + FrameBufferImage mRenderTarget2; + + ShaderEffect mHorizBlurShader; + ShaderEffect mVertBlurShader; + + ImageActor mImageActorHorizBlur; + ImageActor mImageActorVertBlur; + + RenderTask mHorizBlurTask; + RenderTask mVertBlurTask; + + ///////////////////////////////////////////////////////////// + // for compositing blur and children renders to offscreen target + ImageActor mImageActorComposite; + RenderTask mCompositeTask; + + ///////////////////////////////////////////////////////////// + // for holding blurred result + ImageActor mTargetActor; + + ///////////////////////////////////////////////////////////// + // for animating fade in / out of blur, hiding internal implementation but allowing user to set via GaussianBlurView interface + Property::Index mBlurStrengthPropertyIndex; + + ///////////////////////////////////////////////////////////// + // User can specify image to blur and output target, so we can use GaussianBlurView for arbitrary blur processes + Image mUserInputImage; + FrameBufferImage mUserOutputRenderTarget; + + Dali::Toolkit::GaussianBlurView::GaussianBlurViewSignal mFinishedSignal; ///< Signal emitted when blur has completed. +private: + + // Undefined copy constructor. + GaussianBlurView( const GaussianBlurView& ); + + // Undefined assignment operator. + GaussianBlurView& operator=( const GaussianBlurView& ); +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods +inline Toolkit::Internal::GaussianBlurView& GetImpl( Toolkit::GaussianBlurView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +inline const Toolkit::Internal::GaussianBlurView& GetImpl( const Toolkit::GaussianBlurView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + const Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_GAUSSIAN_BLUR_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/image-view/image-view-impl.cpp b/dali-toolkit/internal/controls/image-view/image-view-impl.cpp new file mode 100644 index 0000000..52b2536 --- /dev/null +++ b/dali-toolkit/internal/controls/image-view/image-view-impl.cpp @@ -0,0 +1,223 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace +{ +//Type registration +BaseHandle Create() +{ + return Toolkit::ImageView::New(); +} +TypeRegistration mType( typeid(Toolkit::ImageView), typeid(Toolkit::Control), Create ); + + /** + * CameraDetailConstraint, generates detail value + * based on camera's position and ImageView's position. + */ + struct CameraDetailConstraint + { + CameraDetailConstraint(float detailFactor) + : mDetailFactor(detailFactor) + { + + } + + float operator()(const float& current, + const PropertyInput& propertyTargetPosition, + const PropertyInput& propertySourcePosition) + { + const Vector3& targetPosition = propertyTargetPosition.GetVector3(); + const Vector3& sourcePosition = propertySourcePosition.GetVector3(); + const float distance = (targetPosition - sourcePosition).Length(); + const float detail = mDetailFactor / distance; + + return detail; + } + + const float mDetailFactor; + }; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ImageView +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::ImageView ImageView::New() +{ + // Create the implementation + ImageViewPtr imageView(new ImageView()); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::ImageView handle(*imageView); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + imageView->Initialize(); + + return handle; +} + +ImageView::ImageView() +: ControlImpl(true) +{ +} + +void ImageView::Initialize() +{ + Actor self = Self(); + // Register property that represents the level of detail. + mPropertyDetail = self.RegisterProperty(Toolkit::ImageView::DETAIL_PROPERTY_NAME, 0.0f); + + // Create an empty image actor, filling the entire size of this ImageView. + Image emptyImage; + mImageActor = ImageActor::New( emptyImage ); + self.Add( mImageActor ); + mImageActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mImageActor.SetParentOrigin( ParentOrigin::CENTER ); +} + +ImageView::~ImageView() +{ + +} + +void ImageView::SetImage(const std::string& filename, ImageType type, float min, float max) +{ + switch(type) + { + case Toolkit::ImageView::BitmapType: + { + SetImageBitmap(filename, min, max); + break; + } + case Toolkit::ImageView::DistanceFieldType: + { + SetImageDistanceField(filename); + break; + } + } +} + +void ImageView::SetImageBitmap(const std::string& filename, float min, float max) +{ + int minLevel = ceilf(logf(min) / logf(2.0f)); + int maxLevel = ceilf(logf(max) / logf(2.0f)); + + ImageAttributes attributes; + const Vector3 size = Self().GetCurrentSize(); + + if(minLevel==maxLevel) + { // Single image detail level, no need for any notifications. + const float detail = powf(2.0f, maxLevel); + attributes.SetSize( size.x * detail, size.y * detail ); + Image image = Image::New( filename, attributes); + mImageActor.SetImage( image ); + } + else + { // Multi image detail level... + for( int level = minLevel; level <= maxLevel; level++) + { + const float minDetail = powf(2.0f, level - 1); + const float maxDetail = powf(2.0f, level); + ImageRequest req(filename, size.x * maxDetail, size.y * maxDetail ); + + if(level==minLevel) + { + AddImage(req, LessThanCondition(maxDetail) ); + } + else if(level==maxLevel) + { + AddImage(req, GreaterThanCondition(minDetail) ); + } + else + { + AddImage(req, InsideCondition(minDetail, maxDetail) ); + } + } + } +} + +void ImageView::SetImageDistanceField(const std::string& filename) +{ + ImageAttributes attributes = Dali::ImageAttributes::NewDistanceField(1.0f, 1); + const Vector3 size = Self().GetCurrentSize(); + + attributes.SetSize( size.x, size.y ); + Image image = Image::NewDistanceField(filename, attributes); + mImageActor.SetImage( image ); + + DistanceFieldEffect effect = DistanceFieldEffect::New(); + Self().SetShaderEffect( effect ); +} + +void ImageView::SetImage(Image image) +{ + mImageActor.SetImage( image ); +} + +void ImageView::AddImage(ImageRequest& req, PropertyCondition condition) +{ + Actor self = Self(); + + PropertyNotification notification = self.AddPropertyNotification( mPropertyDetail, condition ); + + notification.NotifySignal().Connect( this, &ImageView::OnDetailChange ); + + mNotifications[notification] = req; +} + +void ImageView::SetDetail(float detail) +{ + Self().SetProperty( mPropertyDetail, detail ); +} + +void ImageView::SetCameraActor(CameraActor camera, float detailFactor) +{ + Constraint constraint = Constraint::New( mPropertyDetail, + LocalSource( Actor::WORLD_POSITION ), + Source( camera, Actor::WORLD_POSITION ), + CameraDetailConstraint(detailFactor)); + Self().RemoveConstraints(); + Self().ApplyConstraint(constraint); +} + +void ImageView::OnDetailChange( PropertyNotification& notification ) +{ + ImageRequest& req = mNotifications[notification]; + Image image = Image::New( req.mFilename, req.mAttributes ); + mImageActor.SetImage( image ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/image-view/image-view-impl.h b/dali-toolkit/internal/controls/image-view/image-view-impl.h new file mode 100644 index 0000000..9c0f89a --- /dev/null +++ b/dali-toolkit/internal/controls/image-view/image-view-impl.h @@ -0,0 +1,208 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_ImageView_H__ +#define __DALI_TOOLKIT_INTERNAL_ImageView_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class ImageView; + +typedef IntrusivePtr ImageViewPtr; + +/** + * @copydoc Toolkit::ImageView + */ +class ImageView : public ControlImpl +{ +public: + + typedef Toolkit::ImageView::ImageType ImageType; + + /** + * ImageRequest element + * represents an image to be loaded and displayed + * with given attributes. + */ + struct ImageRequest + { + /** + * Default constructor + */ + ImageRequest() + { + } + + /** + * @param[in] filename to load + * @param[in] width Width of image. + * @param[in] height Height of image. + */ + ImageRequest( const std::string& filename, unsigned int width, unsigned int height ) + : mFilename( filename ) + { + mAttributes.SetSize( width, height ); + } + + std::string mFilename; ///< filename of image + ImageAttributes mAttributes; ///< attributes of image + }; + +public: + + /** + * Create a new ImageView. + * @return A public handle to the newly allocated ImageView. + */ + static Dali::Toolkit::ImageView New(); + +public: + + /** + * @copydoc Toolkit::ImageView::SetImage(const std::string& filename, ImageType type, float min, float max) + */ + void SetImage(const std::string& filename, ImageType type, float min, float max); + + /** + * @copydoc Toolkit::ImageView::SetImage(Image& image); + */ + void SetImage(Image image); + + /** + * Adds an image to displayed at a detail range. + * + * @note If two or more images are specified to be displayed at + * the same overlapping range. Then the last image that was added + * will be displayed. + * + * @param[in] req The image to load and display + * @param[in] condition The detail condition to be satisified for the image to display + */ + void AddImage(ImageRequest& req, PropertyCondition condition); + + /** + * @copydoc Toolkit::ImageView::SetCameraActor + */ + void SetCameraActor(CameraActor camera, float detailFactor); + + /** + * @copydoc Toolkit::ImageView::SetDetail + */ + void SetDetail(float detail); + +protected: + + /** + * Construct a new ImageView. + */ + ImageView(); + + /** + * 2nd-phase initialization. + */ + void Initialize(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ImageView(); + +private: + + /** + * Sets a Bitmap Image as the image to display for this ImageView + * min and max represent the minimum and maximum sizes to load. + * sizes will be created at 2^n scale factor. where n goes from + * ceil(log2(min)) to ceil(log2(max)) + * + * @param[in] filename the image path to load + * @param[in] min the minimum size to load + * @param[in] max the maximum size to load + */ + void SetImageBitmap(const std::string& filename, float min, float max); + + /** + * Sets a Distance Field Image as the image to display for this ImageView + * + * @param[in] filename the image path to load + */ + void SetImageDistanceField(const std::string& filename); + + /** + * Invoked whenever the detail property passes a notification point. + * @param[in] notification The notification instance. + */ + virtual void OnDetailChange(PropertyNotification& notification ); + +private: + + // Undefined + ImageView(const ImageView&); + + // Undefined + ImageView& operator=(const ImageView& rhs); + +private: + + Property::Index mPropertyDetail; ///< Detail property, changing this affects the level of detail of the content. + ImageActor mImageActor; ///< Holding image actor for the various images at differing levels of detail. + std::map mNotifications; ///< Property Notification -> Image map table. + + PropertyNotification mPropertyNotification; ///< Property notification +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ImageView& GetImpl(Toolkit::ImageView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::ImageView& GetImpl(const Toolkit::ImageView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ImageView_H__ diff --git a/dali-toolkit/internal/controls/image-view/masked-image-view-impl.cpp b/dali-toolkit/internal/controls/image-view/masked-image-view-impl.cpp new file mode 100644 index 0000000..57b13b8 --- /dev/null +++ b/dali-toolkit/internal/controls/image-view/masked-image-view-impl.cpp @@ -0,0 +1,634 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +const char* CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::CUSTOM_PROPERTY_COUNT ] = +{ + "background-color", + "source-size", + "source-offset", + "mask-size", + "mask-offset" +}; + +const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE = + "precision mediump float; \n" + "uniform vec2 uTargetSize; \n" + "uniform vec2 uSourceSize; \n" + "uniform vec2 uSourceOffset; \n" + "uniform vec2 uMaskSize; \n" + "uniform vec2 uMaskOffset; \n" + "varying vec2 vMaskTexCoord; \n" + "void main() \n" + "{ \n" + " float x = uSourceSize.x*aPosition.x + uSourceOffset.x; \n" + " float y = uSourceSize.y*aPosition.y + uSourceOffset.y; \n" + " \n" + " gl_Position = vec4( x/(uTargetSize.x*0.5), y/(uTargetSize.y*0.5), 0.0, 1.0 ); \n" + " \n" + " vMaskTexCoord.x = (uMaskSize.x*0.5 + x - uMaskOffset.x) / uMaskSize.x; \n" + " vMaskTexCoord.y = (uMaskSize.y*0.5 + y - uMaskOffset.y) / uMaskSize.y; \n"; + +const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE0 = + " \n" + " vTexCoord = aTexCoord; \n" + "}"; + +const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE90 = + " \n" + " vTexCoord.x = aTexCoord.y; \n" + " vTexCoord.y = 1.0 - aTexCoord.x; \n" + "}"; + +const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE180 = + " \n" + " vTexCoord.x = 1.0 - aTexCoord.x; \n" + " vTexCoord.y = 1.0 - aTexCoord.y; \n" + "}"; + +const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE270 = + " \n" + " vTexCoord.x = 1.0 - aTexCoord.y; \n" + " vTexCoord.y = aTexCoord.x; \n" + "}"; + +const char* const MASKED_IMAGE_VIEW_FRAGMENT_SOURCE = + "precision mediump float; \n" + "varying vec2 vMaskTexCoord; \n" + "void main() \n" + "{ \n" + " highp vec4 mask = texture2D(sEffect, vMaskTexCoord); \n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * vec4(1,1,1,mask.a); \n" + "}"; + +Vector2 EqualToConstraintVector2( const Vector2& current, const PropertyInput& property ) +{ + return property.GetVector2(); +} + +Vector2 GetSizeForAspectRatio( const Vector2& targetSize, float aspectRatio ) +{ + Vector2 sizeToKeepAspectRatio( targetSize ); + + float targetAspectRatio( targetSize.width / targetSize.height ); + + if( aspectRatio > targetAspectRatio ) + { + sizeToKeepAspectRatio.width = sizeToKeepAspectRatio.height * aspectRatio; + } + else if ( aspectRatio < targetAspectRatio ) + { + sizeToKeepAspectRatio.height = sizeToKeepAspectRatio.width / aspectRatio; + } + + return sizeToKeepAspectRatio; +} + +Vector2 ClampSourceSize( const Vector2& sourceSize, const Vector2& targetSize, float widthOverHeight, float maxSourceScale ) +{ + Vector2 clampedSize( sourceSize ); + + Vector2 minSize( targetSize ); + if ( widthOverHeight > 0.0f ) + { + minSize = GetSizeForAspectRatio( targetSize, widthOverHeight ); + } + + if ( clampedSize.width < minSize.width || + clampedSize.height < minSize.height ) + { + clampedSize = minSize; + } + else if ( clampedSize.width > minSize.width *maxSourceScale || + clampedSize.height > minSize.height*maxSourceScale ) + { + clampedSize = minSize * maxSourceScale; + } + + return clampedSize; +} + +Vector2 ClampSourceOffset( const Vector2& sourceOffset, const Vector2& targetSize, const Vector2& sourceSize ) +{ + Vector2 min, max; + + if ( sourceSize.width > targetSize.width ) + { + float offset = (sourceSize.width - targetSize.width) * 0.5f; + min.x = -offset; + max.x = offset; + } + + if ( sourceSize.height > targetSize.height ) + { + float offset = (sourceSize.height - targetSize.height) * 0.5f; + min.y = -offset; + max.y = offset; + } + + return Vector2( Clamp(sourceOffset.x, min.x, max.x), Clamp(sourceOffset.y, min.y, max.y) ); +} + +} // unnamed namespace + +Dali::Toolkit::MaskedImageView MaskedImageView::New( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ) +{ + // Create the implementation + MaskedImageView* maskedImageView = new MaskedImageView(); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::MaskedImageView handle(*maskedImageView); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + maskedImageView->Initialize( targetWidth, targetHeight, sourceImage, maskImage ); + + return handle; +} + +void MaskedImageView::SetSourceImage( Image sourceImage ) +{ + mSourceImageActor.SetImage( sourceImage ); +} + +Image MaskedImageView::GetSourceImage() +{ + return mSourceImageActor.GetImage(); +} + +void MaskedImageView::SetMaskImage( Image maskImage ) +{ + mMaskImage = maskImage; + mSourceImageActor.GetShaderEffect().SetEffectImage( maskImage ); +} + +Image MaskedImageView::GetMaskImage() +{ + return mMaskImage; +} + +Property::Index MaskedImageView::GetPropertyIndex( Dali::Toolkit::MaskedImageView::CustomProperty customProperty ) const +{ + Property::Index index = Property::INVALID_INDEX; + + switch ( customProperty ) + { + case Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR: + { + index = mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ]; + break; + } + + case Dali::Toolkit::MaskedImageView::SOURCE_SIZE: + { + index = mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ]; + break; + } + + case Dali::Toolkit::MaskedImageView::SOURCE_OFFSET: + { + index = mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ]; + break; + } + + case Dali::Toolkit::MaskedImageView::MASK_SIZE: + { + index = mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ]; + break; + } + + case Dali::Toolkit::MaskedImageView::MASK_OFFSET: + { + index = mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ]; + break; + } + + default: + break; + } + + return index; +} + +void MaskedImageView::Pause() +{ + if ( mRenderTask ) + { + mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE ); + } +} + +void MaskedImageView::Resume() +{ + if ( mRenderTask ) + { + mRenderTask.SetRefreshRate( RenderTask::REFRESH_ALWAYS ); + } +} + +bool MaskedImageView::IsPaused() const +{ + return !mRenderTask; // RenderTask is discarded during Pause() +} + +void MaskedImageView::SetEditMode( Dali::Toolkit::MaskedImageView::EditMode editMode ) +{ + Actor self = Self(); + + mEditMode = editMode; + + if ( Dali::Toolkit::MaskedImageView::EDIT_DISABLED == editMode ) + { + if ( mPanGestureDetector ) + { + mPanGestureDetector.DetachAll(); + mPanGestureDetector.Reset(); + } + + if ( mPinchDetector ) + { + mPinchDetector.DetachAll(); + mPinchDetector.Reset(); + } + } + else + { + if ( !mPanGestureDetector ) + { + mPanGestureDetector = PanGestureDetector::New(); + mPanGestureDetector.Attach( self ); + mPanGestureDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPan); + } + + if ( !mPinchDetector ) + { + mPinchDetector = PinchGestureDetector::New(); + mPinchDetector.Attach( self ); + mPinchDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPinch); + } + + if( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == editMode ) + { + // Re-clamp values to preserve image aspect-ratio etc. + ClampSourceSizeAndOffset(); + } + } +} + +Dali::Toolkit::MaskedImageView::EditMode MaskedImageView::GetEditMode() const +{ + return mEditMode; +} + +void MaskedImageView::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + // Ignore OnPropertySet if MaskedImageView is setting the properties + if( !mSelfPropertySetting ) + { + // Synchronize with user-supplied property values... + if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] == index ) + { + // Note that clamping will take effect when edit-mode is used later + mSourcePosition.mStartPinchSize = propertyValue.Get(); + mSourcePosition.mCurrentPinchSize = propertyValue.Get(); + } + else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] == index ) + { + // Note that clamping will take effect when edit-mode is used later + mSourcePosition.mPanOffset = propertyValue.Get(); + } + else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] == index ) + { + mMaskPosition.mStartPinchSize = propertyValue.Get(); + mMaskPosition.mCurrentPinchSize = propertyValue.Get(); + } + else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] == index ) + { + mMaskPosition.mPanOffset = propertyValue.Get(); + } + // else it's fine to do nothing here + } +} + +void MaskedImageView::OnPan(Actor source, PanGesture gesture) +{ + // Used to flag whether edit mode is setting properties + mSelfPropertySetting = true; + + Actor self = Self(); + + if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode ) + { + mSourcePosition.mPanOffset += gesture.displacement; + mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize ); + + self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset ); + } + else // Edit mask + { + mMaskPosition.mPanOffset += gesture.displacement; + + self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_OFFSET ), mMaskPosition.mPanOffset ); + } + + // Used to flag whether edit mode is setting properties + mSelfPropertySetting = false; +} + +void MaskedImageView::OnPinch(Actor actor, PinchGesture pinch) +{ + // Used to flag whether edit mode is setting properties + mSelfPropertySetting = true; + + Actor self = Self(); + + if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode ) + { + if ( pinch.state == Gesture::Started ) + { + mSourcePosition.mStartPinchSize = mSourcePosition.mCurrentPinchSize; + } + + mSourcePosition.mCurrentPinchSize = mSourcePosition.mStartPinchSize * pinch.scale; + + ClampSourceSizeAndOffset(); + } + else // Edit mask + { + if ( pinch.state == Gesture::Started ) + { + mMaskPosition.mStartPinchSize = mMaskPosition.mCurrentPinchSize; + } + + mMaskPosition.mCurrentPinchSize = mMaskPosition.mStartPinchSize * pinch.scale; + + self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_SIZE ), mMaskPosition.mCurrentPinchSize ); + } + + // Used to flag whether edit mode is setting properties + mSelfPropertySetting = false; +} + +void MaskedImageView::SetSourceAspectRatio( float widthOverHeight ) +{ + Actor self = Self(); + + if ( widthOverHeight > 0.0f ) + { + mWidthOverHeight = widthOverHeight; + + ClampSourceSizeAndOffset(); + } + else + { + mWidthOverHeight = 0.0f; // ignore aspect-ratio + } +} + +float MaskedImageView::GetSourceAspectRatio() const +{ + return mWidthOverHeight; +} + +void MaskedImageView::SetMaximumSourceScale( float scale ) +{ + mMaximumSourceScale = scale; +} + +float MaskedImageView::GetMaximumSourceScale() const +{ + return mMaximumSourceScale; +} + +void MaskedImageView::SetSourceRotation( MaskedImageView::ImageRotation newRotation ) +{ + if( mSourceRotation != newRotation ) + { + bool oldLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == mSourceRotation || Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation ); + bool newLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == newRotation || Dali::Toolkit::MaskedImageView::ROTATE_270 == newRotation ); + + if ( oldLandscape != newLandscape ) + { + // Changing between landscape & portraint, swap width & height + float temp = mSourcePosition.mCurrentPinchSize.width; + mSourcePosition.mCurrentPinchSize.width = mSourcePosition.mCurrentPinchSize.height; + mSourcePosition.mCurrentPinchSize.height = temp; + } + + mSourceRotation = newRotation; + + ApplyMaskedImageShader( newRotation ); + + ClampSourceSizeAndOffset(); + } +} + +MaskedImageView::ImageRotation MaskedImageView::GetSourceRotation() const +{ + return mSourceRotation; +} + +Dali::Toolkit::MaskedImageView::MaskedImageViewSignal& MaskedImageView::MaskFinishedSignal() +{ + return mMaskFinishedSignal; +} + +MaskedImageView::MaskedImageView() +: ControlImpl(true), + mEditMode( Dali::Toolkit::MaskedImageView::EDIT_DISABLED ), + mSelfPropertySetting( false ), + mSourceRotation( Dali::Toolkit::MaskedImageView::ROTATE_0 ), + mWidthOverHeight( 0.0f ), + mMaximumSourceScale( Dali::Toolkit::MaskedImageView::DEFAULT_MAXIMUM_SOURCE_SCALE ) +{ +} + +void MaskedImageView::Initialize( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ) +{ + Actor self = Self(); + + // Register custom properties + + mTargetSize = Vector2( static_cast(targetWidth), static_cast(targetHeight) ); + + mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ] + = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ], Color::BLACK ); + + mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] + = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ], mTargetSize ); + + mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] + = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ], Vector2::ZERO ); + + mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] + = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_SIZE ], mTargetSize ); + + mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] + = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ], Vector2::ZERO ); + + // Create destination image (FBO) + mDestinationImage = FrameBufferImage::New( targetWidth, targetHeight, Pixel::RGBA8888 ); + + // Create source actor for off-screen image processing + mSourceImageActor = ImageActor::New( sourceImage ); + self.Add( mSourceImageActor ); + mSourceImageActor.SetParentOrigin( ParentOrigin::CENTER ); + mSourceImageActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION ); + mSourceImageActor.SetInheritRotation( false ); + mSourceImageActor.SetInheritScale( false ); + mSourceImageActor.SetColorMode( USE_OWN_COLOR ); + mSourceImageActor.SetSize( Vector3::ONE ); + + // Apply masking effect to source actor + mMaskImage = maskImage; + ApplyMaskedImageShader( Dali::Toolkit::MaskedImageView::ROTATE_0 ); + + // Create actor to display result of off-screen rendering + mDestinationImageActor = ImageActor::New( mDestinationImage ); + self.Add( mDestinationImageActor ); + mDestinationImageActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mDestinationImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + // Start the masking operation + mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask(); + mRenderTask.SetSourceActor( mSourceImageActor ); + mRenderTask.SetTargetFrameBuffer( mDestinationImage ); + mRenderTask.SetInputEnabled( false ); + mRenderTask.SetExclusive( true ); + mRenderTask.SetClearEnabled( true ); + mRenderTask.ApplyConstraint( Constraint::New( RenderTask::CLEAR_COLOR, + Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ] ), + EqualToConstraint() ) ); + mRenderTask.FinishedSignal().Connect( this, &MaskedImageView::OnRenderTaskFinished ); + + // Edit mode initialization + mSourcePosition.mCurrentPinchSize = Vector2( targetWidth, targetHeight ); + mMaskPosition.mCurrentPinchSize = mSourcePosition.mCurrentPinchSize; +} + +void MaskedImageView::ApplyMaskedImageShader( ImageRotation rotation ) +{ + Actor self = Self(); + + // Vertex shader has different postfix for each rotation + std::stringstream vertexSource; + vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE; + if( Dali::Toolkit::MaskedImageView::ROTATE_90 == rotation ) + { + vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE90; + } + else if( Dali::Toolkit::MaskedImageView::ROTATE_180 == rotation ) + { + vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE180; + } + else if( Dali::Toolkit::MaskedImageView::ROTATE_270 == rotation ) + { + vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE270; + } + else // Default to Dali::Toolkit::MaskedImageView::ROTATE_0 + { + vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE0; + } + + ShaderEffect shader = ShaderEffect::New( vertexSource.str(), + MASKED_IMAGE_VIEW_FRAGMENT_SOURCE, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) ); + + shader.SetUniform( "uTargetSize", mTargetSize ); + shader.SetUniform( "uSourceSize", mTargetSize ); + shader.ApplyConstraint( Constraint::New( shader.GetPropertyIndex( "uSourceSize" ), + Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] ), + EqualToConstraintVector2 ) ); + shader.SetUniform( "uSourceOffset", Vector2::ZERO ); + shader.ApplyConstraint( Constraint::New( shader.GetPropertyIndex( "uSourceOffset" ), + Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] ), + EqualToConstraintVector2 ) ); + shader.SetUniform( "uMaskSize", mTargetSize ); + shader.ApplyConstraint( Constraint::New( shader.GetPropertyIndex( "uMaskSize" ), + Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] ), + EqualToConstraintVector2 ) ); + shader.SetUniform( "uMaskOffset", mTargetSize ); + shader.ApplyConstraint( Constraint::New( shader.GetPropertyIndex( "uMaskOffset" ), + Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] ), + EqualToConstraintVector2 ) ); + + shader.SetEffectImage( mMaskImage ); + mSourceImageActor.SetShaderEffect( shader ); +} + +void MaskedImageView::ClampSourceSizeAndOffset() +{ + float rotatedAspectRatio( mWidthOverHeight ); + if( mWidthOverHeight > 0.0f && + ( Dali::Toolkit::MaskedImageView::ROTATE_90 == mSourceRotation || + Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation ) ) + { + rotatedAspectRatio = 1.0f / mWidthOverHeight; + } + + Actor self = Self(); + + mSourcePosition.mCurrentPinchSize = ClampSourceSize( mSourcePosition.mCurrentPinchSize, mTargetSize, rotatedAspectRatio, mMaximumSourceScale ); + self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_SIZE ), mSourcePosition.mCurrentPinchSize ); + + mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize ); + self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset ); +} + +MaskedImageView::~MaskedImageView() +{ + // Guard to allow handle destruction after Core has been destroyed + if( Stage::IsInstalled() ) + { + Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask ); + } +} + +void MaskedImageView::OnRenderTaskFinished( Dali::RenderTask& renderTask ) +{ + Toolkit::MaskedImageView handle( GetOwner() ); + mMaskFinishedSignal.Emit( handle ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/image-view/masked-image-view-impl.h b/dali-toolkit/internal/controls/image-view/masked-image-view-impl.h new file mode 100644 index 0000000..ea213f5 --- /dev/null +++ b/dali-toolkit/internal/controls/image-view/masked-image-view-impl.h @@ -0,0 +1,272 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_MASKED_IMAGE_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_MASKED_IMAGE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * @copydoc Dali::Toolkit::MaskedImageView + */ +class MaskedImageView : public ControlImpl +{ +public: + + typedef Dali::Toolkit::MaskedImageView::ImageRotation ImageRotation; + + /** + * Create a new MaskedImageView. + * @return A public handle to the newly allocated MaskedImageView. + */ + static Dali::Toolkit::MaskedImageView New( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetSourceImage() + */ + void SetSourceImage( Image sourceImage ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetSourceImage() + */ + Image GetSourceImage(); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetMaskImage() + */ + void SetMaskImage( Image maskImage ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetMaskImage() + */ + Image GetMaskImage(); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetPropertyIndex() + */ + Property::Index GetPropertyIndex( Dali::Toolkit::MaskedImageView::CustomProperty customProperty ) const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::Pause() + */ + void Pause(); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::Resume() + */ + void Resume(); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::IsPaused() + */ + bool IsPaused() const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetEditMode() + */ + void SetEditMode( Dali::Toolkit::MaskedImageView::EditMode editMode ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetEditMode() + */ + Dali::Toolkit::MaskedImageView::EditMode GetEditMode() const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetSourceAspectRatio() + */ + void SetSourceAspectRatio( float widthOverHeight ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetSourceAspectRatio() + */ + float GetSourceAspectRatio() const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetMaximumSourceScale() + */ + void SetMaximumSourceScale( float scale ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetMaximumSourceScale() + */ + float GetMaximumSourceScale() const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::SetSourceRotation() + */ + void SetSourceRotation( ImageRotation rotation ); + + /** + * @copydoc Dali::Toolkit::MaskedImageView::GetSourceRotation() + */ + ImageRotation GetSourceRotation() const; + + /** + * @copydoc Dali::Toolkit::MaskedImageView::RenderFinishedSignal + */ + Dali::RenderTask::RenderTaskSignalV2& RenderFinishedSignal() + { + return mRenderTask.FinishedSignal(); + } + + /** + * @copydoc Dali::Toolkit::MaskedImageView::MaskFinishedSignal + */ + Dali::Toolkit::MaskedImageView::MaskedImageViewSignal& MaskFinishedSignal(); + +protected: + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + void OnPropertySet( Property::Index index, Property::Value propertyValue ); + + /** + * Helper for edit mode. + */ + void OnPan( Actor source, PanGesture gesture ); + + /** + * Helper for edit mode. + */ + void OnPinch( Actor actor, PinchGesture pinch ); + + /** + * Construct a new MaskedImageView. + */ + MaskedImageView(); + + /** + * 2nd-phase initialization. + */ + void Initialize( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ); + + /** + * Helper to apply the desired shader-effect for a given rotation. + * @param[in] rotation The rotation to apply to the source image. + */ + void ApplyMaskedImageShader( ImageRotation rotation ); + + /** + * Helper to clamp the source image properties (only in edit mode). + */ + void ClampSourceSizeAndOffset(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~MaskedImageView(); + +private: + + // Undefined + MaskedImageView(const MaskedImageView&); + + // Undefined + MaskedImageView& operator=(const MaskedImageView& rhs); + + /** + * Emit MaskFinishedSignal when the render task finished rendering + * @param[in] renderTask the off-screen render task + */ + void OnRenderTaskFinished( Dali::RenderTask& renderTask ); + +private: + + Vector2 mTargetSize; + + Property::Index mCustomProperties[ Dali::Toolkit::MaskedImageView::CUSTOM_PROPERTY_COUNT ]; + + // Used for off-screen rendering + RenderTask mRenderTask; + ImageActor mSourceImageActor; + FrameBufferImage mDestinationImage; + + // Create actor to display result of off-screen rendering + ImageActor mDestinationImageActor; + + // Because ShaderEffect doesn't have a GetEffectImage() + Image mMaskImage; + + // For edit mode + Dali::Toolkit::MaskedImageView::EditMode mEditMode; + PanGestureDetector mPanGestureDetector; + PinchGestureDetector mPinchDetector; + bool mSelfPropertySetting; + + struct ImagePosition + { + Vector2 mPanOffset; + Vector2 mStartPinchSize; + Vector2 mCurrentPinchSize; + }; + ImagePosition mSourcePosition; + ImagePosition mMaskPosition; + + ImageRotation mSourceRotation; + + // Limits for edit mode + float mWidthOverHeight; + float mMaximumSourceScale; + + Dali::Toolkit::MaskedImageView::MaskedImageViewSignal mMaskFinishedSignal; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::MaskedImageView& GetImpl(Toolkit::MaskedImageView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::MaskedImageView& GetImpl(const Toolkit::MaskedImageView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_MASKED_IMAGE_VIEW_H__ diff --git a/dali-toolkit/internal/controls/magnifier/magnifier-impl.cpp b/dali-toolkit/internal/controls/magnifier/magnifier-impl.cpp new file mode 100644 index 0000000..bd44d95 --- /dev/null +++ b/dali-toolkit/internal/controls/magnifier/magnifier-impl.cpp @@ -0,0 +1,386 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +using namespace Dali; + +namespace // unnamed namespace +{ + +const char* DEFAULT_FRAME_IMAGE_PATH = DALI_IMAGE_DIR "magnifier-image-frame.png"; + +const float IMAGE_BORDER_INDENT = 14.0f; ///< Indent of border in pixels. + +/** + * ImageBorderSizeConstraint + */ +struct ImageBorderSizeConstraint +{ + ImageBorderSizeConstraint() + : mSizeOffset(Vector3(IMAGE_BORDER_INDENT - 1, IMAGE_BORDER_INDENT - 1, 0.0f) * 2.0f) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& referenceSizeProperty) + { + const Vector3& referenceSize = referenceSizeProperty.GetVector3(); + + return referenceSize + mSizeOffset; + } + + Vector3 mSizeOffset; ///< The amount to offset the size from referenceSize +}; + +struct CameraActorPositionConstraint +{ + CameraActorPositionConstraint(const Vector2& stageSize, float defaultCameraDistance = 0.0f) + : mStageSize(stageSize), + mDefaultCameraDistance(defaultCameraDistance) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& sourcePositionProperty) + { + const Vector3& sourcePosition = sourcePositionProperty.GetVector3(); + + return Vector3(sourcePosition.x + mStageSize.x * 0.5f, + -sourcePosition.y + mStageSize.y * 0.5f, + sourcePosition.z + mDefaultCameraDistance); + } + + Vector2 mStageSize; + float mDefaultCameraDistance; + +}; + +struct RenderTaskViewportPositionConstraint +{ + RenderTaskViewportPositionConstraint(const Vector2& stageSize) + : mStageSize(stageSize) + { + } + + Vector2 operator()(const Vector2& current, + const PropertyInput& positionProperty, + const PropertyInput& magnifierSizeProperty, + const PropertyInput& magnifierScaleProperty) + { + Vector2 position(positionProperty.GetVector3()); // World position? + + //position -= mStageSize * 0.5f; + + // should be updated when: + // Magnifier's world position/size/scale/parentorigin/anchorpoint changes. + // or Magnifier camera's world position changes. + Vector3 size = magnifierSizeProperty.GetVector3() * magnifierScaleProperty.GetVector3(); + + // Reposition, and resize viewport to reflect the world bounds of this Magnifier actor. + position.x += (mStageSize.width - size.width) * 0.5f; + position.y += (mStageSize.height - size.height) * 0.5f; + + return position; + } + + Vector2 mStageSize; +}; + +struct RenderTaskViewportSizeConstraint +{ + RenderTaskViewportSizeConstraint() + { + } + + Vector2 operator()(const Vector2& current, + const PropertyInput& magnifierSizeProperty, + const PropertyInput& magnifierScaleProperty) + { + return Vector2(magnifierSizeProperty.GetVector3() * magnifierScaleProperty.GetVector3()); + } +}; + +/** + * Returns relative border (0.0f...1.0f x 0.0f...1.0f) + * from an absolute pixel specified border. + * @param[in] absolute A border using absolute pixel coordinates + * @param[in] width The width of the texture + * @param[in] height The height of the texture. + * @return A border relative to the size of the Image texture dimensions. + */ +Vector4 GetRelativeBorder(Vector4 absolute, float width, float height) +{ + return Vector4( absolute.x / width, + absolute.y / height, + absolute.z / width, + absolute.w / height); +} + +} + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Magnifier +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::Magnifier Magnifier::New() +{ + // Create the implementation + MagnifierPtr magnifier(new Magnifier()); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::Magnifier handle(*magnifier); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + magnifier->Initialize(); + + return handle; +} + +Magnifier::Magnifier() +: ControlImpl(true), + mPropertySourcePosition(Property::INVALID_INDEX), + mActorSize(Vector3::ZERO), + mMagnificationFactor(1.0f) +{ +} + +void Magnifier::SetSourceActor(Actor actor) +{ + mTask.SetSourceActor( actor ); +} + +void Magnifier::SetSourcePosition(const Vector3& position) +{ + Self().SetProperty(mPropertySourcePosition, position); +} + +void Magnifier::Initialize() +{ + Actor self = Self(); + mPropertySourcePosition = self.RegisterProperty( Toolkit::Magnifier::SOURCE_POSITION_PROPERTY_NAME, Vector3::ZERO ); + Vector2 stageSize(Stage::GetCurrent().GetSize()); + + Layer dummyLayer = Layer::New(); + Stage().GetCurrent().Add(dummyLayer); + dummyLayer.SetParentOrigin(ParentOrigin::CENTER); + + // NOTE: + // sourceActor is a dummy delegate actor that takes the source property position, + // and generates a WORLD_POSITION, which is 1 frame behind the source property. + // This way the constraints for determining the camera position (source) and those + // for determining viewport position use the same 1 frame old values. + // A simple i) CameraPos = f(B), ii) B = f(A) set of constraints wont suffice as + // although CameraPos will use B, which is A's previous value. The constraint will + // not realise that B is still dirty as far as constraint (i) is concerned. + // Perhaps this is a bug in the way the constraint system factors into what is dirty + // and what is not. + mSourceActor = Actor::New(); + dummyLayer.Add(mSourceActor); + mSourceActor.SetParentOrigin(ParentOrigin::CENTER); + Constraint constraint = Constraint::New( Actor::POSITION, + Source( self, mPropertySourcePosition ), + EqualToConstraint() ); + mSourceActor.ApplyConstraint(constraint); + + // create the render task this will render content on top of everything + // based on camera source position. + InitializeRenderTask(); + + // set up some constraints to: + // i) reposition (dest) frame actor based on magnifier actor's world position (this is 1 frame delayed) + // ii) reposition and resize (dest) the render task's viewport based on magnifier actor's world position (1 frame delayed) & size. + // iii) reposition (source) camera actor based on magnifier source actor's world position (this is 1 frame delayed) + + // Apply constraint to camera's position + // Position our camera at the same distance from its target as the default camera is. + // The camera position doesn't affect how we render, just what we render (due to near and far clip planes) + // NOTE: We can't interrogate the default camera's position as it is not known initially (takes 1 frame + // for value to update). + // But we can determine the initial position using the same formula: + // distance = stage.height * 0.5 / tan(FOV * 0.5) + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + RenderTask renderTask = taskList.GetTask(0u); + float fov = renderTask.GetCameraActor().GetFieldOfView(); + mDefaultCameraDistance = (stageSize.height * 0.5f) / tanf(fov * 0.5f); + + // Use a 1 frame delayed source position to determine the camera actor's position. + // This is necessary as the viewport is determined by the Magnifier's Actor's World position (which is computed + // at the end of the update cycle i.e. after constraints have been applied.) + //Property::Index propertySourcePositionDelayed = mCameraActor.RegisterProperty("delayed-source-position", Vector3::ZERO); + + constraint = Constraint::New( Actor::POSITION, + Source( mSourceActor, Actor::WORLD_POSITION ), + CameraActorPositionConstraint(stageSize, mDefaultCameraDistance) ); + mCameraActor.ApplyConstraint(constraint); + + // Apply constraint to render-task viewport position + constraint = Constraint::New( RenderTask::VIEWPORT_POSITION, + Source( self, Actor::WORLD_POSITION ),//mPropertySourcePosition ), + Source( self, Actor::SIZE ), + Source( self, Actor::WORLD_SCALE), + RenderTaskViewportPositionConstraint(stageSize) ); + mTask.ApplyConstraint(constraint); + + // Apply constraint to render-task viewport position + constraint = Constraint::New( RenderTask::VIEWPORT_SIZE, + Source( self, Actor::SIZE ), + Source( self, Actor::WORLD_SCALE), + RenderTaskViewportSizeConstraint() ); + mTask.ApplyConstraint(constraint); +} + +Magnifier::~Magnifier() +{ + +} + +void Magnifier::InitializeRenderTask() +{ + Stage stage = Stage::GetCurrent(); + + RenderTaskList taskList = stage.GetRenderTaskList(); + + mTask = taskList.CreateTask(); + mTask.SetInputEnabled(false); + mTask.SetClearColor(Vector4(0.5f, 0.5f, 0.5f, 1.0f)); + mTask.SetClearEnabled(true); + + mCameraActor = CameraActor::New(); + mCameraActor.SetType(Camera::FREE_LOOK); + mCameraActor.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); // Look at stage + + stage.Add(mCameraActor); + mTask.SetCameraActor( mCameraActor ); + + SetFrameVisibility(true); +} + +bool Magnifier::GetFrameVisibility() const +{ + return mFrameLayer; +} + +void Magnifier::SetFrameVisibility(bool visible) +{ + if(visible && !mFrameLayer) + { + Actor self(Self()); + + Layer mFrameLayer = Layer::New(); + mFrameLayer.SetParentOrigin( ParentOrigin::CENTER ); + Stage::GetCurrent().Add(mFrameLayer); + + Image image = Image::New( DEFAULT_FRAME_IMAGE_PATH ); + ImageActor frame = ImageActor::New( image ); + frame.SetDrawMode(DrawMode::OVERLAY); + frame.SetStyle( ImageActor::STYLE_NINE_PATCH ); + + frame.SetNinePatchBorder( Vector4::ONE * IMAGE_BORDER_INDENT ); + mFrameLayer.Add(frame); + + // Apply position constraint to the frame + Constraint constraint = Constraint::New( Actor::POSITION, + Source( self, Actor::WORLD_POSITION ), + EqualToConstraint() ); + frame.ApplyConstraint(constraint); + + // Apply scale constraint to the frame + constraint = Constraint::New( Actor::SCALE, + Source( self, Actor::SCALE ), + EqualToConstraint() ); + frame.ApplyConstraint(constraint); + + Source(self, Actor::SCALE), + + // Apply size constraint to the the frame + constraint = Constraint::New(Actor::SIZE, + Source(self, Actor::SIZE), + ImageBorderSizeConstraint()); + frame.ApplyConstraint(constraint); + } + else if(!visible && mFrameLayer) + { + Stage::GetCurrent().Remove(mFrameLayer); + mFrameLayer.Reset(); + } +} + +void Magnifier::OnControlSizeSet(const Vector3& targetSize) +{ + // TODO: Once Camera/CameraActor properties function as proper animatable properties + // this code can disappear. + // whenever the size of the magnifier changes, the field of view needs to change + // to compensate for the new size of the viewport. this cannot be done within + // a constraint yet as Camera/CameraActor properties are not animatable/constrainable. + mActorSize = targetSize; + Update(); +} + +float Magnifier::GetMagnificationFactor() const +{ + return mMagnificationFactor; +} + +void Magnifier::SetMagnificationFactor(float value) +{ + mMagnificationFactor = value; + Update(); +} + +void Magnifier::Update() +{ + // TODO: Make Camera settings (fieldofview/aspectratio) as animatable constraints. + + // should be updated when: + // Magnifier's world size/scale changes. + Actor self(Self()); + Vector3 worldSize = mActorSize * self.GetCurrentWorldScale(); + + // Adjust field of view to scale content + + // size.height / 2 + // |------/ + // |d / + // |i / + // |s / + // |t / + // |./ + // |/ <--- fov/2 radians. + // + const float fov = atanf( 0.5f * worldSize.height / mDefaultCameraDistance / mMagnificationFactor) * 2.0f; + mCameraActor.SetFieldOfView( fov ); + + // Adjust aspect ratio to compensate for rectangular viewports. + mCameraActor.SetAspectRatio( worldSize.width / worldSize.height ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/magnifier/magnifier-impl.h b/dali-toolkit/internal/controls/magnifier/magnifier-impl.h new file mode 100644 index 0000000..d18fa24 --- /dev/null +++ b/dali-toolkit/internal/controls/magnifier/magnifier-impl.h @@ -0,0 +1,162 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_MAGNIFIER_H__ +#define __DALI_TOOLKIT_INTERNAL_MAGNIFIER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class Magnifier; + +typedef IntrusivePtr MagnifierPtr; + +/** + * @copydoc Toolkit::Magnifier + */ +class Magnifier : public ControlImpl +{ +public: + + /** + * Create a new Magnifier. + * @return A public handle to the newly allocated Magnifier. + */ + static Dali::Toolkit::Magnifier New(); + +public: + + /** + * @copydoc Toolkit::ImageView::SetSourceActor + */ + void SetSourceActor(Actor actor); + + /** + * @copydoc Toolkit::ImageView::SetSourcePosition + */ + void SetSourcePosition(const Vector3& position); + + /** + * @copydoc Toolkit::ImageView::GetFrameVisibility + */ + bool GetFrameVisibility() const; + + /** + * @copydoc Toolkit::ImageView::SetFrameVisibility + */ + void SetFrameVisibility(bool visible); + + /** + * @copydoc Toolkit::ImageView::GetMagnificationFactor + */ + float GetMagnificationFactor() const; + + /** + * @copydoc Toolkit::ImageView::SetMagnificationFactor + */ + void SetMagnificationFactor(float value); + + /** + * Update magnification + */ + void Update(); + +protected: + + /** + * Construct a new Magnifier. + */ + Magnifier(); + + /** + * 2nd-phase initialization. + */ + void Initialize(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Magnifier(); + +private: + + /** + * Initializes the render task required to render contents. + */ + void InitializeRenderTask(); + +private: + + virtual void OnControlSizeSet(const Vector3& targetSize); + +private: + + // Undefined + Magnifier(const Magnifier&); + + // Undefined + Magnifier& operator=(const Magnifier& rhs); + +private: + + RenderTask mTask; ///< Render Task to render the source actor contents. + CameraActor mCameraActor; ///< CameraActor attached to RenderTask + Layer mFrameLayer; ///< Frame is placed on separate layer added to stage. + Property::Index mPropertySourcePosition; ///< Source Position ("source-position") + Actor mSourceActor; ///< Source Delegate Actor represents the source position to read. + float mDefaultCameraDistance; ///< Default RenderTask's camera distance from target. + Vector3 mActorSize; ///< The Actor size + float mMagnificationFactor; ///< Magnification factor 1.0f is default. same as content. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Magnifier& GetImpl(Toolkit::Magnifier& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::Magnifier& GetImpl(const Toolkit::Magnifier& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_MAGNIFIER_H__ diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-bar.cpp b/dali-toolkit/internal/controls/navigation-frame/navigation-bar.cpp new file mode 100644 index 0000000..dfedc41 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-bar.cpp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "navigation-bar.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +NavigationBar::NavigationBar(NavigationControl& naviControl, + Toolkit::BasicNaviBarStyle barStylePortrait, + Toolkit::BasicNaviBarStyle barStyleLandscape ) +:mInternalNavigationControl( naviControl ), + mBasicStylePortrait( barStylePortrait ), + mBasicStyleLandscape( barStyleLandscape ), + mBasicCurrentStyle( &mBasicStylePortrait ), + mIsPortrait( true ), + mVisible( true ) +{ + mInternalNavigationControl.ItemPushedSignal().Connect( this, &NavigationBar::OnItemPushed ); + mInternalNavigationControl.ItemPoppedSignal().Connect( this, &NavigationBar::OnItemPopped ); + + mLayout = Toolkit::TableView::New(1,3); + mInternalNavigationControl.GetBarLayer().Add(mLayout); + mLayout.SetSize(mBasicCurrentStyle->referenceWidth, mBasicCurrentStyle->height); + mLayout.SetFixedHeight(0, mBasicCurrentStyle->height); + mLayout.SetDrawMode(DrawMode::OVERLAY); + + SetBackground( mBasicCurrentStyle->background ); +} + +NavigationBar::~NavigationBar() +{ +} + +void NavigationBar::ScaleStyleUpdate( Vector2 naviControlSize, int orientation ) +{ + bool isPortrait( orientation == 0 || orientation == 180 ); + // change in orientation. + if(mIsPortrait != isPortrait) + { + mIsPortrait = isPortrait; + mBasicCurrentStyle = isPortrait ? &mBasicStylePortrait : &mBasicStyleLandscape; + OrientationUpdate( isPortrait ); + mLayout.SetSize(mBasicCurrentStyle->referenceWidth, mBasicCurrentStyle->height); + mLayout.SetFixedHeight(0, mBasicCurrentStyle->height); + if(mBackground) + { + mBackground.SetSize(mBasicCurrentStyle->referenceWidth, mBasicCurrentStyle->height); + } + } + + mRelativeScale = naviControlSize.x / static_cast( mBasicCurrentStyle->referenceWidth); + mLayout.SetScale(mRelativeScale); + mBarHeight = mBasicCurrentStyle->height * mRelativeScale; + if(mBackground) + { + mBackground.SetScale(mRelativeScale); + } +} + +float NavigationBar::GetBarHeight() const +{ + if( mVisible ) + { + return mBarHeight; + } + else + { + return 0.0f; + } +} + +void NavigationBar::SetBackground( Actor background ) +{ + mBackground = background; + mBackground.SetSize(mBasicCurrentStyle->referenceWidth, mBasicCurrentStyle->height); + mInternalNavigationControl.GetBarLayer().Add( mBackground ); + mBackground.SetScale(mRelativeScale); +} + +void NavigationBar::OnItemPushed( Toolkit::NavigationControl naviControl, Toolkit::Page naviItem ) +{ + mCurrentItem = naviItem; + Update( mCurrentItem ); +} + +void NavigationBar::OnItemPopped( Toolkit::NavigationControl naviControl, Toolkit::Page naviItem ) +{ + mCurrentItem = mInternalNavigationControl.GetCurrentItem(); + Update( mCurrentItem ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-bar.h b/dali-toolkit/internal/controls/navigation-frame/navigation-bar.h new file mode 100644 index 0000000..33bb920 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-bar.h @@ -0,0 +1,139 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_NAVIGATION_BAR_H__ +#define __DALI_TOOLKIT_INTERNAL_NAVIGATION_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * Base class for different navigation bars such as tool bar, title bar. + */ +class NavigationBar : public Dali::RefObject, public ConnectionTracker +{ + +public: + /** + * Constructor + * Pass in the navigationControl with which the bar associates and its style. + */ + NavigationBar( NavigationControl& naviControl, + Toolkit::BasicNaviBarStyle barStylePortrait, + Toolkit::BasicNaviBarStyle barStyleLandscape); + + /** + * Update the bar scale when the size of the navigation control is set / reset + * Also Update the style when the orientation( portrait/landscape) is changed + * @param[in] naviControlSize The size of the navigation control + * @param[in] orientation The angle of the current orientation + */ + void ScaleStyleUpdate( Vector2 naviControlSize, int orientation ); + + /** + * Retrieve the height of the bar + * @return The height of the bar + */ + float GetBarHeight() const; + +private: + + /** + * Set a background image and add it onto the NavigaionControl's bar layer. + * + */ + void SetBackground( Actor background ); + + /** + * Call the update function when it receives the page pushed signal. + */ + void OnItemPushed( Toolkit::NavigationControl naviControl, Toolkit::Page naviItem ); + + /** + * Call the update function when it receives the page popped signal. + */ + void OnItemPopped( Toolkit::NavigationControl naviControl, Toolkit::Page naviItem ); + +protected: + + /** + * virtual destructor + */ + virtual ~NavigationBar(); + + /** + * Given the current page, update the bar content. + * @param[in] naviItem the item on the top of the navigation stack + */ + virtual void Update( Toolkit::Page naviItem ) = 0; + + /** + * update the bar style when the orientation is changed + * @param[in] isPortrait Whether the current orientation is portrait mode + */ + virtual void OrientationUpdate( bool isPortrait ) = 0; + +private: + + // Undefined + NavigationBar(const NavigationBar&); + + // Undefined + NavigationBar& operator=(const NavigationBar& rhs); + + +protected: + NavigationControl& mInternalNavigationControl; + Toolkit::BasicNaviBarStyle mBasicStylePortrait; + Toolkit::BasicNaviBarStyle mBasicStyleLandscape; + const Toolkit::BasicNaviBarStyle* mBasicCurrentStyle; + float mRelativeScale; + float mBarHeight; + + Toolkit::TableView mLayout; + Actor mBackground; + + bool mIsPortrait; + Toolkit::Page mCurrentItem; + + bool mVisible; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + + +#endif /* __DALI_TOOLKIT_INTERNAL_NAVIGATION_BAR_H__ */ diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.cpp b/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.cpp new file mode 100644 index 0000000..37ca96a --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.cpp @@ -0,0 +1,454 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "navigation-control-impl.h" + +//INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // to register type +{ +BaseHandle Create() +{ + return Toolkit::NavigationControl::New(); +} + +TypeRegistration mType( typeid(Toolkit::NavigationControl), typeid(Toolkit::Control), Create ); + +TypeAction a1(mType, Toolkit::NavigationControl::ACTION_PUSH, &NavigationControl::DoAction); +TypeAction a2(mType, Toolkit::NavigationControl::ACTION_POP, &NavigationControl::DoAction); +} + +NavigationControl::NavigationControl() +: ControlImpl( true ), + mToolBar(NULL), + mTitleBar(NULL), + mOrientationAngle( 0 ), + mOrientationAnimationDuration( 1.0f ), + mOrientationAnimationAlphaFunc( AlphaFunctions::EaseOut ), + mItemPositionCoefficient( Vector3( 0.0f, 1.0f, 0.0f) ), + mItemPushedSignal( ), + mItemPoppedSignal( ) +{ +} + +NavigationControl::~NavigationControl() +{ + // Clear all the items in the stack, forces their destruction before NavigationControl is destroyed. + mItemStack.clear(); +} + +void NavigationControl::OnInitialize() +{ + //create layers for display background, current item, and bars respectively + mBackgroundLayer = CreateLayer(); + mContentLayer = CreateLayer(); + mBarLayer = CreateLayer(); + mPopupLayer = CreateLayer(); +} + +void NavigationControl::OnControlChildAdd( Actor& child ) +{ + Toolkit::Page page = Toolkit::Page::DownCast(child); + + // If it's a page then store it locally, Off stage. + if(page) + { + mUnpushedItems.push_back(page); + + // Orphan it until needed later during "push". + Self().Remove( child ); + } +} + +Toolkit::NavigationControl NavigationControl::New() +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< NavigationControl > internalNavigationControl = new NavigationControl(); + + // Pass ownership to CustomActor handle + Toolkit::NavigationControl navigationControl( *internalNavigationControl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalNavigationControl->Initialize(); + + return navigationControl; +} + +void NavigationControl::OnStageConnection() +{ + //only works when navigation control is already on stage! + mContentLayer.RaiseAbove( mBackgroundLayer ); + mBarLayer.RaiseAbove( mContentLayer ); + mPopupLayer.RaiseAbove( mBarLayer ); + Self().SetSensitive(true); + SetKeyInputFocus(); +} + +void NavigationControl::PushItem( Toolkit::Page page ) +{ + // check the uninitialized item + // check the duplicated push for the top item + if(!page || page == mCurrentItem) + { + return; + } + + if( mCurrentItem ) + { + mContentLayer.Remove( mCurrentItem ); + } + + //push the new item into the stack and show it + mItemStack.push_back(page); + mCurrentItem = page; + mContentLayer.Add(page); + mCurrentItem.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION); + + //set up the popup menu which would response to the KEY_MENU + SetupPopupMenu(); + + //Emit singal + Toolkit::NavigationControl handle( GetOwner() ); + mItemPushedSignal.Emit(handle, page); +} + +Toolkit::Page NavigationControl::PopItem() +{ + // cannot pop out the bottom-most item + Toolkit::Page poppedItem; + if(mItemStack.size() > 1) + { + // pop out the top item of the stack and show the new item right under the old one. + mContentLayer.Remove(mCurrentItem); + poppedItem = mItemStack.back(); + mItemStack.pop_back(); + mCurrentItem = mItemStack.back(); + mContentLayer.Add(mCurrentItem); + + //set up the popup menu which would response to the KEY_MENU + SetupPopupMenu(); + } + + //Emit signal + Toolkit::NavigationControl handle( GetOwner() ); + mItemPoppedSignal.Emit(handle, poppedItem); + + return poppedItem; +} + +size_t NavigationControl::GetItemCount() const +{ + return mItemStack.size(); +} + +Toolkit::Page NavigationControl::GetItem(std::size_t index) const +{ + DALI_ASSERT_ALWAYS( index < mItemStack.size() ); + return mItemStack[index]; +} + +Toolkit::Page NavigationControl::GetCurrentItem() const +{ + return mCurrentItem; +} + +void NavigationControl::SetBackground( Actor background) +{ + // It removes the old background + if( mBackground ) + { + mBackgroundLayer.Remove( mBackground ); + } + mBackgroundLayer.Add( background ); + mBackground = background; + mBackground.SetSize( mControlSize ); +} + +void NavigationControl::CreateNavigationToolBar(Toolkit::NaviToolBarStyle toolBarStylePortrait, + Toolkit::NaviToolBarStyle toolBarStyleLandscape ) +{ + // Set a navigation tool bar at the bottom of the navigation frame + // the controls on the tool bar will update automatically when item is pushed or popped by responding to the signals + mToolBar = new NavigationToolBar(*this, toolBarStylePortrait, toolBarStyleLandscape); +} + +void NavigationControl::CreateNavigationTitleBar(Toolkit::NaviTitleBarStyle titleBarStylePortrait, + Toolkit::NaviTitleBarStyle titleBarStyleLandscape) +{ + // Set a navigation title bar at the top of the navigation frame + // the tile/subtitle/titl icon/buttons will update automatically when item is pushed or popped by responding to the signals + mTitleBar = new NavigationTitleBar(*this, titleBarStylePortrait, titleBarStyleLandscape); +} + +void NavigationControl::OrientationChanged( int angle ) +{ + if( mOrientationAngle != angle ) + { + Vector2 targetSize = Vector2(GetSizeSet()); + + // checking to see if changing from landscape -> portrait, or portrait -> landscape + if( mOrientationAngle%180 != angle%180 ) + { + targetSize = Vector2( targetSize.height, targetSize.width ); + } + + mOrientationAngle = angle; + + switch(angle) + { + case 0: + { + mItemPositionCoefficient = Vector3(0.0f, 1.0f, 0.0f); + break; + } + case 90: + { + mItemPositionCoefficient = Vector3(1.0f, 0.0f, 0.0f); + break; + } + case 180: + { + mItemPositionCoefficient = Vector3(0.0f, -1.0f, 0.0f); + break; + } + case 270: + { + mItemPositionCoefficient = Vector3(-1.0f, 0.0f, 0.0f); + break; + } + default: + { + DALI_ASSERT_ALWAYS(false); + break; + } + } + + Animation animation = Animation::New( mOrientationAnimationDuration ); + animation.SetDestroyAction( Animation::Bake ); + animation.RotateTo( Self(), Degree( -angle ), Vector3::ZAXIS, mOrientationAnimationAlphaFunc ); + animation.Play(); + + Self().SetSize( targetSize ); + + RelayoutRequest(); + } +} + +void NavigationControl::SetOrientationRotateAnimation( float duration, AlphaFunction alphaFunc) +{ + mOrientationAnimationDuration = duration; + mOrientationAnimationAlphaFunc = alphaFunc; +} + +Layer NavigationControl::GetBarLayer() const +{ + return mBarLayer; +} + +void NavigationControl::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + const Vector2 setSize( size ); + + if( mCurrentItem ) + { + // always set the current item to fully occupy navigationControl space apart from the bars, + // here the bars might be hidden if the current item does not need them + float positionOffset = 0.0f; + float sizeShrink = 0.0f; + if(mTitleBar) + { + positionOffset += mTitleBar->GetBarHeight()*0.5f; + sizeShrink += mTitleBar->GetBarHeight(); + } + if(mToolBar) + { + positionOffset -= mToolBar->GetBarHeight()*0.5f; + sizeShrink += mToolBar->GetBarHeight(); + } + mCurrentItem.SetPosition( mItemPositionCoefficient * positionOffset); + Vector2 itemSize( setSize.x, setSize.y-sizeShrink ); + + Relayout(mCurrentItem, itemSize, container); + } + + container.push_back(ActorSizePair( mBarLayer, setSize )); + container.push_back(ActorSizePair( mPopupLayer, setSize )); +} + +void NavigationControl::OnControlSizeSet( const Vector3& size ) +{ + if( mControlSize == Vector2(size) ) + { + return; + } + mControlSize = Vector2(size); + + mBarLayer.SetSize(mControlSize); + mPopupLayer.SetSize(mControlSize); + + if( mBackground ) + { + mBackground.SetSize( mControlSize ); + } + if( mToolBar ) + { + mToolBar->ScaleStyleUpdate( mControlSize, mOrientationAngle ); + } + if( mTitleBar ) + { + mTitleBar->ScaleStyleUpdate( mControlSize, mOrientationAngle ); + } +} + +bool NavigationControl::OnKeyEvent( const KeyEvent& event ) +{ + bool consumed = false; + + if(event.state == KeyEvent::Down) + { + if(event.keyCode == 96 ) // F12 == for test + //if( event.keyCode == Dali::DALI_KEY_BACK || event.keyCode == Dali::DALI_KEY_ESCAPE ) + { + if( mPopupMenu && mPopupMenu.IsSensitive() ) // State:POPUP_SHOW + { + mPopupMenu.Hide(); + consumed = true; + } + else if(PopItem()) + { + consumed = true; + } + } + + if( mPopupMenu && event.keyCode == 9) + //if( mPopupMenu && ( event.keyCode == Dali::DALI_KEY_MENU || event.keyCode == Dali::DALI_KEY_SEND ) ) + //Todo: replace with dali key enum after the mapping between X key definition and dali key enum is implemented in dali-adapto + //if( mPopupMenu && event.keyPressedName == "XF86Send" ) + { + if( !mPopupMenu.IsSensitive() ) // State: POPUP_HIDE + { + mPopupMenu.Show(); + } + else // State:POPUP_SHOW + { + mPopupMenu.Hide(); + } + consumed = true; + } + } + + return consumed; +} + +Layer NavigationControl::CreateLayer() +{ + Layer layer = Layer::New(); + layer.SetPositionInheritanceMode(USE_PARENT_POSITION); + Self().Add(layer); + return layer; +} + +void NavigationControl::SetupPopupMenu() +{ + if(mPopupMenu) + { + mPopupLayer.Remove( mPopupMenu ); + } + mPopupMenu = mCurrentItem.GetPopupMenu(); + if( mPopupMenu ) + { + mPopupLayer.Add( mPopupMenu ); + mPopupMenu.OutsideTouchedSignal().Connect(this, &NavigationControl::OnPopupTouchedOutside); + } +} + +void NavigationControl::OnPopupTouchedOutside() +{ + if( mPopupMenu ) + { + mPopupMenu.Hide(); + } +} + +Toolkit::NavigationControl::ItemPushedSignalV2& NavigationControl::ItemPushedSignal() +{ + return mItemPushedSignal; +} + +Toolkit::NavigationControl::ItemPoppedSignalV2& NavigationControl::ItemPoppedSignal() +{ + return mItemPoppedSignal; +} + +bool NavigationControl::DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes) +{ + bool ret = false; + + Dali::BaseHandle handle(object); + Toolkit::NavigationControl control = Toolkit::NavigationControl::DownCast(handle); + DALI_ASSERT_ALWAYS(control); + + if (Toolkit::NavigationControl::ACTION_PUSH == actionName) + { + for (PropertyValueConstIter iter = attributes.begin(); iter != attributes.end(); ++iter) + { + const Property::Value& value = *iter; + + DALI_ASSERT_ALWAYS(value.GetType() == Property::STRING); + std::string itemName = value.Get (); + + for (std::list::iterator itemsIter = GetImpl(control).mUnpushedItems.begin(); itemsIter != GetImpl(control).mUnpushedItems.end(); ++itemsIter) + { + Toolkit::Page page = *itemsIter; + if (page.GetName() == itemName) + { + GetImpl(control).PushItem(page); + ret = true; + break; + } + } + } + } + else if(Toolkit::NavigationControl::ACTION_POP == actionName) + { + GetImpl(control).PopItem(); + + ret = true; + } + + return ret; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.h b/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.h new file mode 100644 index 0000000..964de5a --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-control-impl.h @@ -0,0 +1,267 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_NAVIGATION_CONTROL_H__ +#define __DALI_TOOLKIT_INTERNAL_NAVIGATION_CONTROL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class NavigationControl; +class Page; + +namespace Internal +{ + +class NavigationBar; + +/** + * NavigationControl implements a controller than manages the navigation of hierarchical contents. + * @see Dali::Toolkit::NavigationControl for more details. + */ +class NavigationControl : public ControlImpl +{ +public: + + /** + * Create an initialized NavigationControl. + * @return A handle to a newly allocated Dali resource + */ + static Toolkit::NavigationControl New(); + + /** + * @copydoc Dali::Toolkit::NavigationControl::PushItem() + */ + void PushItem( Toolkit::Page page ); + + /** + * @copydoc Dali::Toolkit::NavigationControl::PopItem() + */ + Toolkit::Page PopItem(); + + /** + * @copydoc Dali::Toolkit::NavigationControl::GetItemCount() + */ + size_t GetItemCount() const; + + /** + * @copydoc Dali::Toolkit::NavigationControl::GetItem() + */ + Toolkit::Page GetItem(std::size_t index) const; + + /** + * @copydoc Dali::Toolkit::NavigationControl::GetCurrentItem() + */ + Toolkit::Page GetCurrentItem() const; + + /** + * @copydoc Dali::Toolkit::NavigationControl::SetBackground() + */ + void SetBackground( Actor background); + + /** + * @copydoc Dali::Toolkit::NavigationControl::CreateNavigationToolBar() + */ + void CreateNavigationToolBar( Toolkit::NaviToolBarStyle toolBarStylePortrait, + Toolkit::NaviToolBarStyle toolBarStyleLandscape ); + + /** + * @copydoc Dali::Toolkit::NavigationControl::CreateNavigationTitleBar() + */ + void CreateNavigationTitleBar( Toolkit::NaviTitleBarStyle titleBarStylePortrait, + Toolkit::NaviTitleBarStyle titleBarStyleLandscape ); + + /** + * @copydoc Dali::Toolkit::NavigationControl::OrientationChanged() + */ + void OrientationChanged( int angle ); + + /** + * @copydoc Dali::Toolkit::NavigationControl::SetOrientationRotateAnimation() + */ + void SetOrientationRotateAnimation( float duration, AlphaFunction alphaFunc); + + /** + * Retrieve the layer for displaying navigation bar + * @return The layer for navigation bar + */ + Layer GetBarLayer() const; + + /** + * Performs actions as requested using the action name. + * @param[in] object The object on which to perform the action. + * @param[in] actionName The action to perform. + * @param[in] attributes The attributes with which to perfrom this action. + * @return true if action has been accepted by this control + */ + static bool DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes); + +public: + + /** + * @copydoc Dali::Toolkit::NavigatinControl::ItemPushedSignal() + */ + Toolkit::NavigationControl::ItemPushedSignalV2& ItemPushedSignal(); + + /** + * @copydoc Dali::Toolkit::NavigatinControl::ItemPoppedSignal() + */ + Toolkit::NavigationControl::ItemPoppedSignalV2& ItemPoppedSignal(); + +private: // override functions from ControlImpl + + /** + * @copydoc Toolkit::ControlImpl::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * From ControlImpl; called after a child has been added to the owning actor. + * @param[in] child The child which has been added. + */ + virtual void OnControlChildAdd( Actor& child ); + + /** + * @copydoc Toolkit::ControlImple::OnStageConnection() + */ + virtual void OnStageConnection(); + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut() + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + + /** + * @copydoc Toolkit::ControlImpl::OnControlSizeSet + */ + virtual void OnControlSizeSet( const Vector3& size ); + + /** + * @copydoc Toolkit::ControlImpl::OnKeyEvent() + */ + virtual bool OnKeyEvent( const KeyEvent& event ); + +protected: + + /** + * Constructor. + * It initializes the NavigationControl members + */ + NavigationControl(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~NavigationControl(); + +private: + + // Undefined + NavigationControl(const NavigationControl&); + + // Undefined + NavigationControl& operator=(const NavigationControl& rhs); + + /** + * Create a Layer and add it to the navigation control + * @return The newly created layer + */ + Layer CreateLayer(); + + /** + * Setup the pop up menu which would show when KEY_MENU is pressed + * This function is called when pushing/popping item + */ + void SetupPopupMenu(); + + /** + * Signal handler called when the user touches outside of pop up menu. + */ + void OnPopupTouchedOutside(); + +public: + std::list< Toolkit::Page > mUnpushedItems; + +private: + + std::vector< Toolkit::Page > mItemStack; + Toolkit::Page mCurrentItem; + Vector2 mControlSize; + + Layer mBackgroundLayer; + Layer mBarLayer; + Layer mContentLayer; + Layer mPopupLayer; + + Actor mBackground; + + NavigationBar* mToolBar; + NavigationBar* mTitleBar; + + int mOrientationAngle; + float mOrientationAnimationDuration; + AlphaFunction mOrientationAnimationAlphaFunc; + Vector3 mItemPositionCoefficient; + + Toolkit::Popup mPopupMenu; + +private: + Toolkit::NavigationControl::ItemPushedSignalV2 mItemPushedSignal; ///< The signal to notify the item push + Toolkit::NavigationControl::ItemPoppedSignalV2 mItemPoppedSignal; ///< The signal to notify the item pop +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::NavigationControl& GetImpl( Toolkit::NavigationControl& navigationControl ) +{ + DALI_ASSERT_ALWAYS( navigationControl ); + + Dali::RefObject& handle = navigationControl.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::NavigationControl& GetImpl( const Toolkit::NavigationControl& navigationControl ) +{ + DALI_ASSERT_ALWAYS( navigationControl ); + + const Dali::RefObject& handle = navigationControl.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_NAVIGATION_CONTROL_H__ */ diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.cpp b/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.cpp new file mode 100644 index 0000000..8ab877a --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.cpp @@ -0,0 +1,173 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "navigation-title-bar.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +//ToDo: use const variables instead of magic numbers + +NavigationTitleBar::NavigationTitleBar(NavigationControl& naviControl, + Toolkit::NaviTitleBarStyle titleBarStylePortrait, + Toolkit::NaviTitleBarStyle titleBarStyleLandscape ) +: NavigationBar(naviControl, titleBarStylePortrait, titleBarStyleLandscape ), + mStylePortrait( titleBarStylePortrait ), + mStyleLandscape( titleBarStyleLandscape ), + mCurrentStyle( &mStylePortrait ) +{ + // title bar is located at the top of the frame + mLayout.SetParentOrigin( Dali::ParentOrigin::TOP_CENTER ); + mLayout.SetAnchorPoint( Dali::AnchorPoint::TOP_CENTER ); + if(mBackground) + { + mBackground.SetParentOrigin( Dali::ParentOrigin::TOP_CENTER ); + mBackground.SetAnchorPoint( Dali::AnchorPoint::TOP_CENTER ); + } + + // button layout: three rows, controls will be put in the middle row, the top and bottom rows are just for margins + mButtonLayout = Toolkit::TableView::New(3, 1); + // title layout: fours rows, the top and bottom rows are just for margins + // if subtitle exists, title in the second row and subtitle in the third row + // if no subtitle, title will occupy both second and third row + mTitleLayout= Toolkit::TableView::New( 4,1 ); + // title icon layout: the top row, the bottom row and the left column are all for margins + mTitleIconLayout= Toolkit::TableView::New( 3,2 ); + SetFixedSizes(); + + mTitle = Toolkit::TextView::New(); + mTitle.SetTextAlignment( Toolkit::Alignment::HorizontalLeft ); + mTitle.SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit); + mSubTitle = Toolkit::TextView::New(); + mSubTitle.SetTextAlignment( Toolkit::Alignment::HorizontalLeft ); + mSubTitle.SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit); +} + +void NavigationTitleBar::Update( Toolkit::Page page ) +{ + const std::vector& controls = page.GetControlsOnTitleBar(); + + // if there is no control to place on the bar ano no title is set, hide the bar + if(controls.empty() && page.GetTitle().empty()) + { + mVisible = false; + mLayout.SetVisible(false); + mBackground.SetVisible(false); + return; + } + + if(mLayout.GetColumns() == 4)// | leftMargin | titleLayout(may have icon and subtitle) | buttonLayout | rightMargin | + { + //remove buttonLayout + mLayout.DeleteColumn(2); + } + // remove titleLayout + mLayout.RemoveChildAt( Toolkit::TableView::CellPosition(0,1) ); + //Remove the controls in the buttonLayout + mButtonLayout.Resize(3,1); + //remove titleIcon + if(mTitleLayout.GetColumns() == 2) + { + mTitleLayout.DeleteColumn( 0 ); + } + // remove title and subtitle + mTitleLayout.RemoveChildAt( Toolkit::TableView::CellPosition(2,0) ); + mTitleLayout.RemoveChildAt( Toolkit::TableView::CellPosition(1,0) ); + + // add controls at the right part of the bar(if exist) + if(!controls.empty()) + { + int numControls = controls.size(); + + for( int index = 0; index < numControls; index++) + { + mButtonLayout.AddChild( controls[index], Toolkit::TableView::CellPosition(1, 2*index + 1) ); + mButtonLayout.SetFixedWidth (2*index, mCurrentStyle->gapBetweenButtons); + mButtonLayout.SetFixedWidth (2*index+1, mCurrentStyle->buttonSize); + } + + mLayout.InsertColumn(2); + mLayout.SetFixedWidth(2, numControls * ( mCurrentStyle->buttonSize + mCurrentStyle->gapBetweenButtons) ); + mLayout.AddChild(mButtonLayout, Toolkit::TableView::CellPosition(0,2)); + } + + // add title and subtitle(if exist) + mTitle.SetText( page.GetTitle() ); + mTitle.SetStyleToCurrentText(mCurrentStyle->titleTextStyle); + if( page.GetSubTitle().empty() ) //display title + { + mTitleLayout.SetFixedHeight( 1,mCurrentStyle->titleHeightWithoutSubtitle - mCurrentStyle->subtitleHeight ); + mTitleLayout.AddChild( mTitle, Toolkit::TableView::CellPosition(1,0,2,1) ); + } + else //display title and subtitle + { + mTitleLayout.SetFixedHeight( 1, mCurrentStyle->titleHeightWithSubtitle ); + mTitleLayout.AddChild( mTitle, Toolkit::TableView::CellPosition(1,0) ); + mSubTitle.SetText( page.GetSubTitle() ); + mSubTitle.SetStyleToCurrentText(mCurrentStyle->subtitleTextStyle); + mTitleLayout.AddChild( mSubTitle, Toolkit::TableView::CellPosition(2,0) ); + } + + // insert title icon to the left of the title(if exist) + if( page.GetTitleIcon() ) + { + mTitleIconLayout.RemoveChildAt(Toolkit::TableView::CellPosition(1,0) ); + mTitleIconLayout.AddChild( page.GetTitleIcon(), Toolkit::TableView::CellPosition(1,0) ); + mTitleLayout.InsertColumn( 0 ); + mTitleLayout.SetFixedWidth( 0, mCurrentStyle->titleLeftMargin + mCurrentStyle->titleIconSize); + mTitleLayout.AddChild( mTitleIconLayout, Toolkit::TableView::CellPosition(1,0,2,1) ); + } + + mLayout.AddChild( mTitleLayout, Toolkit::TableView::CellPosition(0,1) ); + + mVisible = true; + mLayout.SetVisible(true); + mBackground.SetVisible(true); +} + +void NavigationTitleBar::OrientationUpdate( bool isPortrait ) +{ + mCurrentStyle = isPortrait ? &mStylePortrait : &mStyleLandscape; + SetFixedSizes(); + Update( mCurrentItem ); +} + +void NavigationTitleBar::SetFixedSizes() +{ + mLayout.SetFixedWidth(0, mCurrentStyle->titleLeftMargin); + mLayout.SetFixedWidth(2, mCurrentStyle->buttonRightMargin); + + mButtonLayout.SetFixedHeight(2, mCurrentStyle->buttonBottomMargin); + mButtonLayout.SetFixedHeight(1, mCurrentStyle->buttonSize); + + mTitleLayout.SetFixedHeight( 3,mCurrentStyle->titleBottomMargin ); + mTitleLayout.SetFixedHeight( 2,mCurrentStyle->subtitleHeight ); + + mTitleIconLayout.SetFixedWidth( 0, mCurrentStyle->titleIconSize ); + mTitleIconLayout.SetFixedHeight( 1, mCurrentStyle->titleIconSize ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.h b/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.h new file mode 100644 index 0000000..35cdfd3 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-title-bar.h @@ -0,0 +1,95 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_NAVIGATION_TITLE_BAR_H__ +#define __DALI_TOOLKIT_INTERNAL_NAVIGATION_TITLE_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * The title bar locates in the top of the frame where title, subtitle, title icon and other controls could be placed. + * The title bar contains two groups: the left group includes title icon, title and subtitle (subtitle and title icon are not must); + * while the right group can have multiple controls placed on, and it is also fine to have no control on it. + * If the current NavigationOtem provides no control for the bar at all and also has not set a title, the bar is hidden. + * +----------------------------------------+ + * | +-+ Title +-+ +-+ | + * | +-+ Subtitle +-+ +-+ | + * +----------------------------------------+ + */ +class NavigationTitleBar : public NavigationBar +{ +public: + + NavigationTitleBar(NavigationControl& naviControl, + Toolkit::NaviTitleBarStyle titleBarStylePortrait, + Toolkit::NaviTitleBarStyle titleBarStyleLandscape ); + +protected: + + /** + * @copydoc Toolkit::Internal::NavigationBar::Update + */ + void Update( Toolkit::Page page ); + + /** + * @copydoc Toolkit::Internal::NavigationBar::OrientationUpdate + */ + void OrientationUpdate( bool isPortrait ); + +private: + /** + * Set the fixed width and height to the cells of the layout + * These sizes need to be updated when the orientation is changing. + */ + void SetFixedSizes(); + +private: + + Toolkit::NaviTitleBarStyle mStylePortrait; + Toolkit::NaviTitleBarStyle mStyleLandscape; + const Toolkit::NaviTitleBarStyle* mCurrentStyle; + + Toolkit::TableView mButtonLayout; + Toolkit::TableView mTitleLayout; + Toolkit::TableView mTitleIconLayout; + + Toolkit::TextView mTitle; + Toolkit::TextView mSubTitle; + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + + +#endif /* __DALI_TOOLKIT_INTERNAL_NAVIGATION_TITLE_BAR_H__ */ diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.cpp b/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.cpp new file mode 100644 index 0000000..d982e8d --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.cpp @@ -0,0 +1,181 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "navigation-tool-bar.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +//ToDo: use const variables instead of magic numbers + +NavigationToolBar::NavigationToolBar( NavigationControl& naviControl, + Toolkit::NaviToolBarStyle toolBarStylePortrait, + Toolkit::NaviToolBarStyle toolBarStyleLandscape ) +: NavigationBar(naviControl, toolBarStylePortrait, toolBarStyleLandscape), + mStylePortrait( toolBarStylePortrait ), + mStyleLandscape( toolBarStyleLandscape ), + mCurrentStyle( &mStylePortrait), + mNumCentralActor( 0 ) +{ + // tool bar is located at the bottom of the frame + mLayout.SetParentOrigin( Dali::ParentOrigin::BOTTOM_CENTER ); + mLayout.SetAnchorPoint( Dali::AnchorPoint::BOTTOM_CENTER ); + if(mBackground) + { + mBackground.SetParentOrigin( Dali::ParentOrigin::BOTTOM_CENTER ); + mBackground.SetAnchorPoint( Dali::AnchorPoint::BOTTOM_CENTER ); + } + + // layout of the left part, which contains only one control at CellPosition(1,1) + mLeftLayout = Toolkit::TableView::New(3,3); + mLayout.AddChild(mLeftLayout, Toolkit::TableView::CellPosition(0,0)); + + // layout of the right part, which contains only one control at CellPosition(1,1) + mRightLayout = Toolkit::TableView::New(3,3); + mLayout.AddChild(mRightLayout, Toolkit::TableView::CellPosition(0,2)); + + // layout of the central part, which contains multiples control, will add cells dynamically + mCentralLayout = Toolkit::TableView::New(3,2); + mLayout.AddChild(mCentralLayout, Toolkit::TableView::CellPosition(0,1)); + + SetFixedSizes(); +} + +void NavigationToolBar::AddControl(Actor actor, Toolkit::Alignment::Type alignment) +{ + switch( alignment ) + { + case Toolkit::Alignment::HorizontalLeft: // can only have one control on the left side of the bar + { + mLeftLayout.RemoveChildAt(Toolkit::TableView::CellPosition(1,1)); + mLeftLayout.AddChild(actor, Toolkit::TableView::CellPosition(1,1)); + break; + } + case Toolkit::Alignment::HorizontalRight: // can only have one control on the right side of the bar + { + mRightLayout.RemoveChildAt(Toolkit::TableView::CellPosition(1,1)); + mRightLayout.AddChild(actor, Toolkit::TableView::CellPosition(1,1)); + break; + } + case Toolkit::Alignment::HorizontalCenter: // can only have multiple controls on the central part of the bar + { + // already have button in central part + if( mCentralLayout.GetChildAt(Toolkit::TableView::CellPosition(1,1))) + { + unsigned int columnIndex = mCentralLayout.GetColumns() ; + mCentralLayout.InsertColumn( columnIndex-1 ); + mCentralLayout.SetFixedWidth( columnIndex-1, mCurrentStyle->centralButtonGap ); + + mCentralLayout.InsertColumn( columnIndex ); + mCentralLayout.AddChild(actor, Toolkit::TableView::CellPosition( 1, columnIndex ) ); + } + else // have no button in central part + { + mCentralLayout.InsertColumn( 1 ); + mCentralLayout.AddChild(actor, Toolkit::TableView::CellPosition(1,1)); + } + mNumCentralActor++; + break; + } + default: + DALI_ASSERT_ALWAYS( false ); + } + +} + +void NavigationToolBar::Update( Toolkit::Page page ) +{ + const Toolkit::Page::ControlOnBarContainer& controls = page.GetControlsOnToolBar(); + + // if there is no control to place on the bar, hide the bar + if( controls.empty() ) + { + mVisible = false; + mLayout.SetVisible(false); + mBackground.SetVisible(false); + return; + } + + //clear central controls + unsigned int numColumns = mCentralLayout.GetColumns() ; + unsigned int idx = numColumns-2; + while(idx > 0) + { + mCentralLayout.DeleteColumn(idx); + idx--; + } + mNumCentralActor = 0; + mLeftLayout.RemoveChildAt(Toolkit::TableView::CellPosition(1,1)); + mRightLayout.RemoveChildAt(Toolkit::TableView::CellPosition(1,1)); + + Toolkit::Page::ControlOnBarContainer::const_iterator iter; + + for( iter = controls.begin(); iter != controls.end(); iter++ ) + { + AddControl( (*iter)->control, (*iter)->alignment ); + } + + float buttonWidth = static_cast( mCurrentStyle->centralMinimum ); + int length = mNumCentralActor * (mCurrentStyle->centralMinimum + mCurrentStyle->centralButtonGap) - mCurrentStyle->centralButtonGap; + if( length > mCurrentStyle->centralMaximum ) // shrink the width to make sure all the controls can be fit in + { + buttonWidth = static_cast( mCurrentStyle->centralMaximum - (mNumCentralActor - 1) * mCurrentStyle->centralButtonGap ) + / static_cast( mNumCentralActor ); + } + numColumns = mCentralLayout.GetColumns(); + idx = 1; + while(idx < numColumns-1 ) + { + mCentralLayout.SetFixedWidth( idx, buttonWidth ); + idx += 2; + } + mVisible = true; + mLayout.SetVisible(true); + mBackground.SetVisible(true); +} + +void NavigationToolBar::OrientationUpdate( bool isPortrait ) +{ + mCurrentStyle = isPortrait ? &mStylePortrait : &mStyleLandscape; + SetFixedSizes(); + Update( mCurrentItem ); +} + +void NavigationToolBar::SetFixedSizes() +{ + mLayout.SetFixedWidth(1, mCurrentStyle->centralMaximum); + + mLeftLayout.SetFixedWidth(0,mCurrentStyle->hotizontalMargin); + mLeftLayout.SetFixedWidth(1,mCurrentStyle->sideButtonSize ); + mLeftLayout.SetFixedHeight(1,mCurrentStyle->sideButtonSize ); + + mRightLayout.SetFixedWidth(2,mCurrentStyle->hotizontalMargin); + mRightLayout.SetFixedWidth(1,mCurrentStyle->sideButtonSize ); + mRightLayout.SetFixedHeight(1,mCurrentStyle->sideButtonSize ); + + mCentralLayout.SetFixedHeight(1,mCurrentStyle->centralButtonHeight ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.h b/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.h new file mode 100644 index 0000000..6708cc8 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/navigation-tool-bar.h @@ -0,0 +1,101 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_NAVIGATION_TOOL_BAR_H__ +#define __DALI_TOOLKIT_INTERNAL_NAVIGATION_TOOL_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * The tool bar locates in the bottom of the frame where other controls (such as PushButton ) could be placed. + * Controls could be added into three groups: HorizontalLeft, HorizontalCenter or HorizontalRight. + * The left and right groups can only have one control maximum each, while the central group can have multiple controls. + * It is also fine to have no control in each group. + * If the current NavigationOtem provides no control for the bar at all, the bar is hidden. + * +----------------------------------------+ + * | +-+ +-----+ +-----+ +-+ | + * | +-+ +-----+ +-----+ +-+ | + * +----------------------------------------+ + */ +class NavigationToolBar : public NavigationBar +{ +public: + + NavigationToolBar(NavigationControl& naviControl, + Toolkit::NaviToolBarStyle toolBarStylePortrait, + Toolkit::NaviToolBarStyle toolBarStyleLandscape ); + +protected: + + /** + * @copydoc Toolkit::Internal::NavigationBar::Update + */ + void Update( Toolkit::Page page ); + + /** + * @copydoc Toolkit::Internal::NavigationBar::OrientationUpdate + */ + void OrientationUpdate( bool isPortrait ); + +private: + + /** + * Set a control onto the navigation tool bar when the page is on the top. + * @param[in] control The control on the navigation tool bar. + * @param[in] alignment The position of the control, can be HorizontalLeft/HorizontalRight/HorizontalCenter. + */ + void AddControl(Actor actor, Toolkit::Alignment::Type alignment); + + /** + * Set the fixed width and height to the cells of the layout + * These sizes need to be updated when the orientation is changing. + */ + void SetFixedSizes(); + +private: + + Toolkit::NaviToolBarStyle mStylePortrait; + Toolkit::NaviToolBarStyle mStyleLandscape; + const Toolkit::NaviToolBarStyle* mCurrentStyle; + + Toolkit::TableView mLeftLayout; + Toolkit::TableView mRightLayout; + Toolkit::TableView mCentralLayout; + + int mNumCentralActor; +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_NAVIGATION_TOOL_BAR_H__ */ diff --git a/dali-toolkit/internal/controls/navigation-frame/page-impl.cpp b/dali-toolkit/internal/controls/navigation-frame/page-impl.cpp new file mode 100644 index 0000000..0b15d12 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/page-impl.cpp @@ -0,0 +1,187 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "page-impl.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +BaseHandle Create() +{ + return Toolkit::Page::New(); +} + +TypeRegistration mType( typeid(Toolkit::Page), typeid(CustomActor), Create ); + +} // unnamed namespace + +Page::Page() +: ControlImpl(false), + mTitle(""), + mSubTitle("") +{ +} + +Page::~Page() +{ + Toolkit::Page::ControlOnBarContainer::const_iterator iter; + + if( !mToolBarControls.empty() ) + { + for( iter = mToolBarControls.begin(); iter != mToolBarControls.end(); iter++ ) + { + delete( *iter ); + } + } + +} + +Toolkit::Page Page::New() +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< Page > internalpage = new Page(); + + // Pass ownership to CustomActor handle + Toolkit::Page page( *internalpage ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalpage->Initialize(); + + return page; + +} + +void Page::SetTitle(const std::string& title) +{ + mTitle = title; +} + +const std::string& Page::GetTitle() const +{ + return mTitle; +} + +void Page::SetSubTitle(const std::string& subtitle) +{ + mSubTitle = subtitle; +} + +const std::string& Page::GetSubTitle() const +{ + return mSubTitle; +} + +void Page::SetTitleIcon( Actor titleIcon) +{ + mTitleIcon = titleIcon; +} + +Actor Page::GetTitleIcon() const +{ + return mTitleIcon; +} + +bool Page::AddControlToToolBar(Actor control, Toolkit::Alignment::Type alignment) +{ + if( !control || ( alignment!=Toolkit::Alignment::HorizontalLeft + && alignment!=Toolkit::Alignment::HorizontalCenter + && alignment!=Toolkit::Alignment::HorizontalRight ) ) + { + return false; + } + else + { + mToolBarControls.push_back(new Toolkit::Page::ControlOnBar(control, alignment)); + return true; + } +} + +const Toolkit::Page::ControlOnBarContainer& Page::GetControlsOnToolBar() const +{ + return mToolBarControls; +} + +bool Page::AddControlToTitleBar(Actor control) +{ + if( !control ) + { + return false; + } + else + { + mTitleBarControls.push_back(control); + return true; + } +} + +const ActorContainer& Page::GetControlsOnTitleBar() const +{ + return mTitleBarControls; +} + +void Page::SetPopupMenu( Toolkit::Popup popupMenu ) +{ + mPopupMenu = popupMenu; +} + +Toolkit::Popup Page::GetPopupMenu() const +{ + return mPopupMenu; +} + +void Page::OnInitialize() +{ + Actor self = Self(); + + mPropertyTitle = self.RegisterProperty( Dali::Toolkit::Page::PROPERTY_TITLE, "", Property::READ_WRITE ); + mPropertySubTitle = self.RegisterProperty( Dali::Toolkit::Page::PROPERTY_SUB_TITLE, "", Property::READ_WRITE ); +} + +void Page::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + if( index == mPropertyTitle ) + { + std::string title( propertyValue.Get() ); + SetTitle(title); + } + else if( index == mPropertySubTitle ) + { + std::string subTitle( propertyValue.Get() ); + SetSubTitle(subTitle); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/navigation-frame/page-impl.h b/dali-toolkit/internal/controls/navigation-frame/page-impl.h new file mode 100644 index 0000000..2db0ad6 --- /dev/null +++ b/dali-toolkit/internal/controls/navigation-frame/page-impl.h @@ -0,0 +1,179 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PAGE_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_PAGE_IMPL_H__ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Page; + +namespace Internal +{ + +/** + * Page object is a custom control which can be pushed into the stack of navigation control. + * @see Dali::Toolkit::Page for more details + */ + +class Page : public ControlImpl +{ + +public: + + /** + * destructor + */ + virtual ~Page(); + + /** + * Create an initialized Page. + * @return A handle to a newly allocated Dali resource + */ + static Toolkit::Page New(); + + /** + * @copydoc Dali::Toolkit::Page::SetTitle + */ + void SetTitle(const std::string& title); + + /** + * @copydoc Dali::Toolkit::Page::GetTitle + */ + const std::string& GetTitle() const; + + /** + * @copydoc Dali::Toolkit::Page::SetSubTitle + */ + void SetSubTitle(const std::string& subtitle); + + /** + * @copydoc Dali::Toolkit::Page::GetSubTitle + */ + const std::string& GetSubTitle() const; + + /** + * @copydoc Dali::Toolkit::Page::SetTitleIcon + */ + void SetTitleIcon( Actor titleIcon); + + /** + * @copydoc Dali::Toolkit::Page::GetTitleIcon + */ + Actor GetTitleIcon() const; + + /** + * @copydoc Dali::Toolkit::Page::AddControlToToolBar + */ + bool AddControlToToolBar(Actor control, Toolkit::Alignment::Type alignment); + + /** + * @copydoc Dali::Toolkit::Page::GetControlsOnToolBar + */ + const Toolkit::Page::ControlOnBarContainer& GetControlsOnToolBar() const; + + /** + * @copydoc Dali::Toolkit::Page::AddControlToTitleBar + */ + bool AddControlToTitleBar(Actor control); + + /** + * @copydoc Dali::Toolkit::Page::GetControlsOnTitleBar + */ + const ActorContainer& GetControlsOnTitleBar() const; + + /** + * @copydoc Dali::Toolkit::Page::SetPopupMenu + */ + void SetPopupMenu( Toolkit::Popup popupMenu ); + + /** + * @copydoc Dali::Toolkit::Page::GetPopupMenu + */ + Toolkit::Popup GetPopupMenu() const; + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + virtual void OnPropertySet( Property::Index index, Property::Value propertyValue ); + +private: + + /** + * Constructor. + * It initializes the Page members + */ + Page(); + +private: + std::string mTitle; + std::string mSubTitle; + Actor mTitleIcon; + Toolkit::Popup mPopupMenu; + + ActorContainer mTitleBarControls; + Toolkit::Page::ControlOnBarContainer mToolBarControls; + + Property::Index mPropertyTitle; ///< Property index for title. + Property::Index mPropertySubTitle; ///< Property index for sub title. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Page& GetImpl( Toolkit::Page& page ) +{ + DALI_ASSERT_ALWAYS( page ); + + Dali::RefObject& handle = page.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::Page& GetImpl( const Toolkit::Page& page ) +{ + DALI_ASSERT_ALWAYS( page ); + + const Dali::RefObject& handle = page.GetImplementation(); + + return static_cast( handle ); +} + +}// namespace Toolkit + +} // namespace Dali + + +#endif /* __DALI_TOOLKIT_INTERNAL_PAGE_IMPL_H__*/ diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.cpp b/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.cpp new file mode 100644 index 0000000..a831641 --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.cpp @@ -0,0 +1,141 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +using namespace Dali; +TypeRegistration mType( typeid(Toolkit::PageTurnLandscapeView), typeid(Toolkit::PageTurnView), NULL ); +} + +PageTurnLandscapeView::PageTurnLandscapeView( PageFactory& pageFactory, const Vector2& pageSize ) +: PageTurnView( pageFactory, pageSize ) +{ +} + +PageTurnLandscapeView::~PageTurnLandscapeView() +{} + +Toolkit::PageTurnLandscapeView PageTurnLandscapeView::New( PageFactory& pageFactory, const Vector2& pageSize ) +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< PageTurnLandscapeView > internalPageTurnView = new PageTurnLandscapeView( pageFactory, pageSize ); + + // Pass ownership to CustomActor + Dali::Toolkit::PageTurnLandscapeView pageTurnView( *internalPageTurnView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalPageTurnView->Initialize(); + + return pageTurnView; +} + +void PageTurnLandscapeView::OnPageTurnViewInitialize() +{ + mControlSize = Vector2( mPageSize.width * 2.f, mPageSize.height ); + Self().SetSize( mControlSize ); + mTurningPageLayer.SetParentOrigin( ParentOrigin::CENTER ); +} + +ImageActor PageTurnLandscapeView::NewPageFromRenderBuffer( int pageIndex ) +{ + int index = pageIndex % NUMBER_OF_CACHED_PAGES; + ImageActor page = ImageActor::New( mRenderedPage[ index ], + ImageActor::PixelArea( mPageSize.width, 0, mPageSize.width, mPageSize.height ) ); + if( pageIndex <= mTotalPageCount-1) + { + int nextIndex = (pageIndex+1) % NUMBER_OF_CACHED_PAGES; + page.Add( ImageActor::New( mRenderedPage[ nextIndex ],ImageActor::PixelArea( 0, 0, mPageSize.width, mPageSize.height ) ) ); + } + return page; +} + +void PageTurnLandscapeView::OnAddPage( ImageActor newPage, bool isLeftSide ) +{ + newPage.SetParentOrigin( ParentOrigin::CENTER ); + newPage.SetCullFace( CullBack ); + + if( 0 < newPage.GetChildCount() ) + { + ImageActor backImage = ImageActor::DownCast( newPage.GetChildAt( 0 ) ); + backImage.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION ); + backImage.SetSize( mPageSize ); + backImage.SetCullFace( CullFront ); + backImage.SetZ( 0.25f * STATIC_PAGE_INTERVAL_DISTANCE ); + } + if( isLeftSide ) + { + SetShaderEffect( newPage, mSpineEffectBack ); + } +} + +Vector2 PageTurnLandscapeView::SetPanPosition( const Vector2& gesturePosition ) +{ + if( mIsTurnBack[mPanActor] ) + { + return Vector2( mPageSize.width - gesturePosition.x, gesturePosition.y ); + } + else + { + return Vector2( gesturePosition.x - mPageSize.width, gesturePosition.y ); + } +} + +void PageTurnLandscapeView::SetPanActor( const Vector2& panPosition ) +{ + if( panPosition.x > mPageSize.width && mCurrentPageIndex < mTotalPageCount-1 ) + { + mPanActor = mPageActors[mCurrentPageIndex%NUMBER_OF_CACHED_PAGES]; // right side page + } + else if( panPosition.x <= mPageSize.width && mCurrentPageIndex > 0 ) + { + mPanActor = mPageActors[ (mCurrentPageIndex-1)%NUMBER_OF_CACHED_PAGES ]; // left side page + } + else + { + mPanActor.Reset(); + } +} + +void PageTurnLandscapeView::SetSpineEffect(Actor actor, bool isLeftSide) +{ + if(isLeftSide) + { + SetShaderEffect( actor, mSpineEffectBack ); + } + else + { + SetShaderEffect( actor, mSpineEffectFront ); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.h b/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.h new file mode 100644 index 0000000..5dc56b6 --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-landscape-view-impl.h @@ -0,0 +1,102 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PAGE_TURN_LANDSCAPE_VIEW_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_PAGE_TURN_LANDSCAPE_VIEW_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class PageTurnLandscapeView : public PageTurnView +{ +public: + + /** + * Create a new PageTurnLandscapeView + * @return A handle to the newly allocated PageTurnLandscapeView + */ + static Toolkit::PageTurnLandscapeView New( PageFactory& pageFactory, const Vector2& pageSize ); + +protected: + /** + * Constructor. + * It initializes the PageTurnLandscapeView members + */ + PageTurnLandscapeView( PageFactory& pageFactory, const Vector2& pageSize ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~PageTurnLandscapeView(); + +protected: // From PageTurnView + + /** + * @copydoc PageTurnView::OnPageTurnViewInitialize + */ + virtual void OnPageTurnViewInitialize(); + + /** + * copydoc PageTurnView::NewPAgeFromRenderBuffer + */ + virtual ImageActor NewPageFromRenderBuffer( int pageIndex ); + + /** + * @copydoc PageTurnView::OnAddPage + */ + virtual void OnAddPage( ImageActor newPage, bool isLeftSide ); + + /** + * @copydoc PageTurnView::SetPanPosition + */ + virtual Vector2 SetPanPosition( const Vector2& gesturePosition ); + + /** + * @copydoc PageTurnView::SetPanActor + */ + virtual void SetPanActor( const Vector2& panPosition ); + + /** + * @copydoc PageTurnView::SetSpineEffect + */ + virtual void SetSpineEffect(Actor actor, bool isLeftSide); + +private: + + //Undefined + PageTurnLandscapeView( const PageTurnLandscapeView& ); + + //undefined + PageTurnLandscapeView& operator=(const PageTurnLandscapeView& rhs); + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_INTERNAL_PAGE_TURN_LANDSCAPE_VIEW_IMPL_H__ */ diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.cpp b/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.cpp new file mode 100644 index 0000000..fda15a7 --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.cpp @@ -0,0 +1,165 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +using namespace Dali; +TypeRegistration mType( typeid(Toolkit::PageTurnPortraitView), typeid(Toolkit::PageTurnView), NULL ); + +// the panning speed threshold, no matter how far is the pan displacement, pan fast to left/right quickly (speed > 0.3) will turn over/back the page +const float GESTURE_SPEED_THRESHOLD(0.3f); + +// the animation duration of turning the previous page back when an outwards flick is detected +const float PAGE_TURN_OVER_ANIMATION_DURATION(0.5f); +} + +PageTurnPortraitView::PageTurnPortraitView( PageFactory& pageFactory, const Vector2& pageSize ) +: PageTurnView( pageFactory, pageSize ) +{ + +} + +PageTurnPortraitView::~PageTurnPortraitView() +{ +} + +Toolkit::PageTurnPortraitView PageTurnPortraitView::New( PageFactory& pageFactory, const Vector2& pageSize ) +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< PageTurnPortraitView > internalPageTurnView = new PageTurnPortraitView( pageFactory, pageSize ); + + // Pass ownership to CustomActor + Dali::Toolkit::PageTurnPortraitView pageTurnView( *internalPageTurnView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalPageTurnView->Initialize(); + + return pageTurnView; +} + +void PageTurnPortraitView::OnPageTurnViewInitialize() +{ + mControlSize = mPageSize; + Self().SetSize( mPageSize ); + mTurningPageLayer.SetParentOrigin( ParentOrigin::CENTER_LEFT ); +} + +ImageActor PageTurnPortraitView::NewPageFromRenderBuffer( int pageIndex ) +{ + return ImageActor::New(mRenderedPage[pageIndex % NUMBER_OF_CACHED_PAGES]); +} + +Vector2 PageTurnPortraitView::SetPanPosition( const Vector2& gesturePosition ) +{ + return gesturePosition; +} + +void PageTurnPortraitView::SetPanActor( const Vector2& panPosition ) +{ + if( mCurrentPageIndex < mTotalPageCount ) + { + mPanActor = mPageActors[mCurrentPageIndex%NUMBER_OF_CACHED_PAGES]; + } + else + { + mPanActor.Reset(); + } +} + +void PageTurnPortraitView::SetSpineEffect(Actor actor, bool isLeftSide) +{ + if(isLeftSide) + { + actor.RemoveShaderEffect(); + } + else + { + actor.SetShaderEffect( mSpineEffectFront ); + } +} + +void PageTurnPortraitView::OnPossibleOutwardsFlick( const Vector2& panPosition, float gestureSpeed ) +{ + Vector2 offset = panPosition - mPressDownPosition; + // There is previous page and an outwards flick is detected + if( mCurrentPageIndex > 0 && gestureSpeed > GESTURE_SPEED_THRESHOLD && offset.x > fabs( offset.y )) + { + Actor actor = mPageActors[ (mCurrentPageIndex-1) % NUMBER_OF_CACHED_PAGES ]; + if(actor.GetParent() != mRootOnScreen) + { + return; + } + + // Guard against destruction during signal emission + //Emit signal, to notify that page[mCurrentPageIndex-1] is turning backwards + Toolkit::PageTurnView handle( GetOwner() ); + mPageTurnStartedSignal.Emit( handle, static_cast(mCurrentPageIndex-1), false ); + + //update pages + mCurrentPageIndex--; + RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE ); + AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE ); + OrganizePageDepth(); + + // Add the page to tuning page layer and set up PageTurnEffect + mShadowView.Add( actor ); + actor.SetShaderEffect( mTurnEffect[mIndex] ); + GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint(); + mIsAnimating[mIndex] = true; + mTurnEffect[mIndex].SetIsTurningBack( true ); + Vector2 originalCenter( mPageSize.width*1.5f, 0.5f*mPageSize.height ); + mTurnEffect[mIndex].SetOriginalCenter( originalCenter ); + mTurnEffect[mIndex].SetCurrentCenter( Vector2( mPageSize.width*0.5f, mPageSize.height*0.5f )); + + // Start an animation to turn the previous page back + Animation animation = Animation::New( PAGE_TURN_OVER_ANIMATION_DURATION ); + mAnimationActorPair[animation] = actor; + mAnimationIndexPair[animation] = mIndex; + + animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ), + originalCenter, + AlphaFunctions::EaseOut, PAGE_TURN_OVER_ANIMATION_DURATION*0.75f ); + animation.AnimateBy( Property( actor, Actor::ROTATION ), AngleAxis( Degree( 180.0f ), Vector3::YAXIS ) ,AlphaFunctions::EaseOut ); + animation.Play(); + ImageActor::DownCast(actor).SetCullFace( CullBack ); + animation.FinishedSignal().Connect( this, &PageTurnPortraitView::OnTurnedOver ); + } +} + +void PageTurnPortraitView::OnTurnedOver( Animation& animation ) +{ + ImageActor::DownCast(mAnimationActorPair[animation]).SetCullFace( CullNone ); + TurnedOver( animation ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.h b/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.h new file mode 100644 index 0000000..dde9f99 --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-portrait-view-impl.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PAGE_TURN_PORTRAIT_VIEW_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_PAGE_TURN_PORTRAIT_VIEW_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * Implementation class of the PageTurnView in portrait mode + */ +class PageTurnPortraitView : public PageTurnView +{ +public: + + /** + * Create a new PageTurnPortraitView + * @return A handle to the newly allocated PageTurnPortraitView + */ + static Toolkit::PageTurnPortraitView New( PageFactory& pageFactory, const Vector2& pageSize ); + +protected: + + /** + * Constructor. + * It initializes the PageTurnPortraitView members + */ + PageTurnPortraitView( PageFactory& pageFactory, const Vector2& pageSize ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~PageTurnPortraitView(); + +protected: // From PageTurnView + + /** + * @copydoc PageTurnView::OnPageTurnViewInitialize + */ + virtual void OnPageTurnViewInitialize(); + + /** + * @copydoc PageTurnView::NewPageFromRenderBuffer + */ + virtual ImageActor NewPageFromRenderBuffer( int pageIndex ); + + /** + * @copydoc PageTurnView::SetPanPosition + */ + virtual Vector2 SetPanPosition( const Vector2& gesturePosition ); + + /** + * @copydoc PageTurnView::SetPanActor + */ + virtual void SetPanActor( const Vector2& panPosition ); + + /** + * @copydoc PageTurnView::SetSpineEffect + */ + virtual void SetSpineEffect(Actor actor, bool isLeftSide); + + /** + * @copydoc PageTurnView::OnPossibleOutwardsFlick + */ + virtual void OnPossibleOutwardsFlick( const Vector2& panPosition, float gestureSpeed ); + +private: + + /** + * @copydoc PageTurnView::TurnedOver + */ + void OnTurnedOver( Animation& animation ); + +private: + + //Undefined + PageTurnPortraitView( const PageTurnPortraitView& ); + + //undefined + PageTurnPortraitView& operator=(const PageTurnPortraitView& rhs); + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_INTERNAL_PAGE_TURN_PORTRAIT_VIEW_IMPL_H__ */ diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.cpp b/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.cpp new file mode 100644 index 0000000..078be1c --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.cpp @@ -0,0 +1,1063 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +using namespace Dali; + +namespace //unnamed namespace +{ +// To register type +TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL ); + +// default grid density for page turn effect, 10 pixels by 10 pixels +const float DEFAULT_GRID_DENSITY(10.0f); + +// to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO +const float MINIMUM_START_POSITION_RATIO(0.6f); + +// the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO +const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f); + +// when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over +const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f); + +// duration of animation, shorter for faster speed +const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f); +const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f); + +// the major&minor radius (in pixels) to form an ellipse shape +// the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow +const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f); + +// constants for shadow casting +const float POINT_LIGHT_HEIGHT_RATIO(2.f); +const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f); + +// constraints //////////////////////////////////////////////////////////////// +/** + * Original Center Constraint + * + * This constraint adjusts the original center property of the page turn shader effect + * based on the X-direction displacement of the pan gesture + */ +struct OriginalCenterConstraint +{ + OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset) + : mOldCenter( originalCenter ) + { + mNewCenter = originalCenter + offset; + mDistance = offset.Length() * 0.5f; + mDirection = offset / mDistance; + } + + Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement) + { + float displacement = panDisplacement.GetFloat(); + + if( displacement < mDistance ) + { + return mOldCenter + mDirection * displacement; + } + else + { + return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f); + } + } + + Vector2 mOldCenter; + Vector2 mNewCenter; + float mDistance; + Vector2 mDirection; +}; + +/** + * Rotation Constraint + * + * This constraint adjusts the rotation property of the page actor + * based on the X-direction displacement of the pan gesture + */ +struct RotationConstraint +{ + RotationConstraint( float distance, float pageWidth, bool isTurnBack ) + : mDistance( distance*0.5f ) + { + mStep = 1.f / pageWidth; + mSign = isTurnBack ? -1.0f : 1.0f; + mConst = isTurnBack ? -1.0f : 0.f; + mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS ); + } + + Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement ) + { + float displacement = panDisplacement.GetFloat(); + float angle; + if( displacement < mDistance) + { + return mRotation; + } + else + { + float coef = std::max(-1.0f, mStep*(mDistance-displacement)); + angle = Math::PI*( mConst + mSign*coef ); + return Quaternion( angle, Vector3::YAXIS ); + } + } + + float mDistance; + float mStep; + float mConst; + float mSign; + Quaternion mRotation; +}; + +/** + * Current Center Constraint + * + * This constraint adjusts the current center property of the page turn shader effect + * based on the pan position and the original center position + */ +struct CurrentCenterConstraint +{ + CurrentCenterConstraint( float pageWidth) + : mPageWidth( pageWidth ) + { + mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f; + } + + Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter ) + { + Vector2 centerPosition = center.GetVector2(); + if( centerPosition.x > 0.f ) + { + return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y); + } + else + { + Vector2 centerOrigin = originalCenter.GetVector2(); + Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y); + float coef = 1.f+(centerPosition.x*2.f / mPageWidth); + // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce + if(coef < 0.025f) + { + coef = (coef+0.225f)/10.0f; + } + return centerOrigin - direction * coef; + } + } + + float mPageWidth; + float mThres; +}; + +struct ShadowBlurStrengthConstraint +{ + ShadowBlurStrengthConstraint( float thres ) + : mThres( thres ) + {} + + float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement) + { + float displacement = panDisplacement.GetFloat(); + float blurStrength; + if( EqualsZero(displacement)) + { + Vector2 cur = currentCenter.GetVector2(); + Vector2 ori = originalCenter.GetVector2(); + blurStrength = 5.f*(ori-cur).Length() / mThres; + } + else + { + blurStrength = 1.f - (displacement-2.f*mThres)/mThres; + } + + blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength ); + return blurStrength; + } + + float mThres; +}; + +bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type ) +{ + bool hittable = false; + + switch (type) + { + case Dali::HitTestAlgorithm::CHECK_ACTOR: + { + // Check whether the actor is visible and not fully transparent. + Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE); + if( actor.IsSensitive() + && actor.IsVisible() + && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT + && ( propertyActorHittable != Property::INVALID_INDEX && + actor.GetProperty( propertyActorHittable ) ) ) + { + hittable = true; + } + break; + } + case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE: + { + if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible. + { + hittable = true; + } + break; + } + default: + { + break; + } + } + + return hittable; +} + +} //unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +// these several constants are also used in the derived classes +const int PageTurnView::MAXIMUM_TURNING_NUM = 4; +const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1; +const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2; +const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f; + +PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize ) +: ControlImpl( true ), + mPageFactory( pageFactory ), + mPageSize( pageSize ), + mIsEditMode( false ), + mPanning( false ), + mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ), + mCurrentPageIndex( 0 ), + mIndex( 0 ), + mPress( false ), + mPageUpdated( true ), + mPageTurnStartedSignal(), + mPageTurnFinishedSignal(), + mPagePanStartedSignal(), + mPagePanFinishedSignal() +{ + mPageActors.resize( NUMBER_OF_CACHED_PAGES ); + mIsAnimating.resize( MAXIMUM_TURNING_NUM ); + mIsSliding.resize( MAXIMUM_TURNING_NUM ); + mTurnEffect.resize( MAXIMUM_TURNING_NUM ); + mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM ); + mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM ); +} + +PageTurnView::~PageTurnView() +{ +} + +void PageTurnView::OnInitialize() +{ + // create the two book spine effect for static images, left and right side pages respectively + mSpineEffectFront = PageTurnBookSpineEffect::New(); + mSpineEffectFront.SetIsBackImageVisible( false ); + mSpineEffectFront.SetPageWidth( mPageSize.width ); + mSpineEffectFront.SetShadowWidth( 0.f ); + mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter ); + + mSpineEffectBack = PageTurnBookSpineEffect::New(); + mSpineEffectBack.SetIsBackImageVisible( true ); + mSpineEffectBack.SetPageWidth( mPageSize.width ); + mSpineEffectBack.SetShadowWidth( 0.f ); + mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter ); + + // create the page turn effect objects + for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ ) + { + mTurnEffect[i] = Toolkit::PageTurnEffect::New( false ); + mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) ); + mTurnEffect[i].SetPageSize( mPageSize ); + mTurnEffect[i].SetShadowWidth(0.f); + mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter ); + mIsAnimating[i] = false; + mIsSliding[i] = false; + mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f); + mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f)); + } + + mTurningPageLayer = Layer::New(); + mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + // Set control size and the parent origin of turningPageLayer + OnPageTurnViewInitialize(); + + mRootOnScreen = Actor::New(); + mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION ); + mRootOnScreen.SetSize( mControlSize ); + Self().Add( mRootOnScreen ); + mRootOnScreen.Add(mTurningPageLayer); + + mTotalPageCount = static_cast( mPageFactory.GetNumberOfPages() ); + mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded(); + if( mNeedOffscreenRendering ) + { + SetupRenderTasks(); + } + + // add pages to the scene, and set depth for the stacked pages + for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ ) + { + AddPage( i ); + if(mPageActors[i]) + { + mPageActors[i].SetZ( -static_cast( i )*STATIC_PAGE_INTERVAL_DISTANCE ); + } + } + + // enable the pan gesture which is attached to the control + EnableGestureDetection(Gesture::Type(Gesture::Pan)); + + mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage); +} + +void PageTurnView::SetupRenderTasks() +{ + mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES ); + mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES ); + mRenderedPage.resize( NUMBER_OF_CACHED_PAGES ); + + mCameraActor = CameraActor::New(mControlSize); + mCameraActor.SetParentOrigin(ParentOrigin::CENTER); + mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION ); + mCameraActor.SetInheritScale( false ); + mCameraActor.SetInvertYAxis(false); + Self().Add(mCameraActor); + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + for(int i=0; i(pageId); + // record the new current page index + mCurrentPageIndex = pageIdx; + + // clear the old pages + for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ ) + { + if( mPageActors[i] ) + { + mPageActors[i].Unparent(); + mPageActors[i].Reset(); + } + } + + // add the current page and the pages right before and after it + for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ ) + { + AddPage( i ); + } + // set ordered depth to the stacked pages + OrganizePageDepth(); +} + +unsigned int PageTurnView::GetCurrentPage() +{ + DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 ); + return static_cast< unsigned int >( mCurrentPageIndex ); +} + +Actor PageTurnView::EnterEditMode() +{ + if( mNeedOffscreenRendering ) + { + DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 ); + + mIsEditMode = true; + + int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES; + mOffscreenTask[index].SetInputEnabled( true ); + mPageSourceActor[index].SetSensitive( true ); + mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS ); + + mRootOnScreen.SetSensitive(false); + + return mPageSourceActor[index].GetChildAt( 0 ); + } + else + { + return Actor(); + } +} + +void PageTurnView::LeaveEditMode() +{ + if( mNeedOffscreenRendering ) + { + DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 ); + + mIsEditMode = false; + + int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES; + mOffscreenTask[index].SetInputEnabled( false ); + mPageSourceActor[index].SetSensitive( false ); + mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE ); + + mRootOnScreen.SetSensitive(true); + } +} + +Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates ) +{ + if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount) + { + int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES; + + Dali::HitTestAlgorithm::Results results; + if( !mOffscreenTask[index].GetInputEnabled() ) + { + mOffscreenTask[index].SetInputEnabled( true ); + mPageSourceActor[index].SetSensitive( true ); + Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction ); + mOffscreenTask[index].SetInputEnabled( false ); + mPageSourceActor[index].SetSensitive( false ); + } + else + { + Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction ); + } + actorCoordinates = results.actorCoordinates; + return results.actor; + } + else + { + return Actor(); + } +} + +void PageTurnView::AddPage( int pageIndex ) +{ + if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory + { + int index = pageIndex % NUMBER_OF_CACHED_PAGES; + ImageActor newPage; + if( mNeedOffscreenRendering ) + { + Actor source = mPageFactory.NewPage( pageIndex ); + if( mPageSourceActor[index].GetChildCount() > 0 ) + { + mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) ); + } + mPageSourceActor[index].Add( source ); + mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE ); + newPage = NewPageFromRenderBuffer( pageIndex ); + } + else + { + newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) ); + DALI_ASSERT_ALWAYS( newPage ); + } + newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT ); + newPage.SetSize( mPageSize ); + mRootOnScreen.Add( newPage ); + mPageActors[index] = newPage; + + bool isLeftSide = ( pageIndex < mCurrentPageIndex ); + mIsTurnBack[ newPage ] = isLeftSide; + if( isLeftSide ) + { + // new page is added to the left side, so need to rotate it 180 degrees + newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS ); + } + else + { + SetShaderEffect( newPage, mSpineEffectFront); + } + + // For Portrait, nothing to do + // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists + OnAddPage( newPage, isLeftSide ); + } +} + +void PageTurnView::RemovePage( int pageIndex ) +{ + if( pageIndex > -1 && pageIndex < mTotalPageCount) + { + int index = pageIndex % NUMBER_OF_CACHED_PAGES; + mPageActors[index].Unparent(); + mIsTurnBack.erase( mPageActors[index] ); + mPageActors[index].Reset(); + if( mNeedOffscreenRendering ) + { + mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) ); + } + } +} + +void PageTurnView::RenderPage( int pageIndex ) +{ + if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1) + && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE)) + { + int index = pageIndex % NUMBER_OF_CACHED_PAGES; + mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE ); + } +} + +void PageTurnView::RefreshAll() +{ + mTotalPageCount = static_cast( mPageFactory.GetNumberOfPages() ); + if( mTotalPageCount > 0 ) + { + if(mCurrentPageIndex < mTotalPageCount) + { + GoToPage( mCurrentPageIndex ); + } + else + { + GoToPage( mCurrentPageIndex-- ); + } + } +} + +void PageTurnView::RefreshCurrentPage() +{ + RenderPage( mCurrentPageIndex ); +} + +void PageTurnView::OnPan( PanGesture gesture ) +{ + if( mIsEditMode ) + { + // when interrupted by the call of DisplayCurrentPageSourceActor(), + // make sure the panFinished is always called before stopping to responding the gesture + // so the status of the control is updated correctly + if(mPanning) + { + mPanning = false; + PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() ); + } + + return; + } + // the pan gesture is attached to control itself instead of each page + switch( gesture.state ) + { + case Gesture::Started: + { + mPanning = true; + // to find out whether the undergoing turning page number already reaches the maximum allowed + // and get one idle index when it is animatable + bool animatable = false; + for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ ) + { + if( !mIsAnimating[mIndex] ) + { + animatable = true; + break; + } + if( mIsSliding[mIndex] ) + { + animatable = false; + break; + } + mIndex++; + mIndex = mIndex % MAXIMUM_TURNING_NUM; + } + + if( mPageUpdated && animatable ) + { + SetPanActor( gesture.position ); // determine which page actor is panned + if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently + { + mPanActor.Reset(); + } + PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate + } + else + { + mPanActor.Reset(); + } + break; + } + case Gesture::Continuing: + { + PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate + break; + } + case Gesture::Finished: + case Gesture::Cancelled: + { + mPanning = false; + PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() ); + break; + } + case Gesture::Clear: + case Gesture::Possible: + default: + { + break; + } + } +} + +void PageTurnView::PanStarted( const Vector2& gesturePosition ) +{ + mPressDownPosition = gesturePosition; + + if( !mPanActor ) + { + return; + } + + mOriginalCenter = gesturePosition; + mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] ); + mPress = false; + mPageUpdated = false; + + // Guard against destruction during signal emission + Toolkit::PageTurnView handle( GetOwner() ); + mPagePanStartedSignal.Emit( handle ); +} + +void PageTurnView::PanContinuing( const Vector2& gesturePosition ) +{ + if( !mPanActor ) + { + return; + } + + // Guard against destruction during signal emission + Toolkit::PageTurnView handle( GetOwner() ); + + if(!mPress) + { + // when the touch down position is near the spine + // or when the panning goes outwards or some other position which would tear the paper in real situation + // we change the start position into the current panning position and update the shader parameters + if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO + || gesturePosition.x > mOriginalCenter.x-1.0f + || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) && + ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) ) + { + mOriginalCenter = gesturePosition; + } + else + { + mDistanceUpCorner = mOriginalCenter.Length(); + mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length(); + mShadowView.Add( mPanActor ); + SetShaderEffect( mPanActor, mTurnEffect[mIndex] ); + mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter ); + mCurrentCenter = mOriginalCenter; + mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter ); + mPanDisplacement = 0.f; + mConstraints = true; + mPress = true; + mIsAnimating[mIndex] = true; + + mPageTurnStartedSignal.Emit( handle, static_cast(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] ); + + mShadowView.RemoveConstraints(); + Actor self = Self(); + self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f ); + Constraint shadowBlurStrengthConstraint = Constraint::New( mShadowView.GetBlurStrengthPropertyIndex(), + Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())), + Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())), + Source( self, mPropertyPanDisplacement[mIndex] ), + ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) ); + mShadowView.ApplyConstraint( shadowBlurStrengthConstraint ); + } + } + else + { + Vector2 currentCenter = gesturePosition; + + // Test whether the new current center would tear the paper from the top pine in real situation + // we do not forbid this totally, which would restrict the panning gesture too much + // instead, set it to the nearest allowable position + float distanceUpCorner = currentCenter.Length(); + float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length(); + if( distanceUpCorner > mDistanceUpCorner ) + { + currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner; + } + // would tear the paper from the bottom spine in real situation + if( distanceBottomCorner > mDistanceBottomCorner ) + { + currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) ); + } + // If direction has a very high y component, reduce it. + Vector2 curveDirection = currentCenter - mOriginalCenter; + if( fabs( curveDirection.y ) > fabs( curveDirection.x ) ) + { + currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y ); + } + // If the vertical distance is high, reduce it + float yShift = currentCenter.y - mOriginalCenter.y; + if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO ) + { + currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift ); + } + + // use contraints to control the page shape and rotation when the pan position is near the spine + if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) + { + // set the property values used by the constraints + mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x; + Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement ); + Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter ); + + // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect + // also set up the RotationConstraint to the page actor + if( mConstraints ) + { + Vector2 corner; + // the corner position need to be a little far away from the page edge to ensure the whole page is lift up + if( currentCenter.y >= mOriginalCenter.y ) + { + corner = Vector2( 1.1f*mPageSize.width, 0.f ); + } + else + { + corner = mPageSize*1.1f; + } + + Vector2 offset( currentCenter-mOriginalCenter ); + float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y ) + /( offset.x*offset.x + offset.y*offset.y ); + offset *= k; + Actor self = Self(); + Source source(self, mPropertyPanDisplacement[mIndex]); + + Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName()); + Constraint originalCenterConstraint = Constraint::New( shaderOriginalCenterPropertyIndex , + source, + OriginalCenterConstraint( mOriginalCenter, offset )); + mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint ); + + Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName()); + Constraint currentCenterConstraint = Constraint::New( shaderCurrentCenterPropertyIndex, + Source(self, mPropertyCurrentCenter[mIndex]), + Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex), + CurrentCenterConstraint(mPageSize.width)); + mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint ); + + GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint(); + + float distance = offset.Length(); + Constraint rotationConstraint = Constraint::New( Actor::ROTATION, + Source( self, mPropertyPanDisplacement[mIndex] ), + RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor])); + mPanActor.ApplyConstraint( rotationConstraint ); + + mConstraints = false; + } + } + else + { + if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine + { + mPanActor.RemoveConstraints(); + mTurnEffect[mIndex].RemoveConstraints(); + mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter ); + mConstraints = true; + mPanDisplacement = 0.f; + } + + mTurnEffect[mIndex].SetCurrentCenter( currentCenter ); + mCurrentCenter = currentCenter; + GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint(); + } + } +} + +void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed ) +{ + // Guard against destruction during signal emission + Toolkit::PageTurnView handle( GetOwner() ); + + if( !mPanActor ) + { + if(!mIsAnimating[mIndex]) + { + OnPossibleOutwardsFlick( gesturePosition, gestureSpeed ); + } + return; + } + + mPagePanFinishedSignal.Emit( handle ); + + Actor actor = mPanActor; + if(mPress) + { + if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over + { + // update the pages here instead of in the TurnedOver callback function + // as new page is allowed to respond to the pan gesture before other pages finishing animation + if(mIsTurnBack[actor]) + { + mCurrentPageIndex--; + RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE ); + AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE ); + } + else + { + mCurrentPageIndex++; + RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 ); + AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 ); + } + OrganizePageDepth(); + + // set up an animation to turn the page over + Actor self = Self(); + float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO); + Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) ); + animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]), + width,AlphaFunctions::EaseOutSine33); + animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]), + Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33); + mAnimationActorPair[animation] = actor; + mAnimationIndexPair[animation] = mIndex; + animation.Play(); + animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver ); + } + else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over + { + Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO ); + animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ), + mOriginalCenter, AlphaFunctions::Linear ); + mAnimationActorPair[animation] = actor; + mAnimationIndexPair[animation] = mIndex; + animation.Play(); + mIsSliding[mIndex] = true; + animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack ); + + mPageTurnStartedSignal.Emit( handle, static_cast( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] ); + } + } + else + { + // In portrait view, an outwards flick should turn the previous page back + // In landscape view, nothing to do + OnPossibleOutwardsFlick( gesturePosition, gestureSpeed ); + } + + mPageUpdated = true; +} + +void PageTurnView::TurnedOver( Animation& animation ) +{ + Actor actor = mAnimationActorPair[animation]; + mIsTurnBack[actor] = !mIsTurnBack[actor]; + actor.RemoveConstraints(); + mRootOnScreen.Add(actor); + int index = mAnimationIndexPair[animation]; + mIsAnimating[index] = false; + mTurnEffect[index].RemoveConstraints(); + mAnimationIndexPair.erase( animation ); + mAnimationActorPair.erase( animation ); + + SetSpineEffect( actor, mIsTurnBack[actor] ); + + // Guard against destruction during signal emission + Toolkit::PageTurnView handle( GetOwner() ); + mPageTurnFinishedSignal.Emit( handle, static_cast( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] ); +} + +void PageTurnView::SliddenBack( Animation& animation ) +{ + Actor actor = mAnimationActorPair[animation]; + mRootOnScreen.Add(actor); + int index = mAnimationIndexPair[animation]; + mIsSliding[index] = false; + mIsAnimating[index] = false; + mAnimationIndexPair.erase( animation ); + mAnimationActorPair.erase( animation ); + + SetSpineEffect( actor, mIsTurnBack[actor] ); + + // Guard against destruction during signal emission + Toolkit::PageTurnView handle( GetOwner() ); + mPageTurnFinishedSignal.Emit( handle, static_cast( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] ); +} + +void PageTurnView::OrganizePageDepth() +{ + for( int i=0; i( i )*STATIC_PAGE_INTERVAL_DISTANCE ); + } + if( mCurrentPageIndex >= i + 1 ) + { + mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast( i )*STATIC_PAGE_INTERVAL_DISTANCE ); + } + } +} + +void PageTurnView::SetShaderEffect( Actor actor, ShaderEffect shaderEffect ) +{ + actor.SetShaderEffect( shaderEffect ); + + if( actor.GetChildCount() > 0 ) + { + actor.GetChildAt( 0 ).SetShaderEffect(shaderEffect); + } +} + +Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal() +{ + return mPageTurnStartedSignal; +} + +Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal() +{ + return mPageTurnFinishedSignal; +} + +Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal() +{ + return mPagePanStartedSignal; +} + +Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal() +{ + return mPagePanFinishedSignal; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h b/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h new file mode 100644 index 0000000..1354c19 --- /dev/null +++ b/dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h @@ -0,0 +1,386 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PAGE_TURN_VIEW_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_PAGE_TURN_VIEW_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class PageTurnView : public ControlImpl +{ +protected: + + /** + * Constructor. + * It initializes the PageTurnView members + */ + PageTurnView( PageFactory& pageFactory, const Vector2& pageSize ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~PageTurnView(); + +public: + + /** + * @copydoc Toolkit::PageTurnView::SetSpineShadowParameter + */ + void SetSpineShadowParameter( const Vector2& spineShadowParameter ); + + /** + * @copydoc Toolkit::PageTurnView::GetSpineShadowParameter + */ + Vector2 GetSpineShadowParameter(); + + /** + * @copydoc Toolkit::PageTurnView::GoToPage + */ + void GoToPage( unsigned int pageId ); + + /** + * @copydoc Toolkit::PageTurnView::GetCurrentPage + */ + unsigned int GetCurrentPage(); + + /** + * @copydoc Toolkit::PageTurnView::EnterEditMode + */ + Actor EnterEditMode(); + + /** + * @copydoc Toolkit::PageTurnView::LeaveEditMode + */ + void LeaveEditMode(); + + /** + * @copydoc Toolkit::PageTurnView::GetHitActor + */ + Actor GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates ); + + /** + * @copydoc Toolkit::PageTurnView::RefreshAll + */ + void RefreshAll(); + + /** + * @copydoc Toolkit::PageTurnView::RefreshCurrentPage + */ + void RefreshCurrentPage(); + +protected: + + /** + * This method gets a page from the factory and add to the control + * to keep NUMBER_OF_CACHED_PAGES_EACH_SIDE pages available in each side + * @param[in] pageIndex The index of the page to be added + */ + void AddPage( int pageIndex ); + + /** + * This method removes a page from the control + * to keep only NUMBER_OF_CACHED_PAGES_EACH_SIDE pages available in each side + * @param[in] pageIndex The index of the page to be removed + */ + void RemovePage( int pageIndex ); + + /** + * This method updates the actor and animation states after one page is turned over + * This method is a callback, connected when receiving the finished signal of a page turning over animation. + * @param [in] the page turning over animation handle + */ + void TurnedOver( Animation& animation ); + + /** + * This method organize the depth of the pages on stage + * It is called when there is page added or removed from the control + */ + void OrganizePageDepth(); + + /** + * Set shader Effect to the actor. + * If the actor has children, the shader effect is also applied to its first child + * @param[in] actor The actor which the shader effect would be applied onto + * @param[in] shaderEffect The shader effect to be set to the actor + */ + void SetShaderEffect( Actor actor, ShaderEffect shaderEffect ); + +private: + + /** + * Set up the render tasks for rendering the page actor to off-screen image + */ + void SetupRenderTasks(); + + /** + * Set up the shadow view control to cast shadow + */ + void SetupShadowView(); + + /** + * This method defines the processes when the pan started, gets called by OnPan( .. ) + * @param[in] gesturePosition The current touch position in local page actor coordinates. + */ + void PanStarted( const Vector2& gesturePosition ); + + /** + * This method defines the processes when the pan continuing, gets called by OnPan( .. ) + * @param[in] gesturePosition The current touch position in local page actor coordinates. + */ + void PanContinuing( const Vector2& gesturePosition ); + + /** + * This method defines the processes when the pan finished, gets called by OnPanGesture( .. ) + * @param[in] gesturePosition The current touch position in local page actor coordinates. + * @param[in] gestureSpeed The speed of the pan ( in pixels per millisecond ) + */ + void PanFinished( const Vector2& gesturePosition, float gestureSpeed ); + + /** + * This method updates the actor and the animation states after one page is slidden back instead of turned over + * This method is a callback, connected when receiving the finished signal of a page sliding back animation. + * @param [in] the page sliding back animation handle + */ + void SliddenBack( Animation& animation ); + + /** + * Refresh the given page. + @param[in] the page index. + */ + void RenderPage( int pageIndex ); + +private: // from ControlImpl + + /** + * @copydoc Toolkit::Control::OnPan + */ + virtual void OnPan( PanGesture gesture ); + + /** + * @copydoc Toolkit::Control::OnInitialize + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnControlStageConncection + */ + virtual void OnControlStageConnection(); + + /** + * @copydoc Toolkit::Control::OnControlStageDisConnection + */ + virtual void OnControlStageDisconnection(); + + /** + * @copydoc Toolkit::Control::OnControlSizeSet + */ + virtual void OnControlSizeSet( const Vector3& size ); + +private: // implemented differently by PageTurnLandscapeView and PageTurnPortraitView + + /** + * This method is called after the pageTurnView initialization. + * To set the size of the control size and the parent origin of turning page layer + * Implemented in subclasses to provide specific behaviour. + */ + virtual void OnPageTurnViewInitialize() = 0; + + /** + * Create the page actor from off screen buffer + * @param[in] The index of the page to be added + */ + virtual ImageActor NewPageFromRenderBuffer( int pageIndex ) = 0; + + /** + * This method is called after the a new page is added to the stage. + * Could be re-implemented in subclasses to provide specific behaviour + * @param[in] newPage The added page actor + * @param[in] isLeftSide Which side the new page is added to + */ + virtual void OnAddPage( ImageActor newPage, bool isLeftSide ) { } + + /** + * This method is called when pan started or continuing + * to calculate the pan position in local page actor coordinate given the pan position in control coordinate + * Implemented in subclasses to provide specific behaviour. + * @param[in] gesturePosition The pan position in the control coordinate + * @return The pan position in the page actor local coordinate + */ + virtual Vector2 SetPanPosition( const Vector2& gesturePosition ) = 0; + + /** + * This method is called when pan started to determined which page is panned given the pan position in control coordinate + * Implemented in subclasses to provide specific behaviour. + * @param[in] gesturePosition The pan position in the control coordinate + */ + virtual void SetPanActor( const Vector2& panPosition ) = 0; + + /** + * This method is called when a page is turned over or slidden back + * Remove PageTurnEffect and use a proper PageTurnBookSpineEffect + * Implemented in subclasses to provide specific behaviour. + * @param[in] actor The current page actor + * @param[in] isLeftSide Which side the current page is located + */ + virtual void SetSpineEffect(Actor actor, bool isLeftSide) = 0; + + /** + * This method is called when pan finished to detect outwards flick + * In portrait view, start an animation of turning previous page back when outwards flick is detected + * In landscape view, nothing to do + * @param[in] panPosition The pan position in the page actor local coordinate + * @param[in] gestureSpeed The speed of the pan gesture( in pixels per millisecond ) + */ + virtual void OnPossibleOutwardsFlick( const Vector2& panPosition, float gestureSpeed ) { } + +public: //signal + + /** + * @copydoc Toolkit::PageTurnView::PageTurnStartedSignal() + */ + Toolkit::PageTurnView::PageTurnSignal& PageTurnStartedSignal(); + + /** + * @copydoc Toolkit::PageTurnView::PageTurnFinishedSignal() + */ + Toolkit::PageTurnView::PageTurnSignal& PageTurnFinishedSignal(); + + /** + * @copydoc Toolkit::PageTurnView::PagePanStartSignal() + */ + Toolkit::PageTurnView::PagePanSignal& PagePanStartedSignal(); + + /** + * @copydoc Toolkit::PageTurnView::PagePanFinishedSignal() + */ + Toolkit::PageTurnView::PagePanSignal& PagePanFinishedSignal(); + +private: + + //Undefined + PageTurnView( const PageTurnView& ); + + //undefined + PageTurnView& operator=(const PageTurnView& rhs); + +protected: + + Actor mRootOnScreen; + + Vector2 mControlSize; ///< The size of the control, it is decided by the page size, the SetSize from application can not change it + Layer mTurningPageLayer; ///< The layer for the turning page, to avoid possible depth conflict + Toolkit::ShadowView mShadowView; ///< The shadow view control for shadow casting + ImageActor mShadowPlane; ///< The plane for the shadow to cast on + Actor mPointLight; ///< The point light used for shadow casting + Layer mShadowLayer; ///< The layer to display the shadow + + PageFactory& mPageFactory; ///< The page factory which provides the page actors + Vector2 mPageSize; ///< The page size + int mTotalPageCount; ///< The total number of pages provided by the page factory + + bool mIsEditMode; ///< The boolean to indicate the current page content is edit-able or not + + bool mNeedOffscreenRendering; ///< The boolean to indicate whether off screen rendering is required for creating page image + std::vector mOffscreenTask; ///< The vector of off screen rendering tasks + std::vector mPageSourceActor; ///< The vector of page source actor + std::vector mRenderedPage; ///< The vector of off screen buffers + CameraActor mCameraActor; ///< The camera actor attached to the off screen tasks + bool mPanning; ///< The boolean to indicate whether the pan gesture is continuing + + std::vector mTurnEffect; ///< The group of PageTurnEffects + PageTurnBookSpineEffect mSpineEffectFront; ///< The book spine shader effect without flipping image content + PageTurnBookSpineEffect mSpineEffectBack; ///< The book spine shader effect with image content flipped + Vector2 mSpineShadowParameter; ///< The spine shadow parameter for all the above shader effects + Vector2 mOriginalCenter; ///< The original center set to the PageTurnEffect + Vector2 mCurrentCenter; ///< The current center set to the PageTurnEffect + + std::vector mPageActors; ///< The vector of pages on stage + int mCurrentPageIndex; ///< The index of the current page, between 0 ~ mTotalPageCount-1 + std::map mIsTurnBack; ///< The map to keep track the page actor's turning direction + std::map mAnimationActorPair; ///< The map to keep track which page actor is the animation act on + std::map mAnimationIndexPair; ///< The map to keep track which PageTurnEffect, PanDisplacementProperty, CurrentCenterProperty is used for the animation + int mIndex; ///< The index to keep track which PageTurnEffect, PanDisplacementProperty, CurrentCenterProperty is used for the current panning page + std::vector mIsAnimating; ///< The boolean vector to keep track which PageTurnEffect, PanDisplacementProperty, CurrentCenterProperty is available for using + std::vector mIsSliding; ///< The boolean vector to keep track whether there are animating pages sliding back + + ImageActor mPanActor; ///< The page being panned by the pan gesture + Vector2 mPressDownPosition; ///< The first press down position of the pan gesture + bool mPress; ///< The boolean to keep track the state of the pageTurnEffect is activated or not + bool mPageUpdated; ///< The boolean to keep track whether is page is updated after any turning activity + + float mDistanceUpCorner; ///< The distance between the original center of PageTurnEffect and the top-left corner of the page + float mDistanceBottomCorner; ///< The distance between the original center of PageTurnEffect and the bottom-left corner of the page + + std::vector mPropertyPanDisplacement; ///< The pan displacement property group + std::vector mPropertyCurrentCenter; ///< The current center property group + float mPanDisplacement; ///< The displacement of the pan after the constrains are applied + bool mConstraints; ///< The boolean to keep track the constrains are applied or not + + Toolkit::PageTurnView::PageTurnSignal mPageTurnStartedSignal; ///< The signal to notify that a page has started turning + Toolkit::PageTurnView::PageTurnSignal mPageTurnFinishedSignal; ///< The signal to notify that a page has finished turning + Toolkit::PageTurnView::PagePanSignal mPagePanStartedSignal; ///< The signal to notify that a page has started panning + Toolkit::PageTurnView::PagePanSignal mPagePanFinishedSignal; ///< The signal to notify that a page has finished panning + + static const int MAXIMUM_TURNING_NUM; ///< How many pages are allowed to animating in the same time + static const int NUMBER_OF_CACHED_PAGES_EACH_SIDE; ///< The maximum number of pages kept, (MAXIMUM_ANIMATION_NUM+1) pages for each side + static const int NUMBER_OF_CACHED_PAGES; ///< The maximum number of pages kept, (MAXIMUM_ANIMATION_NUM+1)*2 pages in total + static const float STATIC_PAGE_INTERVAL_DISTANCE; ///< The depth interval between stacked pages (static pages) +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::PageTurnView& GetImplementation(Toolkit::PageTurnView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::PageTurnView& GetImplementation(const Toolkit::PageTurnView& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_INTERNAL_PAGE_TURN_VIEW_IMPL_H__ */ diff --git a/dali-toolkit/internal/controls/popup/popup-impl.cpp b/dali-toolkit/internal/controls/popup/popup-impl.cpp new file mode 100755 index 0000000..db01ab0 --- /dev/null +++ b/dali-toolkit/internal/controls/popup/popup-impl.cpp @@ -0,0 +1,1048 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +using namespace Dali; +using namespace std; + +namespace +{ +const float CONTENT_DEPTH = 1.0f; ///< 3D Effect of buttons/title etc. appearing off the popup. +const float POPUP_ANIMATION_DURATION = 0.5f; ///< Duration of hide/show animations +const float BACKING_DEPTH = -1.0f; ///< Depth of backing (positioned just behind dialog, so dialog catches hit events first) + +const float POPUP_WIDTH = 720.0f; ///< Width of Popup +const float POPUP_OUT_MARGIN_WIDTH = 16.f; ///< Space between the screen edge and the popup edge in the horizontal dimension. +const float POPUP_OUT_MARGIN_HEIGHT = 36.f; ///< Space between the screen edge and the popup edge in the vertical dimension. +const float POPUP_TITLE_WIDTH = 648.0f; ///Initialize(); + + return handle; +} + +Popup::Popup(PopupStyle& style) +: ControlImpl(true), + mShowing(false), + mState(Toolkit::Popup::POPUP_NONE), // Initially, the popup state should not be set, it's set in OnInitialize + mAlterAddedChild(false), + mPopupStyle(PopupStylePtr(&style)) +{ + SetKeyboardNavigationSupport( true ); +} + +void Popup::OnInitialize() +{ + Actor self = Self(); + self.SetSensitive(false); + + // Create Layer + mLayer = Layer::New(); + mLayer.SetParentOrigin(ParentOrigin::CENTER); + mLayer.SetAnchorPoint(AnchorPoint::CENTER); + mLayer.RaiseToTop(); + mLayer.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + self.Add(mLayer); + + mPopupBg = Actor::New(); + mPopupBg.SetParentOrigin(ParentOrigin::CENTER); + mPopupBg.SetAnchorPoint(AnchorPoint::CENTER); + mPopupBg.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mLayer.Add(mPopupBg); + + // Any content after this point which is added to Self() will be reparented to + // mContent. + mAlterAddedChild = true; + + // Add Backing (Dim effect) + CreateBacking(); + + // Add Dialog ( background image, title, content container, button container and tail ) + CreateDialog(); + + // Default content. + ShowTail(ParentOrigin::BOTTOM_CENTER); + + // Hide content by default. + SetState( Toolkit::Popup::POPUP_HIDE, 0.0f ); + + mPropertyTitle = self.RegisterProperty( Dali::Toolkit::Popup::PROPERTY_TITLE, "", Property::READ_WRITE ); + mPropertyState = self.RegisterProperty( Dali::Toolkit::Popup::PROPERTY_STATE, "POPUP_HIDE", Property::READ_WRITE ); + + // Make self as keyboard focusable and focus group + self.SetKeyboardFocusable(true); + SetAsKeyboardFocusGroup(true); +} + +void Popup::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + if( index == mPropertyTitle ) + { + SetTitle(propertyValue.Get()); + } + else if ( index == mPropertyState ) + { + std::string value( propertyValue.Get() ); + if(value == "POPUP_SHOW") + { + SetState( Toolkit::Popup::POPUP_SHOW, 0.0f ); + } + else if( value == "POPUP_HIDE") + { + SetState( Toolkit::Popup::POPUP_HIDE, 0.0f ); + } + } +} + +Popup::~Popup() +{ +} + +size_t Popup::GetButtonCount() const +{ + return mButtons.size(); +} + +void Popup::SetBackgroundImage( Actor image ) +{ + // Removes any previous background. + if( mBackgroundImage && mPopupBg ) + { + mPopupBg.Remove( mBackgroundImage ); + } + + // Adds new background to the dialog. + mBackgroundImage = image; + + // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing. + mBackgroundImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + + mPopupBg.Add( mBackgroundImage ); +} + +void Popup::SetButtonAreaImage( Actor image ) +{ + // Removes any previous area image. + if( mButtonAreaImage && mPopupBg ) + { + mPopupBg.Remove( mButtonAreaImage ); + } + + // Adds new area image to the dialog. + mButtonAreaImage = image; + + // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing. + mButtonAreaImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + + mPopupBg.Add( mButtonAreaImage ); +} + +void Popup::SetTitle( const std::string& text ) +{ + Toolkit::TextView titleActor = Toolkit::TextView::New(); + titleActor.SetText( text ); + titleActor.SetColor( Color::BLACK ); + titleActor.SetMultilinePolicy( Toolkit::TextView::SplitByWord ); + titleActor.SetWidthExceedPolicy( Toolkit::TextView::Split ); + titleActor.SetLineJustification( Toolkit::TextView::Center ); + + SetTitle( titleActor ); +} + +void Popup::SetTitle( Toolkit::TextView titleActor ) +{ + // Replaces the current title actor. + if( mTitle && mPopupBg ) + { + mPopupBg.Remove( mTitle ); + } + mTitle = titleActor; + + mPopupBg.Add( mTitle ); + + RelayoutRequest(); +} + +Toolkit::TextView Popup::GetTitle() const +{ + return mTitle; +} + +void Popup::AddButton( Toolkit::Button button ) +{ + mButtons.push_back( button ); + mBottomBg.Add( button ); + + RelayoutRequest(); +} + +void Popup::SetState( Toolkit::Popup::PopupState state ) +{ + SetState( state, POPUP_ANIMATION_DURATION ); +} + +void Popup::SetState( Toolkit::Popup::PopupState state, float duration ) +{ + // default animation behaviour. + HandleStateChange(state, duration); +} + +Toolkit::Popup::PopupState Popup::GetState() const +{ + return mState; +} + +void Popup::ShowTail(const Vector3& position) +{ + // Replaces the tail actor. + if(mTailImage && mTailImage.GetParent()) + { + mTailImage.GetParent().Remove( mTailImage ); + mTailImage.Reset(); + } + + std::string image = ""; + + // depending on position of tail around ParentOrigin, a different tail image is used... + if(position.y < Math::MACHINE_EPSILON_1) + { + image = mPopupStyle->tailUpImage; + } + else if(position.y > 1.0f - Math::MACHINE_EPSILON_1) + { + image = mPopupStyle->tailDownImage; + } + else if(position.x < Math::MACHINE_EPSILON_1) + { + image = mPopupStyle->tailLeftImage; + } + else if(position.x > 1.0f - Math::MACHINE_EPSILON_1) + { + image = mPopupStyle->tailRightImage; + } + + if(image != "") + { + Image tail = Image::New( image ); + mTailImage = ImageActor::New(tail); + const Vector3 anchorPoint = AnchorPoint::FRONT_BOTTOM_RIGHT - position; + + mTailImage.SetParentOrigin(position); + mTailImage.SetAnchorPoint(anchorPoint); + + mBottomBg.Add(mTailImage); + } +} + +void Popup::HideTail() +{ + ShowTail(ParentOrigin::CENTER); +} + +void Popup::SetStyle(PopupStyle& style) +{ + mPopupStyle = PopupStylePtr(&style); + // update // +} + +PopupStylePtr Popup::GetStyle() const +{ + return mPopupStyle; +} + +void Popup::SetDefaultBackgroundImage() +{ + Image bg = Image::New( mPopupStyle->backgroundImage ); + ImageActor bgImage = ImageActor::New( bg ); + bgImage.SetStyle( ImageActor::STYLE_NINE_PATCH ); + bgImage.SetNinePatchBorder( mPopupStyle->backgroundScale9Border ); + + Image buttonBg = Image::New( mPopupStyle->buttonAreaImage ); + ImageActor buttonBgImage = ImageActor::New( buttonBg ); + buttonBgImage.SetStyle( ImageActor::STYLE_NINE_PATCH ); + buttonBgImage.SetNinePatchBorder( mPopupStyle->buttonArea9PatchBorder ); + + SetBackgroundImage( bgImage ); + SetButtonAreaImage( buttonBgImage ); +} + +void Popup::CreateBacking() +{ + mBacking = Dali::Toolkit::CreateSolidColorActor( mPopupStyle->backingColor ); + + mBacking.SetPositionInheritanceMode(DONT_INHERIT_POSITION); + mBacking.SetSensitive(true); + + mLayer.Add(mBacking); + mBacking.SetOpacity(0.0f); + mBacking.SetPosition(0.0f, 0.0f, BACKING_DEPTH); + mBacking.TouchedSignal().Connect( this, &Popup::OnBackingTouched ); + mBacking.MouseWheelEventSignal().Connect(this, &Popup::OnBackingMouseWheelEvent); +} + +void Popup::CreateDialog() +{ + // Adds default background image. + SetDefaultBackgroundImage(); + + // Adds bottom background + mBottomBg = Actor::New(); + mPopupBg.Add( mBottomBg ); +} + +void Popup::HandleStateChange( Toolkit::Popup::PopupState state, float duration ) +{ + const Vector2& stageSize( Stage::GetCurrent().GetSize() ); + + Vector3 targetSize; + float targetBackingAlpha; + Vector3 targetBackingSize; + + if(mState == state) + { + return; + } + mState = state; + switch(state) + { + case Toolkit::Popup::POPUP_HIDE: + { + targetSize = Vector3(0.0f, 0.0f, 1.0f); + targetBackingAlpha = 0.0f; + targetBackingSize = Vector3(0.0f, 0.0f, 1.0f); + mShowing = false; + ClearKeyInputFocus(); + + // Retore the keyboard focus when popup is hidden + if(mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() ) + { + Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); + if( keyboardFocusManager ) + { + keyboardFocusManager.SetCurrentFocusActor(mPreviousFocusedActor); + } + } + + break; + } + + case Toolkit::Popup::POPUP_SHOW: + default: + { + targetSize = Vector3(1.0f, 1.0f, 1.0f); + targetBackingAlpha = 1.0f; + float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height; + targetBackingSize = Vector3( length, length, 1.0f ); + mShowing = true; + + // Add contents to stage for showing. + if( !mLayer.GetParent() ) + { + mAlterAddedChild = false; + Self().Add(mLayer); + mAlterAddedChild = true; + } + Self().SetSensitive(true); + SetKeyInputFocus(); + + // Handle the keyboard focus when popup is shown + Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); + if( keyboardFocusManager ) + { + mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor(); + + if( mContent && mContent.IsKeyboardFocusable() ) + { + // If content is focusable, move the focus to content + keyboardFocusManager.SetCurrentFocusActor(mContent); + } + else if( !mButtons.empty() ) + { + // Otherwise, movethe focus to the first button + keyboardFocusManager.SetCurrentFocusActor(mButtons[0]); + } + else + { + DALI_LOG_WARNING("There is no focusable in popup\n"); + } + } + break; + } + } + + mBacking.SetSize( targetBackingSize ); + + if(duration > Math::MACHINE_EPSILON_1) + { + if ( mAnimation ) + { + mAnimation.Stop(); + mAnimation.Clear(); + mAnimation.Reset(); + } + mAnimation = Animation::New(duration); + + if(mShowing) + { + mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); + mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(duration * 0.5f, duration * 0.5f) ); + } + else + { + mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); + mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); + } + mAnimation.Play(); + mAnimation.FinishedSignal().Connect(this, &Popup::OnStateAnimationFinished); + } + else + { + mBacking.SetOpacity( targetBackingAlpha ); + mPopupBg.SetScale( targetSize ); + + HandleStateChangeComplete(); + } +} + +void Popup::HandleStateChangeComplete() +{ + // Remove contents from stage if completely hidden. + if( (mState == Toolkit::Popup::POPUP_HIDE) && (mLayer.GetParent()) ) + { + Self().Remove(mLayer); + Self().SetSensitive( false ); + + // Guard against destruction during signal emission + Toolkit::Popup handle( GetOwner() ); + mHiddenSignalV2.Emit(); + } +} + +Toolkit::Popup::TouchedOutsideSignalV2& Popup::OutsideTouchedSignal() +{ + return mTouchedOutsideSignalV2; +} + +Toolkit::Popup::HiddenSignalV2& Popup::HiddenSignal() +{ + return mHiddenSignalV2; +} + +bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::Popup popup = Toolkit::Popup::DownCast(handle); + + if( Dali::Toolkit::Popup::SIGNAL_TOUCHED_OUTSIDE == signalName ) + { + popup.OutsideTouchedSignal().Connect( tracker, functor ); + } + else if( Dali::Toolkit::Popup::SIGNAL_HIDDEN == signalName ) + { + popup.HiddenSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +void Popup::OnStateAnimationFinished( Animation& source ) +{ + HandleStateChangeComplete(); +} + +bool Popup::OnBackingTouched(Actor actor, const TouchEvent& event) +{ + if(event.GetPointCount()>0) + { + const TouchPoint& point = event.GetPoint(0); + + if(point.state == TouchPoint::Down) + { + // Guard against destruction during signal emission + Toolkit::Popup handle( GetOwner() ); + + mTouchedOutsideSignalV2.Emit(); + } + } + + return true; +} + +bool Popup::OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event) +{ + // consume mouse wheel event in dimmed backing actor + return true; +} + +bool Popup::OnDialogTouched(Actor actor, const TouchEvent& event) +{ + // consume event (stops backing actor receiving touch events) + return true; +} + +void Popup::OnControlChildAdd( Actor& child ) +{ + // reparent any children added by user to the body layer. + if( mAlterAddedChild ) + { + // Removes previously added content. + if( mContent ) + { + mPopupBg.Remove( mContent ); + } + + // Reparent new content. + Self().Remove( child ); + + // keep a handle to the new content. + mContent = child; + + mPopupBg.Add( mContent ); + } +} + +void Popup::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + // Set the popup size + Vector2 popupSize; + popupSize.width = size.width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ); + popupSize.height = size.height - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ); + + // Update sizes of all popup's components. + + // Relayout background image. + // Adjust background position and size relative to parent to cater to outer Border. + // Some backgrounds are intended to over-spill. That is some content + // should appear outside the Dialog on all sides i.e. Shadows, glow effects. + const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder; + + if( mBackgroundImage ) + { + Constraint constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + BackgroundSizeConstraint(outerBorder) ); + + mBackgroundImage.RemoveConstraints(); + mBackgroundImage.ApplyConstraint( constraint ); + + mBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mBackgroundImage.SetPosition( -outerBorder.x, -outerBorder.y, 0.0f ); + } + + if( mPopupBg && mButtonAreaImage ) + { + // If there are no buttons, button background is also removed. + if ( mButtons.size() == 0 ) + { + mPopupBg.Remove( mButtonAreaImage ); + } + else + { + Constraint constraint = Constraint::New( Actor::SIZE, + ParentSource( Actor::SIZE ), + ButtonAreaSizeConstraint(outerBorder) ); + + mButtonAreaImage.RemoveConstraints(); + mButtonAreaImage.ApplyConstraint( constraint ); + + mButtonAreaImage.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + mButtonAreaImage.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + mButtonAreaImage.SetY( -outerBorder.z - POPUP_OUT_MARGIN_HEIGHT ); + + mPopupBg.Add( mButtonAreaImage ); + } + } + + // Relayout title + Vector3 positionOffset( 0.0f, mPopupStyle->margin + POPUP_OUT_MARGIN_WIDTH, CONTENT_DEPTH ); + if( mTitle ) + { + Vector2 titleSize; + titleSize.width = popupSize.width; + titleSize.height = mTitle.GetHeightForWidth( titleSize.width ); + + // As the default size policy for text-view is Fixed & Fixed, a size needs to be set. + // Otherwise size-negotiation algorithm uses the GetNaturalSize() with doesn't take + // into account the multiline and exceed policies, giving as result a wrong size. + mTitle.SetSize( titleSize ); + Relayout( mTitle, titleSize, container ); + + mTitle.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + mTitle.SetParentOrigin( ParentOrigin::TOP_CENTER ); + mTitle.SetPosition( positionOffset ); + + positionOffset.y += titleSize.height + mPopupStyle->margin; + } + + // Relayout content + if( mContent ) + { + Vector2 contentSize; + contentSize.width = popupSize.width; + + Toolkit::Control control = Toolkit::Control::DownCast( mContent ); + if( control ) + { + contentSize.height = control.GetHeightForWidth( contentSize.width ); + } + else + { + contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, contentSize.width ); + } + + mContent.SetSize( contentSize ); + Relayout( mContent, contentSize, container ); + + mContent.SetParentOrigin(ParentOrigin::TOP_CENTER); + mContent.SetAnchorPoint(AnchorPoint::TOP_CENTER); + + mContent.SetPosition( positionOffset ); + + positionOffset.y += contentSize.height + mPopupStyle->margin; + } + + // Relayout Button Area + if( mBottomBg ) + { + mBottomBg.SetSize( popupSize.width, mPopupStyle->bottomSize.height ); + + mBottomBg.SetParentOrigin(ParentOrigin::TOP_CENTER); + mBottomBg.SetAnchorPoint(AnchorPoint::TOP_CENTER); + + mBottomBg.SetPosition( positionOffset ); + } + + // Relayout All buttons + if ( !mButtons.empty() ) + { + // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well. + Vector2 buttonSize( ( ( popupSize.width - mPopupStyle->buttonSpacing * ( mButtons.size() - 1 ) ) / mButtons.size() ), + mPopupStyle->bottomSize.height - mPopupStyle->margin ); + + Vector3 buttonPosition; + + for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end(); + iter != endIter; + ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width ) + { + iter->SetPosition( buttonPosition ); + + // If there is only one button, it needs to be laid out on center. + if ( mButtons.size() == 1 ) + { + iter->SetAnchorPoint( AnchorPoint::CENTER ); + iter->SetParentOrigin( ParentOrigin::CENTER ); + } + else + { + iter->SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + iter->SetParentOrigin( ParentOrigin::CENTER_LEFT ); + } + + Relayout( *iter, buttonSize, container ); + } + } + + if( mShowing && mBacking ) + { + Vector2 stageSize = Stage::GetCurrent().GetSize(); + float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height; + Vector3 targetBackingSize = Vector3( length, length, 1.0f ); + + mBacking.SetSize( targetBackingSize ); + } +} + +bool Popup::OnKeyEvent(const KeyEvent& event) +{ + bool consumed = false; + + if(event.state == KeyEvent::Down) + { + if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK) + { + SetState(Toolkit::Popup::POPUP_HIDE); + consumed = true; + } + } + + return consumed; +} + +Vector3 Popup::GetNaturalSize() +{ + Vector3 naturalSize; + + if ( mTitle ) + { + naturalSize += mTitle.GetImplementation().GetNaturalSize(); + naturalSize.height += mPopupStyle->margin; + } + + if( mContent ) + { + Vector3 contentSize; + + Toolkit::Control control = Toolkit::Control::DownCast( mContent ); + if( control ) + { + contentSize = control.GetImplementation().GetNaturalSize(); + } + else + { + contentSize = RelayoutHelper::GetNaturalSize( mContent ); + } + + naturalSize.width = std::max( naturalSize.width, contentSize.width ); + naturalSize.height += contentSize.height + mPopupStyle->margin; + } + + if( !mButtons.empty() ) + { + naturalSize.height += mPopupStyle->bottomSize.height; + } + + // Add the margins + float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) ); + naturalSize.width += margin; + naturalSize.height += margin; + + return naturalSize; +} + +float Popup::GetHeightForWidth( float width ) +{ + float height( 0.0f ); + float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) ); + + if ( mTitle ) + { + height += mTitle.GetImplementation().GetHeightForWidth( popupWidth ); + height += mPopupStyle->margin; + } + + if( mContent ) + { + float contentHeight; + + Toolkit::Control control = Toolkit::Control::DownCast( mContent ); + if( control ) + { + contentHeight = control.GetImplementation().GetHeightForWidth( popupWidth ); + } + else + { + contentHeight = RelayoutHelper::GetHeightForWidth( mContent, popupWidth ); + } + + height += contentHeight + mPopupStyle->margin; + } + + if( !mButtons.empty() ) + { + height += mPopupStyle->bottomSize.height; + } + + // Add the margins + float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) ); + height += margin; + + return height; +} + +float Popup::GetWidthForHeight( float height ) +{ + return GetNaturalSize().width; +} + +Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + Actor nextFocusableActor( currentFocusedActor ); + + // TODO: Needs to be optimised + + if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) ) + { + // The current focused actor is not within popup + if( mContent && mContent.IsKeyboardFocusable() ) + { + // If content is focusable, move the focus to content + nextFocusableActor = mContent; + } + else if( !mButtons.empty() ) + { + // Otherwise, movethe focus to the first button + nextFocusableActor = mButtons[0]; + } + } + else + { + // Rebuild the focus chain because button or content can be added or removed dynamically + ActorContainer focusableActors; + if( mContent && mContent.IsKeyboardFocusable() ) + { + focusableActors.push_back(mContent); + } + + for(unsigned int i = 0; i < mButtons.size(); i++) + { + if( mButtons[i] && mButtons[i].IsKeyboardFocusable() ) + { + focusableActors.push_back(mButtons[i]); + } + } + + for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter ) + { + if ( currentFocusedActor == *iter ) + { + switch ( direction ) + { + case Control::Left: + { + if ( iter == focusableActors.begin() ) + { + nextFocusableActor = *( focusableActors.end() - 1 ); + } + else + { + nextFocusableActor = *( iter - 1 ); + } + break; + } + case Control::Right: + { + if ( iter == focusableActors.end() - 1 ) + { + nextFocusableActor = *( focusableActors.begin() ); + } + else + { + nextFocusableActor = *( iter + 1 ); + } + break; + } + + case Control::Up: + { + if ( *iter == mContent ) + { + nextFocusableActor = *( focusableActors.end() - 1 ); + } + else + { + if ( mContent && mContent.IsKeyboardFocusable() ) + { + nextFocusableActor = mContent; + } + else + { + if ( iter == focusableActors.begin() ) + { + nextFocusableActor = *( focusableActors.end() - 1 ); + } + else + { + nextFocusableActor = *( iter - 1 ); + } + } + } + break; + } + + case Control::Down: + { + if ( mContent && mContent.IsKeyboardFocusable() ) + { + nextFocusableActor = mContent; + } + else + { + if ( iter == focusableActors.end() - 1 ) + { + nextFocusableActor = *( focusableActors.begin() ); + } + else + { + nextFocusableActor = *( iter + 1 ); + } + } + + if ( *iter == mContent && !mButtons.empty() ) + { + nextFocusableActor = mButtons[0]; + } + break; + } + } + + if(!nextFocusableActor) + { + DALI_LOG_WARNING("Can not decide next focusable actor\n"); + } + + break; + } + } + } + + return nextFocusableActor; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/popup/popup-impl.h b/dali-toolkit/internal/controls/popup/popup-impl.h new file mode 100755 index 0000000..747df4c --- /dev/null +++ b/dali-toolkit/internal/controls/popup/popup-impl.h @@ -0,0 +1,354 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_POPUP_H__ +#define __DALI_TOOLKIT_INTERNAL_POPUP_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Button; + +namespace Internal +{ + +class Popup; +class PopupStyle; + +typedef IntrusivePtr PopupPtr; +typedef IntrusivePtr PopupStylePtr; + +/** + * @copydoc Toolkit::Popup + */ +class Popup : public ControlImpl +{ +public: + + /** + * Create a new Popup. + * @return A public handle to the newly allocated Popup. + */ + static Dali::Toolkit::Popup New(); + +public: + + /** + * Returns number of buttons added to Popup + * + * @return Number of buttons + */ + size_t GetButtonCount() const; + + /** + * @copydoc Toolkit::Popup::SetBackgroundImage + */ + void SetBackgroundImage( Actor image ); + + /** + * @copydoc Toolkit::Popup::SetButtonAreaImage + */ + void SetButtonAreaImage( Actor image ); + + /** + * @copydoc Toolkit::Popup::SetTitle( const std::string& text ); + */ + void SetTitle( const std::string& text ); + + /** + * @copydoc Toolkit::Popup::SetTitle( TextView titleActor ) + */ + void SetTitle( Toolkit::TextView titleActor ); + + /** + * @copydoc Toolkit::Popup::GetTitle + */ + Toolkit::TextView GetTitle() const; + + /** + * @copydoc Toolkit::Popup::AddButton + */ + void AddButton( Toolkit::Button button ); + + /** + * @copydoc Toolkit::Popup::SetState( PopupState state ) + */ + void SetState( Toolkit::Popup::PopupState state ); + + /** + * @copydoc Toolkit::Popup::SetState( PopupState state, float duration ) + */ + void SetState( Toolkit::Popup::PopupState state, float duration ); + + /** + * @copydoc Toolkit::Popup::GetState( ) + */ + Toolkit::Popup::PopupState GetState() const; + + /** + * @copydoc Toolkit::Popup::ShowTail + */ + void ShowTail(const Vector3& position); + + /** + * @copydoc Toolkit::Popup::HideTail + */ + void HideTail(); + + + /** + * Sets the style of the popup + * @param[in] style The style of the popup + */ + void SetStyle(PopupStyle& style); + + /** + * Gets the style of the popup + * @return style of the popup + */ + PopupStylePtr GetStyle() const; + +protected: + + /** + * Construct a new Popup. + * @param[in] style of the popup + */ + Popup(PopupStyle& style); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Popup(); + +private: + + /** + * Creates and applies the default background image. + */ + void SetDefaultBackgroundImage(); + + /** + * Create Dim Backing + * (covers all content behind the dialog) + */ + void CreateBacking(); + + /** + * Create Dialog + * (dialog content resides inside this - buttons, title etc.) + */ + void CreateDialog(); + + /** + * Animate Popup by scaling uniformally from 0 to 100% and vice versa (default behaviour) + * @param[in] state The desired state to change into. + * @param[in] duration The time for this animation to take. + */ + void HandleStateChange( Toolkit::Popup::PopupState state, float duration ); + + /** + * Invoked once StateChange has completed. + */ + void HandleStateChangeComplete(); + +public: // Signals + + /** + * @copydoc Dali::Toolkit::Popup::OutsideTouchedSignal() + */ + Toolkit::Popup::TouchedOutsideSignalV2& OutsideTouchedSignal(); + + /** + * @copydoc Dali::Toolkit::Popup::HiddenSignal() + */ + Toolkit::Popup::HiddenSignalV2& HiddenSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +private: + + /** + * Signal occurs when the State animation (transition from hide<->show) finishes + * @param[in] source The animation that just finished. + */ + void OnStateAnimationFinished( Animation& source ); + + /** + * Signal occurs when the dimmed backing for the Popup is touched. + * @param[in] actor The Actor Touched + * @param[in] event The Touch Event. + * @return Whether to consume event or not. + */ + bool OnBackingTouched(Actor actor, const TouchEvent& event); + + /** + * Signal occurs when the mouse wheel event is occured on dimmed backing for the Popup. + * @param[in] actor The Actor got mouse wheel + * @param[in] event The Mouse Wheel Event. + * @return Whether to consume event or not. + */ + bool OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event); + + /** + * Signal occurs when the dialog has been touched. + * @param[in] actor The Actor Touched + * @param[in] event The Touch Event. + * @return Whether to consume event or not. + */ + bool OnDialogTouched(Actor actor, const TouchEvent& event); + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + virtual void OnPropertySet( Property::Index index, Property::Value propertyValue ); + + /** + * From ControlImpl; called after a child has been added to the owning actor. + * @param[in] child The child which has been added. + */ + virtual void OnControlChildAdd( Actor& child ); + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut() + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + + /** + * @copydoc Toolkit::ControlImpl::OnKeyEvent() + */ + virtual bool OnKeyEvent(const KeyEvent& event); + + /** + * @copydoc Control::GetNaturalSize() + */ + virtual Vector3 GetNaturalSize(); + + /** + * @copydoc Control::GetHeightForWidth() + */ + float GetHeightForWidth( float width ); + + /** + * @copydoc Control::GetWidthForHeight() + */ + float GetWidthForHeight( float height ); + + /** + * @copydoc Control::GetNextKeyboardFocusableActor() + */ + Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + +private: + + // Undefined + Popup(const Popup&); + + // Undefined + Popup& operator=(const Popup& rhs); + +private: + + struct LayoutInfo + { + Vector3 mTitleSize; + Vector3 mContentSize; + Vector3 mButtonBgSize; + std::vector mButtonSize; + }; + +private: + + bool mShowing; ///< Popup is showing or not + + Layer mLayer; ///< Popup Layer (i.e. Dim backing and PopupBg reside in this) + Actor mPopupBg; ///< Popup Background (i.e. dialog reside in this) + ImageActor mBacking; ///< Backing actor (dim effect) + + Actor mPreviousFocusedActor; ///< Store the previous focused actor to restore the focus when popup hide + + Actor mBackgroundImage; ///< Stores the background image. + Actor mButtonAreaImage; ///< Stores the button background image. + Toolkit::TextView mTitle; ///< Stores the text title. + Actor mContent; ///< Stores popup's content. + Actor mBottomBg; ///< bottom button bar background. ImageActor is replaced with Actor due to hidden image. + Actor mTailImage; ///< Stores the tail image + + ActorContainer mButtons; ///< Keeps track of the buttons added to this popup. + Toolkit::Popup::PopupState mState; ///< Popup current state. + Animation mAnimation; ///< The animation instance managing state changing. + bool mAlterAddedChild; ///< Flag used to control whether children are reparented or not. + PopupStylePtr mPopupStyle; ///< The style to be used for this popup. + + LayoutInfo mLayoutInfo; ///< Stores sizes of all popup components. + + Toolkit::Popup::TouchedOutsideSignalV2 mTouchedOutsideSignalV2; + Toolkit::Popup::HiddenSignalV2 mHiddenSignalV2; + + Property::Index mPropertyTitle; ///< Property index for Title. + Property::Index mPropertyState; ///< Property index for popup state. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Popup& GetImpl(Toolkit::Popup& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::Popup& GetImpl(const Toolkit::Popup& pub) +{ + DALI_ASSERT_ALWAYS(pub); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_POPUP_H__ diff --git a/dali-toolkit/internal/controls/popup/popup-style-impl.cpp b/dali-toolkit/internal/controls/popup/popup-style-impl.cpp new file mode 100644 index 0000000..e6ef2c9 --- /dev/null +++ b/dali-toolkit/internal/controls/popup/popup-style-impl.cpp @@ -0,0 +1,114 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ +// Popup style default +const Vector4 DEFAULT_BACKING_COLOR = Vector4(0.0f, 0.0f, 0.0f, 0.5f); +const float DEFAULT_MARGIN = 20.0f; //From Tizen GUI UX +const float DEFAULT_BUTTON_SPACING = 20.0f; //From Tizen GUI UX +const Vector3 DEFAULT_BUTTON_SIZE(275.0f, 74.0f, 0.0f); +const char* DEFAULT_BACKGROUND_IMAGE_PATH = DALI_IMAGE_DIR "00_popup_bg.png"; +const char* DEFAULT_BUTTON_AREA_IMAGE_PATH = DALI_IMAGE_DIR "00_popup_button_bg.png"; +const char* DEFAULT_TAIL_UP_IMAGE_PATH = DALI_IMAGE_DIR "popup_tail_up.png"; +const char* DEFAULT_TAIL_DOWN_IMAGE_PATH = DALI_IMAGE_DIR "popup_tail_down.png"; +const char* DEFAULT_TAIL_LEFT_IMAGE_PATH = DALI_IMAGE_DIR "popup_tail_left.png"; +const char* DEFAULT_TAIL_RIGHT_IMAGE_PATH = DALI_IMAGE_DIR "popup_tail_right.png"; +const Vector3 DEFAULT_BOTTOM_SIZE(620.0f,96.0f,0.0f); +const Vector2 DEFAULT_BACKGROUND_SIZE(620.0f, 236.0f); +const Vector4 DEFAULT_BACKGROUND_STYLE_9_BORDER( 25.0f, 25.0f, 26.0f, 50.0f ); +const Vector4 DEFAULT_BACKGROUND_OUTER_BORDER( 0.0f, 0.0f, 0.0f, 0.0f ); +const Vector4 DEFAULT_BUTTON_AREA_9_PATCH_BORDER( 13.0f, 8.0f, 13.0f, 8.0f ); +} + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/////////////////////////////////////////////////////////////////////////////// +// Popup Style (base class) +/////////////////////////////////////////////////////////////////////////////// + +PopupStyle::PopupStyle() +: backingColor(), + backgroundImage(), + buttonAreaImage(), + backgroundSize(), + backgroundScale9Border(), + backgroundOuterBorder(), + buttonArea9PatchBorder(), + margin(0.0f), + buttonSpacing(0.0f), + buttonSize(), + tailUpImage(), + tailDownImage(), + tailLeftImage(), + tailRightImage() +{ +} + +PopupStyle::~PopupStyle() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// Popup style: Default +/////////////////////////////////////////////////////////////////////////////// + +PopupStyleDefault::PopupStyleDefault() +{ + backingColor = DEFAULT_BACKING_COLOR; + backgroundImage = DEFAULT_BACKGROUND_IMAGE_PATH; + buttonAreaImage = DEFAULT_BUTTON_AREA_IMAGE_PATH; + margin = DEFAULT_MARGIN; + buttonSpacing = DEFAULT_BUTTON_SPACING; + buttonSize = DEFAULT_BUTTON_SIZE; + tailUpImage = DEFAULT_TAIL_UP_IMAGE_PATH; + tailDownImage = DEFAULT_TAIL_DOWN_IMAGE_PATH; + tailLeftImage = DEFAULT_TAIL_LEFT_IMAGE_PATH; + tailRightImage = DEFAULT_TAIL_RIGHT_IMAGE_PATH; + backgroundSize = DEFAULT_BACKGROUND_SIZE; + backgroundScale9Border = DEFAULT_BACKGROUND_STYLE_9_BORDER; + backgroundOuterBorder = DEFAULT_BACKGROUND_OUTER_BORDER; + buttonArea9PatchBorder = DEFAULT_BUTTON_AREA_9_PATCH_BORDER; + bottomSize = DEFAULT_BOTTOM_SIZE; +} + +PopupStyleDefaultPtr PopupStyleDefault::New() +{ + return PopupStyleDefaultPtr(new PopupStyleDefault()); +} + +PopupStyleDefault::~PopupStyleDefault() +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/popup/popup-style-impl.h b/dali-toolkit/internal/controls/popup/popup-style-impl.h new file mode 100644 index 0000000..80b65bc --- /dev/null +++ b/dali-toolkit/internal/controls/popup/popup-style-impl.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_POPUP_STYLE_H__ +#define __DALI_TOOLKIT_INTERNAL_POPUP_STYLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class PopupStyle; + +typedef IntrusivePtr PopupStylePtr; + +/** + * A PopupStyle describes the images, positions, sizes, and various other metrics + * which define how the popup should look. + */ +class PopupStyle : public RefObject +{ +public: + + /** + * Virtual destructor. + */ + virtual ~PopupStyle(); + +public: + + Vector4 backingColor; ///< Color of backing layer (covers entire screen) + std::string backgroundImage; ///< Background image path + std::string buttonAreaImage; ///< This image is for the back ground area common for all the buttons in the popup + Vector2 backgroundSize; ///< Background image size. + Vector4 backgroundScale9Border; ///< Background scale-9 border settings. + Vector4 backgroundOuterBorder; ///< Background outer border settings. + Vector4 buttonArea9PatchBorder; ///< 9 patch border constants for buttonAreaImage + float margin; ///< Margin for all contents (body, title, button) + float buttonSpacing; ///< Horizontal spacing for buttons. + Vector3 buttonSize; ///< Size of Buttons. + Vector3 bottomSize; ///< size of bottom button bar. + std::string bottomBackgroundImage; ///< bottom background image path. + std::string tailUpImage; ///< Tail Up-side image path. + std::string tailDownImage; ///< Tail Down-side image path. + std::string tailLeftImage; ///< Tail Left-side image path. + std::string tailRightImage; ///< Tail Right-side image path. + +protected: + + /** + * Create a new PopupStyle; Only derived versions are instantiable. + */ + PopupStyle(); +}; + +class PopupStyleDefault; + +typedef IntrusivePtr PopupStyleDefaultPtr; + +/** + * This is the default popup style. + */ +class PopupStyleDefault : public PopupStyle +{ +public: + + /** + * Create a new PopupStyle + */ + static PopupStyleDefaultPtr New(); + + /** + * Virtual destructor. + */ + virtual ~PopupStyleDefault(); + +private: + +protected: + + /** + * Protected constructor; see also PopupStyleDefault::New() + */ + PopupStyleDefault(); +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_POPUP_STYLE_H__ diff --git a/dali-toolkit/internal/controls/relayout-controller-impl.cpp b/dali-toolkit/internal/controls/relayout-controller-impl.cpp new file mode 100644 index 0000000..bdefe39 --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-controller-impl.cpp @@ -0,0 +1,233 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "relayout-controller-impl.h" + +// EXTERNAL INCLUDES + +#include +#include +#include + +// INTERNAL INCLUDES + +#include "dali-toolkit/public-api/controls/control.h" +#include "dali-toolkit/public-api/controls/control-impl.h" +#include "dali-toolkit/public-api/controls/text-view/text-view.h" + +namespace Dali +{ + +namespace Toolkit +{ + +typedef std::pair< Control, Vector2 > ControlSizePair; +typedef std::stack< ControlSizePair > ControlStack; + +namespace Internal +{ + +namespace +{ +#if defined(DEBUG_ENABLED) + +Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER") ); + +/** + * Prints out all the children of the given actor when debug is enabled. + * + * @param[in] actor The actor whose children to print. + * @param[in] level The number of " | " to put in front of the children. + */ +void PrintChildren( Actor actor, int level ) +{ + std::ostringstream output; + + for ( int t = 0; t < level; ++t ) + { + output << " | "; + } + + output << actor.GetTypeName(); + + output << " - Pos: " << actor.GetCurrentPosition() << " Size: " << actor.GetCurrentSize() << ","; + + output << " (" << actor.GetObjectPtr() << ")" << std::endl; + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, output.str().c_str() ); + + ++level; + unsigned int numChildren = actor.GetChildCount(); + for( unsigned int i=0; i0; --i ) + { + FindControls( actor.GetChildAt(i-1), controls, size ); + } + } +} + +/** + * Pushes the controls in the container, to the stack. + * + * @param[in,out] controlStack The stack to push controls to. + * @param[in] container The container to push controls from. + */ +void PushToStack( ControlStack& controlStack, const ActorSizeContainer& container ) +{ + for ( ActorSizeContainer::const_reverse_iterator iter = container.rbegin(), endIter = container.rend(); iter != endIter; ++iter ) + { + FindControls( iter->first, controlStack, iter->second ); + } +} + +} // unnamed namespace + +RelayoutControllerImpl::~RelayoutControllerImpl() +{ +} + +void RelayoutControllerImpl::Request() +{ + //TODO use Relayout Request to set up logic to optimize relayout of the actors/controls in the scene + + if( !mRelayoutConnection ) + { + Stage stage = Stage::GetCurrent(); + stage.EventProcessingFinishedSignal().Connect( this, &RelayoutControllerImpl::Relayout ); + mRelayoutConnection = true; + } +} + +void RelayoutControllerImpl::Relayout() +{ + PRINT_HIERARCHY; + + // 1. Finds all top-level controls from the root actor and allocate them the size of the stage + // These controls are paired with the stage size and added to the stack. + ControlStack controlStack; + FindControls( Stage().GetCurrent().GetRootLayer(), controlStack, Stage::GetCurrent().GetSize() ); + + // 2. Iterate through the stack until it's empty. + while ( !controlStack.empty() ) + { + ControlSizePair pair ( controlStack.top() ); + Control control ( pair.first ); + Vector2 size ( pair.second ); + controlStack.pop(); + + DALI_LOG_INFO( gLogFilter, Debug::General, "Allocating %p (%.2f, %.2f)\n", control.GetObjectPtr(), size.width, size.height ); + + // 3. Negotiate the size with the current control. Pass it an empty container which the control + // has to fill with all the actors it has not done any size negotiation for. + ActorSizeContainer container; + control.GetImplementation().NegotiateSize( size, container ); + + // 4. Push the controls from the actors in the container to the stack. + PushToStack( controlStack, container ); + } + + //Disconnect so that we relayout only when requested to do so. + Disconnect(); +} + +void RelayoutControllerImpl::Disconnect() +{ + if( mRelayoutConnection ) + { + Stage stage = Stage::GetCurrent(); + stage.EventProcessingFinishedSignal().Disconnect( this, &RelayoutControllerImpl::Relayout ); + mRelayoutConnection = false; + } +} + +RelayoutControllerImpl::RelayoutControllerImpl() +: mRelayoutConnection( false ) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/relayout-controller-impl.h b/dali-toolkit/internal/controls/relayout-controller-impl.h new file mode 100644 index 0000000..0ffed08 --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-controller-impl.h @@ -0,0 +1,109 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_IMPL_H__ +#define __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_IMPL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include "relayout-controller.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class RelayoutController; + +/** + * @copydoc Toolkit::Internal::RelayoutController + */ +class RelayoutControllerImpl : public Dali::BaseObject, public ConnectionTracker +{ +public: + + /** + * Constructor. + * We should only create a unique instance. + */ + RelayoutControllerImpl(); + + + /** + * Destructor + */ + virtual ~RelayoutControllerImpl(); + + /** + * Request for relayout. + */ + void Request(); + +private: + + /** + * Relayouts controls inside actor tree from bottom to top. + */ + void Relayout(); + + /** + * Resets the relayout controller. + */ + void Reset(); + + /** + * Disconnect the Relayout() method from the Stage::EventProcessingFinishedSignal(). + */ + void Disconnect(); + + // Undefined + RelayoutControllerImpl(const RelayoutControllerImpl&); + RelayoutControllerImpl& operator=(const RelayoutControllerImpl&); + +private: + + bool mRelayoutConnection:1; ///< Whether EventProcessingFinishedSignal signal is connected. +}; + +} // namespace Internal + + +inline Internal::RelayoutControllerImpl& GetImpl(Dali::Toolkit::Internal::RelayoutController& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::RelayoutControllerImpl& GetImpl(const Dali::Toolkit::Internal::RelayoutController& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_IMPL_H__ diff --git a/dali-toolkit/internal/controls/relayout-controller.cpp b/dali-toolkit/internal/controls/relayout-controller.cpp new file mode 100644 index 0000000..834ab27 --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-controller.cpp @@ -0,0 +1,94 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "relayout-controller.h" +#include "relayout-controller-impl.h" + +// EXTERNAL INCLUDES + +#include +#include +#include +#include + +// INTERNAL INCLUDES + +#include "dali-toolkit/public-api/controls/control.h" +#include "dali-toolkit/public-api/controls/control-impl.h" +#include "dali-toolkit/public-api/controls/text-view/text-view.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +RelayoutController::RelayoutController() +{ + +} + +RelayoutController::~RelayoutController() +{ + +} + +RelayoutController RelayoutController::Get() +{ + RelayoutController controller; + + // Check whether the RelayoutController is already created + Dali::Adaptor& adaptor = Dali::Adaptor::Get(); + Dali::BaseHandle handle = adaptor.GetSingleton(typeid(RelayoutController)); + + if(handle) + { + // If so, downcast the handle of singleton to RelayoutController + controller = RelayoutController(dynamic_cast(handle.GetObjectPtr())); + } + + if(!controller) + { + // If not, create the RelayoutController and register it as a singleton + controller = RelayoutController(new Internal::RelayoutControllerImpl()); + adaptor.RegisterSingleton(typeid(controller), controller); + } + + return controller; +} + +RelayoutController::RelayoutController(Internal::RelayoutControllerImpl *impl) + : BaseHandle(impl) +{ +} + +void RelayoutController::Request() +{ + GetImpl(*this).Request(); +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/internal/controls/relayout-controller.h b/dali-toolkit/internal/controls/relayout-controller.h new file mode 100644 index 0000000..dd7abca --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-controller.h @@ -0,0 +1,74 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_H__ +#define __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class RelayoutControllerImpl; + +/** + * RelayoutController + * This singleton class provides functionality to trigger relayouting of toolkit controls in the dali scene graph. + */ +class RelayoutController : public Dali::BaseHandle +{ +public: + + /** + * Constructor. + * We should only create a unique instance. + */ + RelayoutController(); + + /** + * Virtual destructor. + */ + virtual ~RelayoutController(); + + /** + * Get the singleton of RelayoutController object. + * @return A handle to the RelayoutController. + */ + static RelayoutController Get(); + + /** + * Request to relayout. + */ + void Request(); + +private: + + RelayoutController(Internal::RelayoutControllerImpl *impl); +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_RELAYOUT_CONTROLLER_H__ diff --git a/dali-toolkit/internal/controls/relayout-helper.cpp b/dali-toolkit/internal/controls/relayout-helper.cpp new file mode 100644 index 0000000..b5f1fc9 --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-helper.cpp @@ -0,0 +1,95 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "relayout-helper.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace RelayoutHelper +{ + +Vector3 GetNaturalSize( Actor actor ) +{ + Vector3 size = actor.GetCurrentSize(); + const float depth = size.depth; + + // Get natural size for TextActor. + TextActor textActor = TextActor::DownCast( actor ); + if( textActor ) + { + Font font = textActor.GetFont(); + if( !font ) + { + font = Font::New(); + } + size = font.MeasureText( textActor.GetText() ); + size.depth = depth; + } + + // Get natural size for ImageActor. + // TODO: currently it doesn't work as expected. + ImageActor imageActor = ImageActor::DownCast( actor ); + if( ( imageActor ) && ( imageActor.GetImage() ) ) + { + Image image = imageActor.GetImage(); + size = Vector3( static_cast( image.GetWidth() ), static_cast( image.GetHeight() ), depth ); + } + + return size; +} + +float GetHeightForWidth( Actor actor, float width ) +{ + Vector3 size = actor.GetCurrentSize(); + float height = 0.f; + + TextActor textActor = TextActor::DownCast( actor ); + if( textActor ) + { + Font font = textActor.GetFont(); + if( !font ) + { + font = Font::New(); + } + size = font.MeasureText( textActor.GetText() ); + } + + ImageActor imageActor = ImageActor::DownCast( actor ); + if( ( imageActor ) && ( imageActor.GetImage() ) ) + { + Image image = imageActor.GetImage(); + size = Vector3( static_cast( image.GetWidth() ), static_cast( image.GetHeight() ), 0.f ); + } + + height = size.height / ( size.width / width ); + + return height; +} + +} // namespace RelayoutHelper + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/relayout-helper.h b/dali-toolkit/internal/controls/relayout-helper.h new file mode 100644 index 0000000..1f61406 --- /dev/null +++ b/dali-toolkit/internal/controls/relayout-helper.h @@ -0,0 +1,54 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_RELAYOUT_HELPER_H__ +#define __DALI_TOOLKIT_INTERNAL_RELAYOUT_HELPER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace RelayoutHelper +{ + +/** + * + */ +Vector3 GetNaturalSize( Actor actor ); + +/** + * + */ +float GetHeightForWidth( Actor actor, float width ); + +} // namespace RelayoutHelper + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_RELAYOUT_HELPER_H__ diff --git a/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.cpp b/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.cpp new file mode 100755 index 0000000..ca69446 --- /dev/null +++ b/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.cpp @@ -0,0 +1,615 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace +{ +/** + * Squares input value + * i.e. y = x*x + * @param[in] x Input value to be squared + * @return Result (x*x) + */ +template +inline T Square(T x) +{ + return x*x; +} + +const char* BAR_TAB_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png"; +const Vector4 BAR_TAB_NINE_PATCH_BORDER(0.0f, 12.0f, 14.0f, 14.0f); +const Vector3 BAR_TAB_SIZE(18.0f, 72.0f, 0.0f); +const Vector3 BAR_TAB_OFFSET_V(-18.0f, 0.0f, 0.1f); +const Vector3 BAR_TAB_OFFSET_H(0.0f, -18.0f, 0.1f); +const float BAR_CONTRACT_DELAY(0.8f); +const float BAR_SHOW_TIME(0.4f); +const float BAR_HIDE_TIME(0.5f); +const int SECOND_UNIT(1000); + +/** + * ScrollBar Visibility Constraint + * Returns whether scroll bar is visible + */ +bool ScrollBarVisibilityConstraint(const bool& current, + const PropertyInput& canScrollProperty) +{ + bool canScroll = canScrollProperty.GetBoolean(); + return canScroll; +} + +/** + * ScrollBar Size Constraint + * Resize ScrollBar Size depends on both ScrollSize and DomainSize + */ +struct ScrollBarSizeConstraint +{ + /** + * @param[in] vertical Whether this constraint controls a vertical scrollbar (true) + * or a horizontal one (false) + */ + ScrollBarSizeConstraint(bool vertical) + : mVertical(vertical) + { + } + + /** + * Constraint operator + * @param[in] current The current ScrollBar size + * @param[in] scrollMinProperty The container's minimum position. + * @param[in] scrollMaxProperty The container's maximum position. + * @param[in] scrollDirectionProperty The container's scroll direction. + * @param[in] scrollSizeProperty The container's size of viewport. + * @return The new ScrollBar position is returned. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& scrollMinProperty, + const PropertyInput& scrollMaxProperty, + const PropertyInput& scrollDirectionProperty, + const PropertyInput& scrollSizeProperty) + { + const Vector3& min = scrollMinProperty.GetVector3(); + const Vector3& max = scrollMaxProperty.GetVector3(); + const Vector3& scrollDirection = scrollDirectionProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& orientation = static_cast(scrollDirection.z); + const Vector3& size = scrollSizeProperty.GetVector3(); + const Vector3 domainSize = max - min; + + if (mVertical && Toolkit::IsVertical(orientation)) + { + float mod = fabsf(domainSize.height) > size.height ? size.height * ( size.height / fabsf(domainSize.height) ) : size.height * ( (size.height - fabsf(domainSize.height * 0.5f)) / size.height); + return Vector3( current.width, mod, current.depth ); + } + else + { + float mod = fabsf(domainSize.height) > size.width ? size.width * ( size.width / fabsf(domainSize.height) ) : size.width * ( (size.width - fabsf(domainSize.height * 0.5f)) / size.width); + return Vector3( current.width, mod, current.depth ); + } + } + + bool mVertical; ///< Whether vertical or horizontal +}; + +/** + * ScrollBar rotation Constraint + * Rotate ScrollBar depends on the scroll direction + */ +struct ScrollBarRotationConstraint +{ + /** + * @param[in] vertical Whether this constraint controls a vertical scrollbar (true) + * or a horizontal one (false) + */ + ScrollBarRotationConstraint(bool vertical) + : mVertical(vertical) + { + } + + /** + * Constraint operator + * @param[in] current The current ScrollBar rotation + * @param[in] scrollDirectionProperty The container's scroll direction. + * @return The new ScrollBar rotation is returned. + */ + Quaternion operator()(const Quaternion& current, + const PropertyInput& scrollDirectionProperty) + { + const Vector3& scrollDirection = scrollDirectionProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& orientation = static_cast(scrollDirection.z); + + if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) ) + { + return Quaternion(0.0f, Vector3::ZAXIS); + } + else + { + return Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } + } + + bool mVertical; ///< Whether vertical or horizontal +}; + +/** + * ScrollBar Position Constraint + * Positions the scroll bar to reflect the current scroll position + * within the domain. + */ +struct ScrollBarPositionConstraint +{ + /** + * @param[in] vertical Whether this constraint controls a vertical scrollbar (true) + * or a horizontal one (false) + * @param[in] wrap Whether to base scrollbar on original position or wrapped position + */ + ScrollBarPositionConstraint(bool vertical, bool wrap = false) + : mVertical(vertical), + mWrap(wrap) + { + } + + /** + * Constraint operator + * @param[in] current The current ScrollBar position + * @param[in] scrollBarSizeProperty ScrollBar size + * @param[in] scrollRelativePositionProperty The container's relative position (from 0.0 -> 1.0 in each axis) + * @param[in] scrollMinProperty The container's minimum position. + * @param[in] scrollMaxProperty The container's maximum position. + * @param[in] scrollDirectionProperty The container's scroll direction. + * @param[in] scrollSizeProperty The container's size of viewport. + * @return The new ScrollBar position is returned. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& scrollBarSizeProperty, + const PropertyInput& scrollRelativePositionProperty, + const PropertyInput& scrollMinProperty, + const PropertyInput& scrollMaxProperty, + const PropertyInput& scrollDirectionProperty, + const PropertyInput& scrollSizeProperty) + { + Vector3 barSize = scrollBarSizeProperty.GetVector3(); + Vector3 relativePosition = scrollRelativePositionProperty.GetVector3(); + Vector3 size = scrollSizeProperty.GetVector3(); + const Vector3& min = scrollMinProperty.GetVector3(); + const Vector3& max = scrollMaxProperty.GetVector3(); + const Vector3& scrollDirection = scrollDirectionProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& orientation = static_cast(scrollDirection.z); + + Vector3 domainSize = max - min; + domainSize.x = fabsf(domainSize.x); + domainSize.y = fabsf(domainSize.y); + domainSize -= size; + + Vector3 mask; // Mask movement aspect of scroll bar + Vector3 relativeOffset; // base position of scroll bar in relation to the container + Vector3 absoluteOffset; // absolute offset position of scroll bar + + if(mVertical) + { + switch(orientation) + { + case Toolkit::ControlOrientation::Up: + { + mask = Vector3::YAXIS; + relativeOffset = (scrollDirection.y < 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 1.0f, 0.0f) : Vector3(1.0f, 0.0f, 0.0f); // Right side of stage. + absoluteOffset = (scrollDirection.y < 0.0f && relativePosition.y <= 0.0f) ? BAR_TAB_OFFSET_V + Vector3( barSize.width * 0.5f, -barSize.height * 0.5f, 1.0f ) : BAR_TAB_OFFSET_V + Vector3( barSize.width * 0.5f, barSize.height * 0.5f, 1.0f ); + break; + } + case Toolkit::ControlOrientation::Left: + { + mask = Vector3::XAXIS; + relativeOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 0.0f, 0.0f) : Vector3(0.0f, 0.0f, 0.0f); // Bottom side of stage. + absoluteOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( -barSize.height * 0.5f, barSize.width * 0.5f, 1.0f ) : Vector3( barSize.height * 0.5f, barSize.width * 0.5f, 1.0f ); + break; + } + case Toolkit::ControlOrientation::Down: + { + mask = Vector3::YAXIS; + relativeOffset = (scrollDirection.y <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(0.0f, 1.0f, 0.0f) : Vector3(0.0f, 0.0f, 0.0f); // Left side of stage. + absoluteOffset = (scrollDirection.y <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( barSize.width * 0.5f, -barSize.height * 0.5f, 1.0f ) : Vector3( barSize.width * 0.5f, barSize.height * 0.5f, 1.0f ); + break; + } + case Toolkit::ControlOrientation::Right: + { + mask = Vector3::XAXIS; + relativeOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 1.0f, 0.0f) : Vector3(0.0f, 1.0f, 0.0f); // Up side of stage. + absoluteOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( -barSize.height * 0.5f, -barSize.width * 0.5f, 1.0f ) : Vector3( barSize.height * 0.5f, -barSize.width * 0.5f, 1.0f ); + break; + } + } + } + else + { + mask = Vector3::XAXIS; + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); // Bottom side of stage. + absoluteOffset = BAR_TAB_OFFSET_H + Vector3( barSize.height * 0.5f, barSize.width * 0.5f, 1.0f ); + } + + Vector3 maskedRelativePosition = Toolkit::IsVertical(orientation) ? Vector3(relativePosition.x * (size.x-barSize.y), relativePosition.y * (size.y-barSize.y), 0.0f) * mask + : Vector3(relativePosition.y * (size.x-barSize.y), relativePosition.x * (size.y-barSize.y), 0.0f) * mask; + + Vector3 finalPosition = relativeOffset * size + absoluteOffset + maskedRelativePosition; + + // If Wrapped Slider, then position 1 domain either before or after current slider. + if(mWrap) + { + if(finalPosition.x < 0.5f) + { + finalPosition.x += size.x; + } + else + { + finalPosition.x -= size.x; + } + + if(finalPosition.y < 0.5f) + { + finalPosition.y += size.y; + } + else + { + finalPosition.y -= size.y; + } + } + + return finalPosition; + } + + bool mVertical; ///< Whether vertical or horizontal. + bool mWrap; ///< Whether to wrap this position. +}; + +/** + * ScrollBar HitSize Constraint + * Resizes HitArea to size of the container. + */ +struct ScrollBarHitSizeConstraint +{ + /** + * @param[in] vertical Whether this constraint controls a vertical scrollbar (true) + * or a horizontal one (false) + * @param[in] thickness The thickness of the scrollbar + */ + ScrollBarHitSizeConstraint(bool vertical, + float thickness) + : mVertical(vertical), + mThickness(thickness) + { + } + + /** + * Constraint operator + * @param[in] current The current HitSize + * @param[in] scrollDirectionProperty The container's scroll direction. + * @param[in] scrollSizeProperty The container's size of viewport. + * @return The new ScrollBar Hit Area size is returned. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& scrollDirectionProperty, + const PropertyInput& scrollSizeProperty) + { + const Vector3& scrollDirection = scrollDirectionProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& orientation = static_cast(scrollDirection.z); + Vector3 size = scrollSizeProperty.GetVector3(); + + Vector3 mask; // Mask size aspect of hit area. + Vector3 offset; // Add Offset size. + + if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) ) + { + mask = Vector3::YAXIS; + offset = Vector3::XAXIS * mThickness; + } + else + { + mask = Vector3::XAXIS; + offset = Vector3::YAXIS * mThickness; + } + + return size * mask + offset; + } + + bool mVertical; ///< Whether vertical or horizontal. + float mThickness; ///< Thickness of the scroll bar +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +using namespace Dali; + +BaseHandle Create() +{ + return BaseHandle(); +} + +TypeRegistration mType( typeid(Toolkit::ScrollBar), typeid(Toolkit::ScrollComponent), Create ); + +} + +ScrollBar::ScrollBar(Toolkit::Scrollable& container, bool vertical) +: mContainer(static_cast(container.GetImplementation())), + mVertical(vertical), + mAxisMask(vertical ? Vector3::YAXIS : Vector3::XAXIS), + mDragMode(false) +{ + Image sliderImage = Image::New( BAR_TAB_IMAGE_PATH ); + + mSlider = ImageActor::New( sliderImage ); + mSlider.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mSlider.SetAnchorPoint( AnchorPoint::CENTER ); + mSlider.SetSize( BAR_TAB_SIZE ); + mSlider.SetStyle( ImageActor::STYLE_NINE_PATCH ); + mSlider.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER ); + + // A duplicate Slider should appear 1 domain away from the original Slider + mSliderWrap = ImageActor::New( sliderImage ); + mSliderWrap.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mSliderWrap.SetAnchorPoint( AnchorPoint::CENTER ); + mSliderWrap.SetSize( BAR_TAB_SIZE ); + mSliderWrap.SetStyle( ImageActor::STYLE_NINE_PATCH ); + mSliderWrap.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER ); + + // target the container to observe for scrolling + Actor target = mContainer.Self(); + Constraint constraint = Constraint::New( Actor::VISIBLE, + Source( target, vertical ? target.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL) : target.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL)), + ScrollBarVisibilityConstraint ); + mSlider.ApplyConstraint( constraint ); + mSliderWrap.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::SIZE, + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ), + Source( target, Actor::SIZE ), + ScrollBarSizeConstraint( vertical ) ); + mSlider.ApplyConstraint( constraint ); + mSliderWrap.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::ROTATION, + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ), + ScrollBarRotationConstraint( vertical ) ); + mSlider.ApplyConstraint( constraint ); + mSliderWrap.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + Source( mSlider, Actor::SIZE), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ), + Source( target, Actor::SIZE ), + ScrollBarPositionConstraint(vertical) ); + + mSlider.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + Source( mSlider, Actor::SIZE), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ), + Source( target, Actor::SIZE ), + ScrollBarPositionConstraint(vertical, true) ); + mSliderWrap.ApplyConstraint( constraint ); + + // Add Sliders to internal Actor, to avoid mixing up with regular + // Actors added by user. + mContainer.AddOverlay( mSlider ); + mContainer.AddOverlay( mSliderWrap ); + mContainer.ScrollStartedSignal().Connect( this, &ScrollBar::OnStarted ); + mContainer.ScrollCompletedSignal().Connect( this, &ScrollBar::OnCompleted ); + + // Hit Area for dragging slider ///////////////////////////////////////////// + mHitArea = Actor::New(); + mHitArea.SetPosition(0.0f, 0.0f, 0.2f); + + mContainer.AddOverlay( mHitArea ); + constraint = Constraint::New( Actor::SIZE, + Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ), + Source( target, Actor::SIZE ), + ScrollBarHitSizeConstraint(vertical, BAR_TAB_SIZE.width) ); + mHitArea.ApplyConstraint( constraint ); + + if(vertical) + { + mHitArea.SetParentOrigin(ParentOrigin::CENTER_RIGHT); + mHitArea.SetAnchorPoint(AnchorPoint::CENTER_RIGHT); + } + else + { + mHitArea.SetParentOrigin(ParentOrigin::BOTTOM_CENTER); + mHitArea.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER); + } + + WaitingContractDelay(); +} + +ScrollBar::~ScrollBar() +{ + DestructTimer(); +} + +void ScrollBar::OnInitialize() +{ + EnableGestureDetection(Gesture::Type(Gesture::Pan)); +} + +void ScrollBar::OnDisconnect() +{ + // Disconnect all connected callback functions. + mContainer.RemoveOverlay( mSlider ); + mContainer.RemoveOverlay( mSliderWrap ); +} + +void ScrollBar::OnPanGesture(Actor actor, PanGesture gesture) +{ + switch(gesture.state) + { + case Gesture::Started: + { + mDragMode = true; + Show(); + mScrollStart = mContainer.GetCurrentScrollPosition(); + mGestureDisplacement = Vector3::ZERO; + break; + } + case Gesture::Continuing: + { + Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f); + mGestureDisplacement+=delta; + + Vector3 size = mContainer.Self().GetCurrentSize(); + Vector3 span = size - Vector3(BAR_TAB_SIZE.y, BAR_TAB_SIZE.y, 1.0f); + Vector3 domainSize = mContainer.GetDomainSize(); + + Vector3 position = mScrollStart + mGestureDisplacement * mAxisMask * domainSize / span; + mContainer.ScrollTo(position, 0.0f); + break; + } + default: + { + mDragMode = false; + break; + } + } +} + +void ScrollBar::OnStarted(const Vector3& position) +{ + // TODO: Need to disable this for the scrollbar which isn't being scrolled. + if(!mDragMode) + { + mDragMode = true; + Show(); + } +} + +void ScrollBar::OnCompleted(const Vector3& position) +{ + if( mDragMode ) + { + mDragMode = false; + + WaitingContractDelay(); + } +} + +bool ScrollBar::OnContractDelayExpired() +{ + if ( !mDragMode ) + { + Hide(); + } + + DestructTimer(); + + return true; +} + +void ScrollBar::Show() +{ + // Cancel any animation + if(mAnimation) + { + mAnimation.Clear(); + mAnimation.Reset(); + } + + mAnimation = Animation::New( BAR_SHOW_TIME ); + mAnimation.OpacityTo( mSlider, 1.0f, AlphaFunctions::EaseIn ); + mAnimation.OpacityTo( mSliderWrap, 1.0f, AlphaFunctions::EaseIn ); + mAnimation.Play(); + + DestructTimer(); +} + +void ScrollBar::Hide() +{ + // Cancel any animation + if(mAnimation) + { + mAnimation.Clear(); + mAnimation.Reset(); + } + + mAnimation = Animation::New( BAR_HIDE_TIME ); + mAnimation.OpacityTo( mSlider, 0.0f, AlphaFunctions::EaseIn ); + mAnimation.OpacityTo( mSliderWrap, 0.0f, AlphaFunctions::EaseIn ); + mAnimation.Play(); +} + +void ScrollBar::CreateTimer() +{ + if( !mTimer ) + { + // Create timer for contract delay + mTimer = Timer::New( BAR_CONTRACT_DELAY * SECOND_UNIT ); + mTimer.TickSignal().Connect( this, &ScrollBar::OnContractDelayExpired ); + } +} + +void ScrollBar::DestructTimer() +{ + if( mTimer ) + { + mTimer.Stop(); + mTimer.TickSignal().Disconnect( this, &ScrollBar::OnContractDelayExpired ); + mTimer.Reset(); + } +} + +void ScrollBar::WaitingContractDelay() +{ + CreateTimer(); + mTimer.Start(); +} + +Toolkit::ScrollBar ScrollBar::New(Toolkit::Scrollable& container, bool vertical) +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< ScrollBar > impl = new ScrollBar( container, vertical ); + + // Pass ownership to CustomActor handle + Toolkit::ScrollBar handle( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.h b/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.h new file mode 100755 index 0000000..f1acd75 --- /dev/null +++ b/dali-toolkit/internal/controls/scroll-component/scroll-bar-impl.h @@ -0,0 +1,169 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_BAR_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * ScrollBar is a UI component that can be added to the sides of the ScrollView + * indicating the current scroll position within the domain. + */ +class ScrollBar : public ScrollComponent +{ + +public: + + /** + * ScrollBar constructor. + * @param[in] container Reference to the container of scroll bar + * @param[in] vertical Whether ScrollBar should be oriented vertically (true) + * or horizontally (false) + */ + ScrollBar(Toolkit::Scrollable& container, bool vertical); + + /** + * Virtual destructor + */ + virtual ~ScrollBar(); + + /** + * Create an initialized ScrollBar + * @param[in] container Reference to the container of scroll bar + * @param[in] vertical Whether ScrollBar should be oriented vertically (true) + * or horizontally (false) + * @return A pointer to the created ScrollBar. + */ + static Toolkit::ScrollBar New(Toolkit::Scrollable& container, bool vertical); + + /** + * Show ScrollBar + */ + void Show(); + + /** + * Hide ScrollBar + */ + void Hide(); + +private: + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::ScrollComponent::OnDisconnect() + */ + virtual void OnDisconnect(); + + /** + * Called when scrollbar is dragged. + * @param[in] actor Actor under touch + * @param[in] gesture The pan gesture data. + */ + void OnPanGesture(Actor actor, PanGesture gesture); + + /** + * Called when scrolling starts (scroll bars should extend out) + * @param[in] position current scroll position. + */ + void OnStarted(const Vector3& position); + + /** + * Called when scrolling ends (scroll bars should contract) + * @param[in] position current scroll position. + */ + void OnCompleted(const Vector3& position); + + /** + * Called when timer is finished. This time guarantee contract animation time. + * @return Timer is used or not. + */ + bool OnContractDelayExpired(void); + + /** + * Create timer. This timer used for wating contract. + */ + void CreateTimer(); + + /** + * Destruct timer. + */ + void DestructTimer(); + + /** + * Wait ContractTime + */ + void WaitingContractDelay(); + +private: + Scrollable& mContainer; ///< Container of scroll bar + ImageActor mSlider; ///< Scroll Slider. + ImageActor mSliderWrap; ///< Scroll Slider (wrapped view). + Actor mHitArea; ///< Hit Area for dragging scroll slider. + PanGestureDetector mPanGesture; ///< Pan Gesture detector for dragging scrollbar. + Animation mAnimation; ///< Scroll Contract/Expand Animation. + bool mVertical; ///< Scroll Axis (Vertical or Horizontal) + Vector3 mAxisMask; ///< Scroll Axis mask (Vector3::YAXIS for vert. or Vector3::XAXIS for horiz.) + Vector3 mScrollStart; ///< Scroll Start position (start of drag) + Vector3 mGestureDisplacement; ///< Gesture Displacement. + bool mDragMode; ///< Flag indicating whether currently dragging or not. + Timer mTimer; ///< Timer guarantee contract delay time. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ScrollBar& GetImpl(Toolkit::ScrollBar& scrollBar) +{ + DALI_ASSERT_ALWAYS(scrollBar); + + Dali::RefObject& handle = scrollBar.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::ScrollBar& GetImpl(const Toolkit::ScrollBar& scrollBar) +{ + DALI_ASSERT_ALWAYS(scrollBar); + + const Dali::RefObject& handle = scrollBar.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_BAR_H__ diff --git a/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.cpp b/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.cpp new file mode 100644 index 0000000..b1299db --- /dev/null +++ b/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +BaseHandle Create() +{ + // empty handle as we cannot create ScrollComponent + return Toolkit::ScrollComponent::New(); +} + +TypeRegistration mType( typeid(Toolkit::ScrollComponent), typeid(Toolkit::Control), Create ); + +} + +ScrollComponent::ScrollComponent() +: ControlImpl(true/*requires touch*/) +{ + +} + +ScrollComponent::~ScrollComponent() +{ +} + +Toolkit::ScrollComponent ScrollComponent::New(Toolkit::Scrollable& scrollable, Toolkit::Scrollable::ScrollComponentType type) +{ + Toolkit::ScrollComponent instance; + + switch(type) + { + case Toolkit::Scrollable::VerticalScrollBar: + { + instance = static_cast(Toolkit::ScrollBar::New(scrollable, true)); + break; + } + case Toolkit::Scrollable::HorizontalScrollBar: + { + instance = static_cast(Toolkit::ScrollBar::New(scrollable, false)); + break; + } + default: + { + DALI_ASSERT_ALWAYS(true && "Unrecognized component type"); + } + } + + return instance; +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.h b/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.h new file mode 100644 index 0000000..a123ad1 --- /dev/null +++ b/dali-toolkit/internal/controls/scroll-component/scroll-component-impl.h @@ -0,0 +1,111 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_COMPONENTS_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_COMPONENTS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class ScrollComponent; + +typedef IntrusivePtr ScrollComponentPtr; + + +/** + * Base class for derived ScrollComponents + * ScrollComponents such as ScrollBar are derived from this class. + * To instantiate these ScrollBars and other derived components. + */ +class ScrollComponent : public ControlImpl +{ +public: + + /** + * Create an initialized ScrollComponent + * @param[in] scrollable reference to ScrollView implementation + * @param[in] type the type of scroll component to create. + * @return A pointer to the created ScrollComponent. + */ + static Toolkit::ScrollComponent New(Toolkit::Scrollable& scrollable, Toolkit::Scrollable::ScrollComponentType type); + + /** + * Called when the scroll component is disconnected from a Scrollable container. + */ + virtual void OnDisconnect() + { + } + +protected: + + /** + * Construct a new ScrollComponent. + */ + ScrollComponent(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollComponent(); + +private: + + // Undefined + ScrollComponent(const ScrollComponent&); + + // Undefined + ScrollComponent& operator=(const ScrollComponent& rhs); + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ScrollComponent& GetImpl(Toolkit::ScrollComponent& scrollComponent) +{ + DALI_ASSERT_ALWAYS(scrollComponent); + + Dali::RefObject& handle = scrollComponent.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::ScrollComponent& GetImpl(const Toolkit::ScrollComponent& scrollComponent) +{ + DALI_ASSERT_ALWAYS(scrollComponent); + + const Dali::RefObject& handle = scrollComponent.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_COMPONENTS_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 new file mode 100644 index 0000000..a0d3be3 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp @@ -0,0 +1,1586 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +using namespace std; +using namespace Dali; + +namespace // unnamed namespace +{ + +//Type registration +TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL ); + +const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f; +const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f; +const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f; + +const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second +const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second + +const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second +const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second + +const float MILLISECONDS_PER_SECONDS = 1000.0f; + +const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png"; +const Rect OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 ); +const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f); +const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels +const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f; // 0.5 second +const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f; + +const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" ); +const string POSITION_PROPERTY_NAME( "item-view-position" ); +const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" ); +const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" ); +const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" ); +const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" ); + +// Functors which wrap constraint functions with stored item IDs + +struct WrappedVector3Constraint +{ + WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId) + : mWrapMe(wrapMe), + mItemId(itemId) + { + } + + Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) + { + float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); + + return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); + } + + Toolkit::ItemLayout::Vector3Function mWrapMe; + unsigned int mItemId; +}; + +struct WrappedQuaternionConstraint +{ + WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId) + : mWrapMe(wrapMe), + mItemId(itemId) + { + } + + Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) + { + float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); + + return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); + } + + Toolkit::ItemLayout::QuaternionFunction mWrapMe; + unsigned int mItemId; +}; + +struct WrappedVector4Constraint +{ + WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId) + : mWrapMe(wrapMe), + mItemId(itemId) + { + } + + Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) + { + float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); + + return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); + } + + Toolkit::ItemLayout::Vector4Function mWrapMe; + unsigned int mItemId; +}; + +struct WrappedBoolConstraint +{ + WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId) + : mWrapMe(wrapMe), + mItemId(itemId) + { + } + + bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) + { + float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); + + return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); + } + + Toolkit::ItemLayout::BoolFunction mWrapMe; + unsigned int mItemId; +}; + +/** + * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction + */ +float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout) +{ + Radian scrollDirection(layout.GetScrollDirection()); + + float cosTheta = cosf(scrollDirection); + float sinTheta = sinf(scrollDirection); + + return panDistance.x * sinTheta + panDistance.y * cosTheta; +} + +// Overshoot overlay constraints + +struct OvershootOverlaySizeConstraint +{ + float operator()(const float& current, + const PropertyInput& parentScrollDirectionProperty, + const PropertyInput& parentOvershootProperty, + const PropertyInput& parentSizeProperty) + { + const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); + const Vector3 parentSize = parentSizeProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); + + float overlayWidth; + + if(Toolkit::IsVertical(parentOrientation)) + { + overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y; + } + else + { + overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x; + } + + return overlayWidth; + } +}; + +struct OvershootOverlayRotationConstraint +{ + Quaternion operator()(const Quaternion& current, + const PropertyInput& parentScrollDirectionProperty, + const PropertyInput& parentOvershootProperty) + { + const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); + const float parentOvershoot = parentOvershootProperty.GetFloat(); + const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); + + Quaternion rotation; + + if(Toolkit::IsVertical(parentOrientation)) + { + if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) + { + if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) + || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) + { + rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } + else + { + rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + } + } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) + { + rotation = Quaternion(0.0f, Vector3::ZAXIS); + } + else + { + rotation = Quaternion(Math::PI, Vector3::ZAXIS); + } + } + else + { + if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) + { + if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0) + ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) ) + { + rotation = Quaternion(Math::PI, Vector3::ZAXIS); + } + else + { + rotation = Quaternion(0.0f, Vector3::ZAXIS); + } + } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + { + rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + } + else + { + rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } + } + + return rotation; + } +}; + +struct OvershootOverlayPositionConstraint +{ + Vector3 operator()(const Vector3& current, + const PropertyInput& parentSizeProperty, + const PropertyInput& parentScrollDirectionProperty, + const PropertyInput& parentOvershootProperty) + { + const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); + const float parentOvershoot = parentOvershootProperty.GetFloat(); + const Vector3 parentSize = parentSizeProperty.GetVector3(); + const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); + + Vector3 relativeOffset; + + if(Toolkit::IsVertical(parentOrientation)) + { + if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) + { + if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) + || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } + else + { + relativeOffset =Vector3(0.0f, 1.0f, 0.0f); + } + } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } + else + { + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + } + else + { + if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) + { + if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0) + || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } + else + { + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + } + else + { + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } + } + + return relativeOffset * parentSize; + + } +}; + +struct OvershootOverlayVisibilityConstraint +{ + bool operator()(const bool& current, + const PropertyInput& parentLayoutScrollableProperty) + { + const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean(); + + return parentLayoutScrollable; + } +}; + +/** + * Relative position Constraint + * Generates the relative position value of the item view based on the layout position, + * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis. + */ +Vector3 RelativePositionConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollMinProperty, + const PropertyInput& scrollMaxProperty, + const PropertyInput& layoutSizeProperty) +{ + const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f); + const Vector3& min = scrollMinProperty.GetVector3(); + const Vector3& max = scrollMaxProperty.GetVector3(); + + Vector3 relativePosition; + Vector3 domainSize = max - min; + + relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f; + relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f; + + return relativePosition; +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ItemPool +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void ItemPool::AddItems(bool scrollingTowardsLast, ItemRange range, const Vector3& targetSize) +{ + // Add new actors to the ItemPool. + // The order of addition depends on the scroll direction. + if (scrollingTowardsLast) + { + for (unsigned int itemId = range.begin; itemId < range.end; ++itemId) + { + AddItem(itemId, targetSize); + } + } + else + { + for (unsigned int itemId = range.end; itemId > range.begin; --itemId) + { + AddItem(itemId-1, targetSize); + } + } +} + +void ItemPool::RemoveItems(ItemRange range) +{ + // Remove unwanted actors from the ItemView & ItemPool + for (IDKeyIter iter = mIdKeyContainer.begin(); iter != mIdKeyContainer.end(); ) + { + unsigned int current = iter->first; + + if (!range.Within(current)) + { + mItemView.ActorRemovedFromItemPool(iter->second, iter->first); + + mActorKeyContainer.erase(iter->second); + mIdKeyContainer.erase(iter++); // erase invalidates the current iter; the post-increment is important here + } + else + { + ++iter; + } + } +} + +void ItemPool::AddItem(unsigned int itemId, const Vector3& targetSize) +{ + if (mIdKeyContainer.find(itemId) == mIdKeyContainer.end()) + { + Actor actor = mItemView.CreateActor(itemId); + + if (actor) + { + mIdKeyContainer.insert(IDKeyPair(itemId, actor)); + mActorKeyContainer.insert(ActorKeyPair(actor, itemId)); + + mItemView.ActorAddedToItemPool(actor, itemId, targetSize); + } + } +} + +bool ItemPool::RemoveItem(unsigned int itemId) +{ + bool found = false; + + IDKeyIter iter = mIdKeyContainer.find(itemId); + if (iter != mIdKeyContainer.end()) + { + mItemView.ActorRemovedFromItemPool(iter->second, iter->first); + + mActorKeyContainer.erase(iter->second); + for (ActorKeyIter iterActorKey = mActorKeyContainer.begin(); iterActorKey != mActorKeyContainer.end(); ++iterActorKey) + { + if(iterActorKey->second > itemId) + { + iterActorKey->second--; + } + } + + for (IDKeyIter iterIDKey = iter; iterIDKey != mIdKeyContainer.end(); ++iterIDKey) + { + if(iterIDKey->first < mIdKeyContainer.rbegin()->first) + { + iterIDKey->second = mIdKeyContainer[iterIDKey->first + 1]; + } + else + { + mIdKeyContainer.erase(iterIDKey); + break; + } + } + + found = true; + } + + return found; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ItemView +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory) +{ + // Create the implementation + ItemViewPtr itemView(new ItemView(factory)); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::ItemView handle(*itemView); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + itemView->Initialize(); + + return handle; +} + +ItemView::ItemView(ItemFactory& factory) +: Scrollable(), + mItemFactory(factory), + mItemPool(*this), + mActiveLayout(NULL), + mAnimatingOvershootOn(false), + mAnimateOvershootOff(false), + mAnchoringEnabled(true), + mAnchoringDuration(DEFAULT_ANCHORING_DURATION), + mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS), + mRefreshOrderHint(true/*Refresh item 0 first*/), + mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED), + mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE), + mScrollDistance(0.0f), + mScrollSpeed(0.0f), + mTotalPanDisplacement(Vector2::ZERO), + mScrollOvershoot(0.0f), + mIsFlicking(false), + mGestureState(Gesture::Clear) +{ + SetRequiresMouseWheelEvents(true); + SetKeyboardNavigationSupport(true); +} + +void ItemView::OnInitialize() +{ + SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); + + RegisterCommonProperties(); + + Actor self = Self(); + + mOvershootEffect = OvershootRippleEffect::New(); + Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH ); + mOvershootOverlay = ImageActor::New( overshootImage ); + mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT); + mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY); + mOvershootOverlay.SetShaderEffect(mOvershootEffect); + mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA); + self.Add(mOvershootOverlay); + + mPropertyLayoutPosition = self.RegisterProperty(LAYOUT_POSITION_PROPERTY_NAME, 0.0f); + mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f); + mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f); + mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f); + mPropertyOvershoot = self.RegisterProperty(OVERSHOOT_PROPERTY_NAME, 0.0f); + + ApplyOvershootOverlayConstraints(); + + Constraint constraint = Constraint::New(mPropertyRelativePosition, + LocalSource(mPropertyPosition), + LocalSource(mPropertyPositionMin), + LocalSource(mPropertyPositionMax), + LocalSource(Actor::SIZE), + RelativePositionConstraint); + self.ApplyConstraint(constraint); + + Vector2 stageSize = Stage::GetCurrent().GetSize(); + mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION; + + EnableGestureDetection(Gesture::Type(Gesture::Pan)); + + mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT ); + mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished ); +} + +ItemView::~ItemView() +{ +} + +unsigned int ItemView::GetLayoutCount() const +{ + return mLayouts.size(); +} + +void ItemView::AddLayout(ItemLayout& layout) +{ + mLayouts.push_back(ItemLayoutPtr(&layout)); +} + +void ItemView::RemoveLayout(unsigned int layoutIndex) +{ + DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size()); + + if (mActiveLayout == mLayouts[layoutIndex].Get()) + { + mActiveLayout = NULL; + } + + mLayouts.erase(mLayouts.begin() + layoutIndex); +} + +ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const +{ + return mLayouts[layoutIndex]; +} + +ItemLayoutPtr ItemView::GetActiveLayout() const +{ + return ItemLayoutPtr(mActiveLayout); +} + +float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const +{ + return Self().GetProperty(mPropertyLayoutPosition) + static_cast(itemId); +} + +void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds) +{ + DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size()); + + Actor self = Self(); + + // The ItemView size should match the active layout size + self.SetSize(targetSize); + + // Switch to the new layout + ItemLayout* previousLayout = mActiveLayout; + mActiveLayout = mLayouts[layoutIndex].Get(); + + // Calculate which items are within either layout + ItemRange oldRange = previousLayout ? GetItemRange(*previousLayout, targetSize, false/*don't reserve extra*/) : ItemRange(0u, 0u); + ItemRange newRange = GetItemRange(*mActiveLayout, targetSize, false/*don't reserve extra*/); + + // Move the items to the new layout positions... + + bool resizeAnimationNeeded(false); + + const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); + for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + { + unsigned int itemId = iter->first; + Actor actor = iter->second; + + // Immediately relayout items that aren't within either layout + bool immediate = !oldRange.Within(itemId) && + !newRange.Within(itemId); + + // Remove constraints from previous layout + actor.RemoveConstraints(); + + Vector3 size; + if(mActiveLayout->GetItemSize(itemId, targetSize, size)) + { + if (!immediate && + durationSeconds > 0.0f) + { + // Use a size animation + if (!resizeAnimationNeeded) + { + resizeAnimationNeeded = true; + RemoveAnimation(mResizeAnimation); + mResizeAnimation = Animation::New(durationSeconds); + } + + // The layout provides its own resize animation + mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds); + } + else + { + // resize immediately + actor.SetSize(size); + } + } + + ApplyConstraints(actor, *mActiveLayout, itemId, immediate ? 0.0f : durationSeconds); + } + + if (resizeAnimationNeeded) + { + mResizeAnimation.Play(); + } + + // Refresh the new layout + ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/); + AddItems(*mActiveLayout, targetSize, range); + + // Scroll to an appropriate layout position + + bool scrollAnimationNeeded(false); + float firstItemScrollPosition(0.0f); + + float current = self.GetProperty(mPropertyLayoutPosition); + float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout); + self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize)); + + if (current < minimum) + { + scrollAnimationNeeded = true; + firstItemScrollPosition = minimum; + } + else if (mAnchoringEnabled) + { + scrollAnimationNeeded = true; + firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current); + } + + if (scrollAnimationNeeded) + { + RemoveAnimation(mScrollAnimation); + mScrollAnimation = Animation::New(mAnchoringDuration); + mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut ); + mScrollAnimation.Play(); + } + + self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize)); + AnimateScrollOvershoot(0.0f); + mScrollOvershoot = 0.0f; + + Radian scrollDirection(mActiveLayout->GetScrollDirection()); + float orientation = static_cast(mActiveLayout->GetOrientation()); + self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation)); + + self.SetProperty(mPropertyScrollSpeed, mScrollSpeed); + + CalculateDomainSize(targetSize); +} + +void ItemView::DeactivateCurrentLayout() +{ + if (mActiveLayout) + { + const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); + for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + { + Actor actor = iter->second; + actor.RemoveConstraints(); + } + + mActiveLayout = NULL; + } + + CancelRefreshTimer(); +} + +bool ItemView::OnRefreshTick() +{ + // Short-circuit if there is no active layout + if (!mActiveLayout) + { + return false; + } + + const Vector3 layoutSize = Self().GetCurrentSize(); + + ItemRange range = GetItemRange(*mActiveLayout, layoutSize, true/*reserve extra*/); + + RemoveItems(range); + AddItems(*mActiveLayout, layoutSize, range); + + // Keep refreshing whilst the layout is moving + return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing); +} + +void ItemView::SetMinimumSwipeSpeed(float speed) +{ + mMinimumSwipeSpeed = speed; +} + +float ItemView::GetMinimumSwipeSpeed() const +{ + return mMinimumSwipeSpeed; +} + +void ItemView::SetMinimumSwipeDistance(float distance) +{ + mMinimumSwipeDistance = distance; +} + +float ItemView::GetMinimumSwipeDistance() const +{ + return mMinimumSwipeDistance; +} + +void ItemView::SetMouseWheelScrollDistanceStep(float step) +{ + mMouseWheelScrollDistanceStep = step; +} + +float ItemView::GetMouseWheelScrollDistanceStep() const +{ + return mMouseWheelScrollDistanceStep; +} + +void ItemView::SetAnchoring(bool enabled) +{ + mAnchoringEnabled = enabled; +} + +bool ItemView::GetAnchoring() const +{ + return mAnchoringEnabled; +} + +void ItemView::SetAnchoringDuration(float durationSeconds) +{ + mAnchoringDuration = durationSeconds; +} + +float ItemView::GetAnchoringDuration() const +{ + return mAnchoringDuration; +} + +void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds) +{ + mRefreshIntervalMilliseconds = intervalMilliseconds; +} + +unsigned int ItemView::GetRefreshInterval() const +{ + return mRefreshIntervalMilliseconds; +} + +Actor ItemView::GetItem(unsigned int itemId) const +{ + Actor actor; + + ItemPool::IDKeyConstIter found = mItemPool.GetIDKeyContainer().find(itemId); + if (found != mItemPool.GetIDKeyContainer().end()) + { + actor = found->second; + } + + return actor; +} + +unsigned int ItemView::GetItemId(Actor actor) const +{ + unsigned int itemId(0); + + ItemPool::ActorKeyConstIter found = mItemPool.GetActorKeyContainer().find(actor); + if (found != mItemPool.GetActorKeyContainer().end()) + { + itemId = found->second; + } + + return itemId; +} + +void ItemView::RemoveItem(unsigned int itemId, float durationSeconds) +{ + if (mItemPool.RemoveItem(itemId)) + { + const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); + for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + { + unsigned int id = iter->first; + Actor actor = iter->second; + + // Reposition the items if necessary + actor.RemoveConstraints(); + ApplyConstraints(actor, *mActiveLayout, id, durationSeconds); + } + + CalculateDomainSize(Self().GetCurrentSize()); + } +} + +Actor ItemView::CreateActor(unsigned int itemId) +{ + return mItemFactory.NewItem(itemId); +} + +void ItemView::ActorAddedToItemPool(Actor actor, unsigned int itemId, const Vector3& targetSize) +{ + Actor self = Self(); + + actor.SetParentOrigin(ParentOrigin::CENTER); + actor.SetAnchorPoint(AnchorPoint::CENTER); + + if (mActiveLayout) + { + Vector3 size; + if(mActiveLayout->GetItemSize(itemId, targetSize, size)) + { + actor.SetSize(size); + } + + ApplyConstraints(actor, *mActiveLayout, itemId, 0.0f/*immediate*/); + } + + self.Add(actor); +} + +void ItemView::ActorRemovedFromItemPool(Actor actor, unsigned int itemId) +{ + Self().Remove(actor); +} + +void ItemView::RemoveItems(ItemRange range) +{ + mItemPool.RemoveItems(range); +} + +void ItemView::AddItems(ItemLayout& layout, const Vector3& layoutSize, ItemRange range) +{ + range.end = min(mItemFactory.GetNumberOfItems(), range.end); + + mItemPool.AddItems(mRefreshOrderHint, range, layoutSize); +} + +ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra) +{ + unsigned int itemCount = mItemFactory.GetNumberOfItems(); + + ItemRange available(0u, itemCount); + + ItemRange range = layout.GetItemsWithinArea(Self().GetProperty(mPropertyLayoutPosition), layoutSize); + + if (reserveExtra) + { + // Add the reserve items for scrolling + unsigned int extra = layout.GetReserveItemCount(layoutSize); + range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u; + range.end += extra; + } + + return range.Intersection(available); +} + +bool ItemView::OnTouchEvent(const TouchEvent& event) +{ + // Ignore events with multiple-touch points + if (event.GetPointCount() != 1) + { + return false; + } + + if (event.GetPoint(0).state == TouchPoint::Down) + { + // Cancel ongoing scrolling etc. + mGestureState = Gesture::Clear; + + mScrollDistance = 0.0f; + mScrollSpeed = 0.0f; + Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed); + + mScrollOvershoot = 0.0f; + AnimateScrollOvershoot(0.0f); + + mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + + RemoveAnimation(mScrollAnimation); + } + + return true; // consume since we're potentially scrolling +} + +bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event) +{ + // Respond the mouse wheel event to scroll + if (mActiveLayout) + { + Actor self = Self(); + const Vector3 layoutSize = Self().GetCurrentSize(); + float layoutPositionDelta = self.GetProperty(mPropertyLayoutPosition) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor()); + float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); + self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); + mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + StartRefreshTimer(); + } + + if (mMouseWheelEventFinishedTimer.IsRunning()) + { + mMouseWheelEventFinishedTimer.Stop(); + } + + mMouseWheelEventFinishedTimer.Start(); + + return true; +} + +bool ItemView::OnMouseWheelEventFinished() +{ + if (mActiveLayout) + { + RemoveAnimation(mScrollAnimation); + + // No more mouse wheel events coming. Do the anchoring if enabled. + mScrollAnimation = DoAnchoring(); + if (mScrollAnimation) + { + StartRefreshTimer(); + + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); + mScrollAnimation.Play(); + } + else + { + mScrollOvershoot = 0.0f; + AnimateScrollOvershoot(0.0f); + + mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + } + } + + return false; +} + +void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration) +{ + ItemLayout::Vector3Function positionConstraint; + if (layout.GetPositionConstraint(itemId, positionConstraint)) + { + WrappedVector3Constraint wrapped(positionConstraint, itemId); + + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource( mPropertyLayoutPosition ), + ParentSource( mPropertyScrollSpeed ), + ParentSource( Actor::SIZE ), + wrapped ); + constraint.SetApplyTime(duration); + + actor.ApplyConstraint(constraint); + } + + ItemLayout::QuaternionFunction rotationConstraint; + if (layout.GetRotationConstraint(itemId, rotationConstraint)) + { + WrappedQuaternionConstraint wrapped(rotationConstraint, itemId); + + Constraint constraint = Constraint::New( Actor::ROTATION, + ParentSource( mPropertyLayoutPosition ), + ParentSource( mPropertyScrollSpeed ), + ParentSource( Actor::SIZE ), + wrapped ); + constraint.SetApplyTime(duration); + + actor.ApplyConstraint(constraint); + } + + ItemLayout::Vector3Function scaleConstraint; + if (layout.GetScaleConstraint(itemId, scaleConstraint)) + { + WrappedVector3Constraint wrapped(scaleConstraint, itemId); + + Constraint constraint = Constraint::New( Actor::SCALE, + ParentSource( mPropertyLayoutPosition ), + ParentSource( mPropertyScrollSpeed ), + ParentSource( Actor::SIZE ), + wrapped ); + constraint.SetApplyTime(duration); + + actor.ApplyConstraint(constraint); + } + + ItemLayout::Vector4Function colorConstraint; + if (layout.GetColorConstraint(itemId, colorConstraint)) + { + WrappedVector4Constraint wrapped(colorConstraint, itemId); + + Constraint constraint = Constraint::New( Actor::COLOR, + ParentSource( mPropertyLayoutPosition ), + ParentSource( mPropertyScrollSpeed ), + ParentSource( Actor::SIZE ), + wrapped ); + constraint.SetApplyTime(duration); + + // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in + constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); + constraint.SetRemoveAction(Dali::Constraint::Discard); + + actor.ApplyConstraint(constraint); + } + + ItemLayout::BoolFunction visibilityConstraint; + if (layout.GetVisibilityConstraint(itemId, visibilityConstraint)) + { + WrappedBoolConstraint wrapped(visibilityConstraint, itemId); + + Constraint constraint = Constraint::New( Actor::VISIBLE, + ParentSource( mPropertyLayoutPosition ), + ParentSource( mPropertyScrollSpeed ), + ParentSource( Actor::SIZE ), + wrapped ); + constraint.SetApplyTime(duration); + + // Release visibility constraints the same time as the color constraint + constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); + constraint.SetRemoveAction(Dali::Constraint::Discard); + + actor.ApplyConstraint(constraint); + } +} + +float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout) +{ + Actor self = Self(); + float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize); + float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition)); + mScrollOvershoot = targetPosition - clamppedPosition; + self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); + + return clamppedPosition; +} + +void ItemView::OnPan(PanGesture gesture) +{ + Actor self = Self(); + const Vector3 layoutSize = Self().GetCurrentSize(); + + RemoveAnimation(mScrollAnimation); + + // Short-circuit if there is no active layout + if (!mActiveLayout) + { + mGestureState = Gesture::Clear; + return; + } + + mGestureState = gesture.state; + + switch (mGestureState) + { + case Gesture::Finished: + { + // Swipe Detection + if (fabsf(mScrollDistance) > mMinimumSwipeDistance && + mScrollSpeed > mMinimumSwipeSpeed) + { + float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f; + + mRefreshOrderHint = true; + + float currentLayoutPosition = self.GetProperty(mPropertyLayoutPosition); + float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction, + layoutSize, + *mActiveLayout); + + if (mAnchoringEnabled) + { + firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition); + } + + RemoveAnimation(mScrollAnimation); + + float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))); + mScrollAnimation = Animation::New(flickAnimationDuration); + mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut ); + + mIsFlicking = true; + // Check whether it has already scrolled to the end + if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0) + { + AnimateScrollOvershoot(0.0f); + } + } + + // Anchoring may be triggered when there was no swipe + if (!mScrollAnimation) + { + mScrollAnimation = DoAnchoring(); + } + + // Reset the overshoot if no scroll animation. + if (!mScrollAnimation) + { + mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + + AnimateScrollOvershoot(0.0f, false); + } + } + break; + + case Gesture::Started: // Fall through + { + mTotalPanDisplacement = Vector2::ZERO; + } + + case Gesture::Continuing: + { + mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout); + mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed()); + + // Refresh order depends on the direction of the scroll; negative is towards the last item. + mRefreshOrderHint = mScrollDistance < 0.0f; + + RemoveAnimation(mScrollSpeedAnimation); + mScrollSpeedAnimation = Animation::New(0.3f); + mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear ); + mScrollSpeedAnimation.Play(); + + float layoutPositionDelta = self.GetProperty(mPropertyLayoutPosition) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor()); + + float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); + + self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); + mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + + mTotalPanDisplacement += gesture.displacement; + mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition; + if( mScrollOvershoot > Math::MACHINE_EPSILON_1 ) + { + AnimateScrollOvershoot(1.0f); + } + else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 ) + { + AnimateScrollOvershoot(-1.0f); + } + else + { + AnimateScrollOvershoot(0.0f); + } + + StartRefreshTimer(); + } + break; + + case Gesture::Cancelled: + { + mScrollAnimation = DoAnchoring(); + } + break; + + default: + break; + } + + if (mScrollAnimation) + { + StartRefreshTimer(); + + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); + mScrollAnimation.Play(); + } +} + +bool ItemView::OnAccessibilityPan(PanGesture gesture) +{ + OnPan(gesture); + return true; +} + +Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + Actor nextFocusActor; + if(mActiveLayout) + { + int nextItemID = 0; + if(!actor || actor == this->Self()) + { + nextFocusActor = GetItem(nextItemID); + } + else if(actor && actor.GetParent() == this->Self()) + { + int itemID = GetItemId(actor); + nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled); + nextFocusActor = GetItem(nextItemID); + if(nextFocusActor == actor) + { + // need to pass NULL actor back to focus manager + nextFocusActor.Reset(); + return nextFocusActor; + } + } + float layoutPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty(mPropertyLayoutPosition)); + Vector3 layoutSize = Self().GetCurrentSize(); + if(!nextFocusActor) + { + // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item + ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize); + nextItemID = viewableItems.begin; + nextFocusActor = GetItem(nextItemID); + } + } + return nextFocusActor; +} + +void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor) +{ + // only in this function if our chosen focus actor was actually used + if(commitedFocusableActor) + { + int nextItemID = GetItemId(commitedFocusableActor); + float layoutPosition = Self().GetProperty(mPropertyLayoutPosition); + Vector3 layoutSize = Self().GetCurrentSize(); + Vector3 focusItemPosition = Vector3::ZERO; + ItemLayout::Vector3Function itemPositionConstraint; + if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint)) + { + focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize); + } + + float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize); + ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION); + } +} + +Animation ItemView::DoAnchoring() +{ + Animation anchoringAnimation; + Actor self = Self(); + + if (mActiveLayout && mAnchoringEnabled) + { + float anchorPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty(mPropertyLayoutPosition)); + + anchoringAnimation = Animation::New(mAnchoringDuration); + anchoringAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), anchorPosition, AlphaFunctions::EaseOut ); + anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut ); + anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut ); + if(!mIsFlicking) + { + AnimateScrollOvershoot(0.0f); + } + } + + return anchoringAnimation; +} + +void ItemView::OnScrollFinished(Animation& source) +{ + Actor self = Self(); + + RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling + + mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + + if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1) + { + AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true); + } + else + { + // Reset the overshoot + AnimateScrollOvershoot( 0.0f ); + } + mIsFlicking = false; + + mScrollOvershoot = 0.0f; +} + +void ItemView::OnOvershootOnFinished(Animation& animation) +{ + mAnimatingOvershootOn = false; + mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished); + RemoveAnimation(mScrollOvershootAnimation); + if(mAnimateOvershootOff) + { + AnimateScrollOvershoot(0.0f); + } +} + +void ItemView::StartRefreshTimer() +{ + if (!mRefreshTimer) + { + mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds ); + mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick ); + } + + if (!mRefreshTimer.IsRunning()) + { + mRefreshTimer.Start(); + } +} + +void ItemView::CancelRefreshTimer() +{ + if (mRefreshTimer) + { + mRefreshTimer.Stop(); + } +} + +void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds) +{ + Actor self = Self(); + const Vector3 layoutSize = Self().GetCurrentSize(); + float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout); + + StartRefreshTimer(); + + if(durationSeconds > 0.0f) + { + RemoveAnimation(mScrollAnimation); + mScrollAnimation = Animation::New(durationSeconds); + mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); + mScrollAnimation.Play(); + } + else + { + self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + AnimateScrollOvershoot(0.0f); + } + + mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); +} + +void ItemView::RemoveAnimation(Animation& animation) +{ + if(animation) + { + // Cease animating, and reset handle. + animation.Clear(); + animation.Reset(); + } +} + +void ItemView::CalculateDomainSize(const Vector3& layoutSize) +{ + Actor self = Self(); + + Vector3 firstItemPosition(Vector3::ZERO); + Vector3 lastItemPosition(Vector3::ZERO); + + if(mActiveLayout) + { + ItemLayout::Vector3Function firstItemPositionConstraint; + if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) + { + firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize); + } + + float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize); + ItemLayout::Vector3Function lastItemPositionConstraint; + if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint)) + { + lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize); + } + + if(IsHorizontal(mActiveLayout->GetOrientation())) + { + self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f)); + self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f)); + } + else + { + self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f)); + self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f)); + } + + bool isLayoutScrollable = IsLayoutScrollable(layoutSize); + self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable); + self.SetProperty(mPropertyCanScrollHorizontal, false); + } +} + +Vector3 ItemView::GetDomainSize() const +{ + Actor self = Self(); + + float minScrollPosition = self.GetProperty(mPropertyPositionMin); + float maxScrollPosition = self.GetProperty(mPropertyPositionMax); + + return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f); +} + +bool ItemView::IsLayoutScrollable(const Vector3& layoutSize) +{ + Actor self = Self(); + + float currentLayoutPosition = ClampFirstItemPosition(self.GetProperty(mPropertyLayoutPosition), layoutSize, *mActiveLayout); + float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout); + float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout); + + return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0); +} + +float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const +{ + Vector3 firstItemPosition(Vector3::ZERO); + ItemLayout::Vector3Function firstItemPositionConstraint; + if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) + { + firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize); + } + + return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y; +} + +Vector3 ItemView::GetCurrentScrollPosition() const +{ + float currentLayoutPosition = Self().GetProperty(mPropertyLayoutPosition); + return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f); +} + +void ItemView::AddOverlay(Actor actor) +{ + Self().Add(actor); +} + +void ItemView::RemoveOverlay(Actor actor) +{ + Self().Remove(actor); +} + +void ItemView::ScrollTo(const Vector3& position, float duration) +{ + Actor self = Self(); + const Vector3 layoutSize = Self().GetCurrentSize(); + + float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout); + + StartRefreshTimer(); + + if(duration > 0.0f) + { + RemoveAnimation(mScrollAnimation); + mScrollAnimation = Animation::New(duration); + mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); + mScrollAnimation.Play(); + } + else + { + self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + AnimateScrollOvershoot(0.0f); + } + + mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); +} + +void ItemView::ApplyOvershootOverlayConstraints() +{ + Constraint constraint = Constraint::New( Actor::SIZE_WIDTH, + ParentSource( mPropertyScrollDirection ), + ParentSource( mPropertyOvershoot ), + ParentSource( Actor::SIZE ), + OvershootOverlaySizeConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height); + + constraint = Constraint::New( Actor::ROTATION, + ParentSource( mPropertyScrollDirection ), + ParentSource( mPropertyOvershoot ), + OvershootOverlayRotationConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::POSITION, + ParentSource( Actor::SIZE ), + ParentSource( mPropertyScrollDirection ), + ParentSource( mPropertyOvershoot ), + OvershootOverlayPositionConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::VISIBLE, + ParentSource( mPropertyCanScrollVertical ), + OvershootOverlayVisibilityConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName()); + Actor self = Self(); + constraint = Constraint::New( effectOvershootPropertyIndex, + Source( self, mPropertyOvershoot ), + EqualToConstraint() ); + mOvershootEffect.ApplyConstraint(constraint); +} + +float ItemView::CalculateScrollOvershoot() +{ + float overshoot = 0.0f; + + if(mActiveLayout) + { + // The overshoot must be calculated from the accumulated pan gesture displacement + // since the pan gesture starts. + Actor self = Self(); + float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor(); + float positionDelta = self.GetProperty(mPropertyLayoutPosition) + scrollDistance; + float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize()); + self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); + float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta)); + overshoot = positionDelta - clamppedPosition; + } + + return overshoot; +} + +void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack) +{ + bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1; + + // make sure we animate back if needed + mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn); + + if( mAnimatingOvershootOn ) + { + // animating on, do not allow animate off + return; + } + + Actor self = Self(); + float currentOvershoot = self.GetProperty(mPropertyOvershoot); + float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)); + + RemoveAnimation(mScrollOvershootAnimation); + mScrollOvershootAnimation = Animation::New(duration); + mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished); + mScrollOvershootAnimation.AnimateTo( Property(self, mPropertyOvershoot), overshootAmount, TimePeriod(0.0f, duration) ); + mScrollOvershootAnimation.Play(); + + mAnimatingOvershootOn = animatingOn; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali 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 new file mode 100644 index 0000000..00262f2 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h @@ -0,0 +1,595 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class ItemView; + +typedef IntrusivePtr ItemViewPtr; + +/** + * ItemPool is container of Actor/ID pairs stored by ItemView + */ +class ItemPool +{ +public: // types + + typedef std::pair IDKeyPair; + typedef std::map IDKeyContainer; + typedef IDKeyContainer::iterator IDKeyIter; + typedef IDKeyContainer::const_iterator IDKeyConstIter; + + typedef std::pair ActorKeyPair; + typedef std::map ActorKeyContainer; + typedef ActorKeyContainer::iterator ActorKeyIter; + typedef ActorKeyContainer::const_iterator ActorKeyConstIter; + +public: // Construction & Destruction + + /** + * Constructor + * @param[in] itemView A reference to the ItemView class that owns this. + */ + ItemPool(ItemView& itemView) + : mItemView(itemView) + { + } + +public: // Methods + + /** + * Returns a const reference to the IDKeyContainer whose key is based on the Item ID. + * @return Const reference to IDKeyContainer. + */ + const IDKeyContainer& GetIDKeyContainer() const + { + return mIdKeyContainer; + } + + /** + * Returns a const reference to the ActorKeyContainer whose key is based on the Actor. + * @return Const reference to IDKeyContainer. + */ + const ActorKeyContainer& GetActorKeyContainer() const + { + return mActorKeyContainer; + } + + /** + * Add a range of items to the ItemPool, if they are not already in the ItemPool. + * @param[in] scrollingTowardsLast Should be true if scrolling towards the last item in the range. + * @param[in] range The range of Item IDs to associate with the new actors. + * @param[in] targetSize The current size of the layout. + */ + void AddItems(bool scrollingTowardsLast, ItemRange range, const Vector3& targetSize); + + /** + * Remove items from the ItemPool that are outside the given range. + * @param[in] range The range of required items. + */ + void RemoveItems(ItemRange range); + + /** + * Remove an item from the ItemPool. + * @param[in] itemId The item to remove. + * @return Whether the item is found and removed or not. + */ + bool RemoveItem(unsigned int itemId); + +private: + + /** + * Adds an item, if not already in the ItemPool. + * @param[in] itemId The item to add. + * @param[in] targetSize The target size of the layout. + */ + void AddItem(unsigned int itemId, const Vector3& targetSize); + +private: + + IDKeyContainer mIdKeyContainer; + ActorKeyContainer mActorKeyContainer; + ItemView& mItemView; +}; + +/** + * ItemView is a scrollable layout container. + * Multiple ItemLayouts may be provided, to determine the logical position of each item a layout. + * Actor-ID pairs are provided from a shared ItemFactory, to display the currently visible items. + */ +class ItemView : public Scrollable +{ +public: + + /** + * Create a new ItemView. + * @param[in] factory The factory which provides ItemView with items. + * @return A public handle to the newly allocated ItemView. + */ + static Dali::Toolkit::ItemView New(ItemFactory& factory); + + /** + * @copydoc Toolkit::ItemView::GetLayoutCount + */ + unsigned int GetLayoutCount() const; + + /** + * @copydoc Toolkit::ItemView::AddLayout + */ + void AddLayout(ItemLayout& layout); + + /** + * @copydoc Toolkit::ItemView::RemoveLayout + */ + void RemoveLayout(unsigned int layoutIndex); + + /** + * @copydoc Toolkit::ItemView::GetLayout + */ + ItemLayoutPtr GetLayout(unsigned int layoutIndex) const; + + /** + * @copydoc Toolkit::ItemView::GetActiveLayout + */ + ItemLayoutPtr GetActiveLayout() const; + + /** + * @copydoc Toolkit::ItemView::GetCurrentLayoutPosition + */ + float GetCurrentLayoutPosition(unsigned int itemId) const; + + /** + * @copydoc Toolkit::ItemView::ActivateLayout + */ + void ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds); + + /** + * @copydoc Toolkit::ItemView::DeactivateCurrentLayout + */ + void DeactivateCurrentLayout(); + + /** + * @copydoc Toolkit::ItemView::SetMinimumSwipeSpeed + */ + void SetMinimumSwipeSpeed(float speed); + + /** + * @copydoc Toolkit::ItemView::GetMinimumSwipeSpeed + */ + float GetMinimumSwipeSpeed() const; + + /** + * @copydoc Toolkit::ItemView::SetMinimumSwipeDistance + */ + void SetMinimumSwipeDistance(float distance); + + /** + * @copydoc Toolkit::ItemView::GetMinimumSwipeDistance + */ + float GetMinimumSwipeDistance() const; + + /** + * @copydoc Toolkit::ItemView::SetMouseWheelScrollDistanceStep + */ + void SetMouseWheelScrollDistanceStep(float step); + + /** + * @copydoc Toolkit::ItemView::GetMouseWheelScrollDistanceStep + */ + float GetMouseWheelScrollDistanceStep() const; + + /** + * @copydoc Toolkit::ItemView::SetAnchoring + */ + void SetAnchoring(bool enabled); + + /** + * @copydoc Toolkit::ItemView::GetAnchoring + */ + bool GetAnchoring() const; + + /** + * @copydoc Toolkit::ItemView::SetAnchoringDuration + */ + void SetAnchoringDuration(float durationSeconds); + + /** + * @copydoc Toolkit::ItemView::GetAnchoringDuration + */ + float GetAnchoringDuration() const; + + /** + * @copydoc Toolkit::ItemView::ScrollToItem + */ + void ScrollToItem(unsigned int itemId, float durationSeconds); + + /** + * @copydoc Toolkit::ItemView::SetRefreshInterval + */ + void SetRefreshInterval(unsigned int intervalMilliseconds); + + /** + * @copydoc Toolkit::ItemView::GetRefreshInterval + */ + unsigned int GetRefreshInterval() const; + + /** + * @copydoc Toolkit::ItemView::GetItem + */ + Actor GetItem(unsigned int itemId) const; + + /** + * @copydoc Toolkit::ItemView::GetItemId + */ + unsigned int GetItemId(Actor actor) const; + + /** + * @copydoc Toolkit::ItemView::RemoveItem + */ + void RemoveItem(unsigned int itemId, float durationSeconds); + + /** + * @copydoc Toolkit::Scrollable::GetDomainSize + */ + Vector3 GetDomainSize() const; + + /** + * @copydoc Toolkit::Scrollable::GetCurrentScrollPosition + */ + Vector3 GetCurrentScrollPosition() const; + + /** + * @copydoc Toolkit::Scrollable::AddOverlay() + */ + void AddOverlay(Actor actor); + + /** + * @copydoc Toolkit::Scrollable::RemoveOverlay() + */ + void RemoveOverlay(Actor actor); + + /** + * @copydoc Toolkit::Scrollable::ScrollTo(const Vector3& position, float duration) + */ + void ScrollTo(const Vector3& position, float duration); + +public: // Used By ItemPool + + /** + * Called by the ItemPool when it wishes to create an actor from the ItemFactory with the + * specified ItemId. + * @param[in] itemId The Item ID of the actor to create. + */ + Actor CreateActor(unsigned int itemId); + + /** + * Called by the ItemPool when an actor is added to the ItemPool. + * @param[in] actor The actor that has been added. + * @param[in] itemId The associated Item ID. + * @param[in] targetSize The target size that was specified upon addition. + */ + void ActorAddedToItemPool(Actor actor, unsigned int itemId, const Vector3& targetSize); + + /** + * Called by the ItemPool when an actor with the specified itemId is about to be removed from the + * ItemPool. + * @param[in] actor The actor about to be removed. + * @param[in] itemId The associated Item ID. + */ + void ActorRemovedFromItemPool(Actor actor, unsigned int itemId); + +private: // From CustomActorImpl + + /** + * From CustomActorImpl; called after a touch-signal is received by the owning actor. + * @param[in] event The touch event. + * @return True if the event should be consumed. + */ + virtual bool OnTouchEvent(const TouchEvent& event); + + /** + * From CustomActorImpl; called after a mouse-wheel-event is received by the owning actor. + * @param[in] event The mouse wheel event. + * @return True if the event should be consumed. + */ + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnAccessibilityPan() + */ + virtual bool OnAccessibilityPan(PanGesture gesture); + + /** + * @copydoc Toolkit::Control::GetNextKeyboardFocusableActor() + */ + virtual Actor GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + + /** + * @copydoc Toolkit::Control::OnKeyboardFocusChangeCommitted() + */ + virtual void OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor); + +protected: + + /** + * Construct a new ItemView. + * @param[in] factory The factory which provides ItemView with items. + */ + ItemView(ItemFactory& factory); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ItemView(); + +private: + + // Undefined + ItemView(const ItemView&); + + // Undefined + ItemView& operator=(const ItemView& rhs); + + /** + * Helper to apply constraints to an actor. + * @param[in] actor The actor to constrain. + * @param[in] layout The active layout. + * @param[in] itemId The ID of the item represented by the actor. + * @param[in] durationSeconds The time taken to fully constrain the actors. + */ + void ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float durationSeconds); + + /** + * Helper to remove items outside a given range. + * @param[in] range The range of required items. + */ + void RemoveItems(ItemRange range); + + /** + * Helper to add a range of items, if not already in the ItemPool. + * @param[in] layout The layout used to position the new items. + * @param[in] layoutSize The current size of the layout. + * @param[in] range The range of required items. + */ + void AddItems(ItemLayout& layout, const Vector3& layoutSize, ItemRange range); + + /** + * Helper to find the range of items to populate with. + * @param[in] layout The current layout. + * @param[in] range The range of items. + * @param[in] reserveExtra True if reserve items should be included. + */ + ItemRange GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra); + + // Input Handling + + /** + * Helper to handle pressed (Down) events. + * @param[in] x The X coordinate of the touch event. + * @param[in] y The Y coordinate of the touch event. + * @param[in] timeMs The time-stamp of the touch event. + */ + void OnPressed(float x, float y, unsigned long timeMs); + + /** + * Helper to clamp the first-item position when dragging/swiping. + * @param[in] targetPosition The target position of the drag etc. + * @param[in] targetSize The target ItemView & layout size. + * @param[in] layout The current layout. + * @return The clamped first-item position. + */ + float ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout); + + /** + * Called upon pan gesture event. + * + * @param[in] gesture The gesture event. + */ + void OnPan(PanGesture pan); + + /** + * Helper to handle anchoring animations. + * @return The animation, or an uninitialized handle if not necessary. + */ + Animation DoAnchoring(); + + /** + * Callback from scroll animations + * @param[in] animation The scroll-animation which has finished. + */ + void OnScrollFinished(Animation& animation); + + /** + * Called by animation system when overshoot has finished animating to maximum (either -1.0f or 1.0f) + * + * @param[in] animation the animation that has finished + */ + void OnOvershootOnFinished(Animation& animation); + + /** + * Helper to start the refresh timer. + */ + void StartRefreshTimer(); + + /** + * Helper to cancel the refresh timer. + */ + void CancelRefreshTimer(); + + /** + * Refresh the ItemView; this is called after a timeout when scrolling. + * During a refresh, ItemFactory::NewItem() will be called to create newly visible items. + * @return True if the refresh timer should be kept running. + */ + bool OnRefreshTick(); + + /** + * This is called after a timeout when no new mouse wheel event is received for a certain period of time. + * @return will return false; one-shot timer. + */ + bool OnMouseWheelEventFinished(); + + /** + * Stops and removes animation if exists. + * @param[in,out] animation The animation handle to be removed. + */ + void RemoveAnimation(Animation& animation); + + /** + * Helper to apply constraints to the overshoot overlay actor. + */ + void ApplyOvershootOverlayConstraints(); + + /** + * Helper to calculate the scroll overshoot according to the pan gesture displacement. + * @return The scroll overshoot. + */ + float CalculateScrollOvershoot(); + + /** + * Helper to calculate the scroll overshoot according to the pan gesture displacement. + * + * @param[in] overshootAmount amount to animate to + * @param[in] animateBack whether to animate back to zero immediately after + * @return The scroll overshoot. + */ + void AnimateScrollOvershoot(float overshootAmount, bool animateBack = false); + + /** + * Gets the scroll position in pixels according to the logical layout position. + * @param[in] layoutSize The current size of the layout. + */ + float GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const; + + /** + * Calculates the minimum and maximum positions for each axis to scroll to. + * @param[in] layoutSize The current size of the layout. + */ + void CalculateDomainSize(const Vector3& layoutSize); + + + /** + * Calculates whether we want to allow current item view to scroll. + * @param[in] layoutSize The current size of the layout. + * @return true if itemview is scrollable + */ + bool IsLayoutScrollable(const Vector3& layoutSize); + +private: + + ItemFactory& mItemFactory; + + ItemPool mItemPool; + + ItemLayoutContainer mLayouts; + ItemLayout* mActiveLayout; + + Animation mResizeAnimation; + Animation mScrollAnimation; + Animation mScrollSpeedAnimation; + Animation mScrollOvershootAnimation; + bool mAnimatingOvershootOn; ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off) + bool mAnimateOvershootOff; ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off) + + bool mAnchoringEnabled; + float mAnchoringDuration; + + Timer mRefreshTimer; + int mRefreshIntervalMilliseconds; + bool mRefreshOrderHint; ///< True if scrolling towards the last item + + // Input handling + + float mMinimumSwipeSpeed; + float mMinimumSwipeDistance; + float mMouseWheelScrollDistanceStep; ///< The step of scroll distance in actor coordinates for each mouse wheel event received. + + float mScrollDistance; + float mScrollSpeed; + Vector2 mTotalPanDisplacement; + + float mScrollOvershoot; + bool mIsFlicking; + + Timer mMouseWheelEventFinishedTimer; ///< The timer to determine whether there is no mouse wheel event received for a certain period of time. + + Dali::Gesture::State mGestureState; + + ImageActor mOvershootOverlay; ///< The overlay actor for overshoot effect + OvershootRippleEffect mOvershootEffect; ///< The vertex/fragment shader used to display the overshoot ripple effect + + Property::Index mPropertyLayoutPosition; ///< The logical position of the first item within the layout + Property::Index mPropertyPosition; ///< The physical position of the first item within the layout + Property::Index mPropertyMinimumLayoutPosition; ///< The minimum valid layout position in the layout. + Property::Index mPropertyScrollSpeed; ///< The current scroll speed of item view + Property::Index mPropertyOvershoot; ///< The scroll overshoot (difference of the layout position before and after clamping) +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ItemView& GetImpl(Toolkit::ItemView& itemView) +{ + DALI_ASSERT_ALWAYS(itemView); + + Dali::RefObject& handle = itemView.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::ItemView& GetImpl(const Toolkit::ItemView& itemView) +{ + DALI_ASSERT_ALWAYS(itemView); + + const Dali::RefObject& handle = itemView.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.cpp new file mode 100644 index 0000000..bcaf8e1 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.cpp @@ -0,0 +1,158 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ScrollBase +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string ScrollBase::SCROLL_DOMAIN_OFFSET_PROPERTY_NAME( "domain-offset" ); + +ScrollBase::ScrollBase() +: Scrollable(), + mParent(NULL), + mPropertyTime(Property::INVALID_INDEX), + mPropertyX(Property::INVALID_INDEX), + mPropertyY(Property::INVALID_INDEX), + mPropertyPrePosition(Property::INVALID_INDEX), + mPropertyPosition(Property::INVALID_INDEX), + mPropertyScale(Property::INVALID_INDEX), + mPropertyOvershootX(Property::INVALID_INDEX), + mPropertyOvershootY(Property::INVALID_INDEX), + mPropertyWrap(Property::INVALID_INDEX), + mPropertyPanning(Property::INVALID_INDEX), + mPropertyScrolling(Property::INVALID_INDEX), + mPropertyFinal(Property::INVALID_INDEX), + mPropertyDomainOffset(Property::INVALID_INDEX), + mPropertyPositionDelta(Property::INVALID_INDEX), + mPropertyScrollStartPagePosition(Property::INVALID_INDEX), + mDelay(0.0f) +{ +} + +void ScrollBase::SetParent(ScrollBase *parent) +{ + mParent = parent; +} + +void ScrollBase::RegisterProperties() +{ + Actor self = Self(); + + // Register common properties + RegisterCommonProperties(); + + // Register Scroll Properties. + mPropertyTime = self.RegisterProperty(Toolkit::ScrollView::SCROLL_TIME_PROPERTY_NAME, 0.0f); + mPropertyPrePosition = self.RegisterProperty(Toolkit::ScrollView::SCROLL_PRE_POSITION_PROPERTY_NAME, Vector3::ZERO); + mPropertyPosition = self.RegisterProperty(Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME, Vector3::ZERO); + mPropertyOvershootX = self.RegisterProperty(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME, 0.0f); + mPropertyOvershootY = self.RegisterProperty(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME, 0.0f); + mPropertyFinal = self.RegisterProperty(Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME, Vector3::ZERO); + mPropertyX = self.RegisterProperty(Toolkit::ScrollView::SCROLL_X_PROPERTY_NAME, 0.0f); + mPropertyY = self.RegisterProperty(Toolkit::ScrollView::SCROLL_Y_PROPERTY_NAME, 0.0f); + mPropertyScale = self.RegisterProperty(Toolkit::ScrollView::SCROLL_SCALE_PROPERTY_NAME, Vector3::ONE); + mPropertyWrap = self.RegisterProperty(Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME, false); + mPropertyPanning = self.RegisterProperty(Toolkit::ScrollView::SCROLL_PANNING_PROPERTY_NAME, false); + mPropertyScrolling = self.RegisterProperty(Toolkit::ScrollView::SCROLL_SCROLLING_PROPERTY_NAME, false); + mPropertyDomainOffset = self.RegisterProperty(SCROLL_DOMAIN_OFFSET_PROPERTY_NAME, Vector3::ZERO); + mPropertyPositionDelta = self.RegisterProperty(Toolkit::ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME, Vector3::ZERO); + mPropertyScrollStartPagePosition = self.RegisterProperty(Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME, Vector3::ZERO); +} + +void ScrollBase::BindActor(Actor child) +{ + FindAndUnbindActor(child); + + ActorInfoPtr actorInfo(new ActorInfo(child)); + mBoundActors.push_back(actorInfo); + + // Apply all our constraints to this new child. + ConstraintStack::iterator i; + + for(i = mConstraintStack.begin();i!=mConstraintStack.end();i++) + { + actorInfo->ApplyConstraint(*i); + } +} + +void ScrollBase::UnbindActor(Actor child) +{ + // Find the child in mBoundActors, and unparent it + for (ActorInfoIter iter = mBoundActors.begin(); iter != mBoundActors.end(); ++iter) + { + ActorInfoPtr actorInfo = *iter; + + if( actorInfo->mActor == child ) + { + mBoundActors.erase(iter); + break; + } + } +} + +void ScrollBase::FindAndUnbindActor(Actor child) +{ + // Since we don't know if and where child may have been bound + // (as we cannot store such information inside the Actor), we + // perform a search on all associated ScrollBases + // This is done by recursively calling the parent of this ScrollBase + // until reaching the top (at which point implementation may be + // different as this is virtual) + + if(mParent) // continuously ascend until reaches root ScrollBase. + { + mParent->FindAndUnbindActor(child); + } +} + +void ScrollBase::ApplyConstraintToBoundActors(Constraint constraint) +{ + mConstraintStack.push_back(constraint); + + for(ActorInfoIter i = mBoundActors.begin();i != mBoundActors.end(); ++i) + { + (*i)->ApplyConstraint(constraint); + } +} + +void ScrollBase::RemoveConstraintsFromBoundActors() +{ + mConstraintStack.clear(); + + for(ActorInfoIter i = mBoundActors.begin();i != mBoundActors.end(); ++i) + { + (*i)->RemoveConstraints(); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.h new file mode 100644 index 0000000..5dd0a3f --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-base-impl.h @@ -0,0 +1,230 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_BASE_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_BASE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 +// TODO - Replace list with dali-vector.h +#include + +// INTERNAL INCLUDES +#include + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class ScrollBase; + +typedef IntrusivePtr ActorPtr; +typedef std::list ConstraintStack; + +/** + * ScrollBase represents a set of properties (time, position + * scale etc.) that constrain a set of actors. + */ +class ScrollBase : public Scrollable +{ +public: + + struct ActorInfo : public Dali::RefObject + { + /** + * ActorInfo constructor + * @param[in] actor The actor that this ActorInfo represents. + */ + ActorInfo(Actor actor) + : mActor(actor) + { + } + + /** + * ActorInfo destructor + * removes scrollview-related constraints only. + */ + ~ActorInfo() + { + RemoveConstraints(); + } + + /** + * Apply a constraint to this actor + * The constraint will be applied to the actor, + * and the ActorInfo will keep track of this constraint. + * @param[in] constraint The constraint to apply to the actor + */ + void ApplyConstraint(Constraint constraint) + { + ActiveConstraint activeConstraint = mActor.ApplyConstraint( constraint ); + mConstraints.push_back( activeConstraint ); + } + + /** + * Remove constraints from this actor. + * All of the constraints that have been applied to the + * actor via this ActorInfo will be removed. + */ + void RemoveConstraints() + { + std::vector::iterator it = mConstraints.begin(); + std::vector::iterator end = mConstraints.end(); + for(;it!=end;++it) + { + mActor.RemoveConstraint(*it); + } + mConstraints.clear(); + } + + Actor mActor; ///< The Actor that this ActorInfo represents. + std::vector mConstraints; ///< A list keeping track of constraints applied to the actor via this delegate. + }; + + typedef IntrusivePtr ActorInfoPtr; + typedef std::vector ActorInfoContainer; + typedef ActorInfoContainer::iterator ActorInfoIter; + typedef ActorInfoContainer::const_iterator ActorInfoConstIter; + +public: + + /** + * Sets the delay in seconds. + * This delay affects the animation timing for all + * Bound Actors. + * + * @param[in] delay The delay in seconds. + */ + void SetDelay(float delay) + { + mDelay = delay; + } + + /** + * Gets the current delay in seconds. + * + * @return The delay in seconds. + */ + float GetDelay() const + { + return mDelay; + } + +public: + + /** + * Sets ScrollBase Parent + * + * @param[in] parent The parent that this ScrollBase belongs to. + */ + void SetParent(ScrollBase *parent); + + /** + * Bind Actor to this scroll view/group. + * Once Bound, this scroll view/group will affect the actor (child) + * + * @param[in] child The actor to be bound. + */ + void BindActor(Actor child); + + /** + * Unbind Actor from this scroll view/group + * Once Unbound, this scroll view/group will not affect the actor + * + * @note this does not remove the child from the ScrollView container + * + * @param[in] child The actor to be unbound + */ + void UnbindActor(Actor child); + + /** + * Searches associated ScrollBases for the Actor, and attempts to Unbind + * systematically this Actor from the ScrollView or Groups attached. + * + * @param[in] child The actor to be unbound. + */ + virtual void FindAndUnbindActor(Actor child); + + /** + * Applies constraint to the bound actors within this ScrollView/Group only. + * + * @param[in] constraint, the constraint to apply to these bound actors and future + * ones. + */ + void ApplyConstraintToBoundActors(Constraint constraint); + + /** + * Removes all constraints from the bound actors within this ScrollView/Group only. + */ + void RemoveConstraintsFromBoundActors(); + +protected: + + static const std::string SCROLL_DOMAIN_OFFSET_PROPERTY_NAME; + +protected: + + /** + * Construct a new ScrollBase. + */ + ScrollBase(); + + /** + * 2nd-phase initialization. + */ + void RegisterProperties(); + +protected: + + ScrollBase *mParent; ///< Pointer to ScrollBase parent, if exists. + Property::Index mPropertyTime; ///< Scroll Time (0 to animationDuration while animating, otherwise 0) + Property::Index mPropertyX; ///< Scroll Position X ("scroll-x") + Property::Index mPropertyY; ///< Scroll Position Y ("scroll-y") + Property::Index mPropertyPrePosition; ///< Scroll Position ("scroll-position") [function of scroll-x, scroll-y] + Property::Index mPropertyPosition; ///< Scroll Position ("scroll-position") [function of scroll-pre-position] + Property::Index mPropertyScale; ///< Scroll Scale ("scroll-scale") + Property::Index mPropertyOvershootX; ///< Scroll Overshoot ("scroll-overshoot-x") [function of scroll-pre-position, scroll-position] + Property::Index mPropertyOvershootY; ///< Scroll Overshoot ("scroll-overshoot-y") [function of scroll-pre-position, scroll-position] + Property::Index mPropertyWrap; ///< Scroll Wrap ("scroll-wrap") + Property::Index mPropertyPanning; ///< Whether we are panning + Property::Index mPropertyScrolling; ///< Whether we are scrolling + Property::Index mPropertyFinal; ///< Scroll Final Position ("scroll-final") [scroll-position + f(scroll-overshoot)] + Property::Index mPropertyDomainOffset; ///< Scroll Domain Offset ("scroll-domain-offset") keeps track of scroll position as it wraps domains + Property::Index mPropertyPositionDelta; ///< Scroll Position Delta ("scroll-position-delta") + Property::Index mPropertyScrollStartPagePosition; ///< Scroll Start Page Position ("scroll-start-page-position") + +private: + + float mDelay; ///< delay in seconds. + ConstraintStack mConstraintStack; ///< The list of constraints to apply to any actors + ActorInfoContainer mBoundActors; ///< The list of actors that have been bound to this ScrollBase. + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_GROUP_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp new file mode 100644 index 0000000..4df873c --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp @@ -0,0 +1,629 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +// EXTERNAL INCLUDES +#include + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +const char* OVERSHOOT_OVERLAY_IMAGE_PATH = DALI_IMAGE_DIR "scroll_overshoot.png"; +const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png"; +const float DEFAULT_MAX_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels +const Rect OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 ); +const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.35f; // time in seconds + +ScrollOvershootIndicator::ScrollOvershootIndicator(Scrollable& scrollable) : + mScrollable(scrollable), + mEffectX(NULL), + mEffectY(NULL) +{ +} + +ScrollOvershootIndicator::~ScrollOvershootIndicator() +{ + +} + +ScrollOvershootIndicator* ScrollOvershootIndicator::New(Scrollable& scrollable) +{ + ScrollOvershootIndicator* scrollOvershootPtr = new ScrollOvershootIndicator(scrollable); + return scrollOvershootPtr; +} + +void ScrollOvershootIndicator::Enable(bool enable) +{ + if(enable) + { + Actor scrollableActor = mScrollable.Self(); + if(!mEffectX) + { + mEffectX = ScrollOvershootEffectRipple::New(false); + } + mEffectX->Apply(mScrollable); + if(!mEffectY) + { + mEffectY = ScrollOvershootEffectRipple::New(true); + } + mEffectY->Apply(mScrollable); + mEffectY->SetPropertyNotifications(scrollableActor); + } + else + { + if(mEffectX) + { + mEffectX->Remove(mScrollable); + } + if(mEffectY) + { + mEffectY->Remove(mScrollable); + } + } +} + +void ScrollOvershootIndicator::Reset() +{ + mEffectX->Reset(); + mEffectY->Reset(); +} + +ScrollOvershootEffect::ScrollOvershootEffect(bool vertical) : + mVertical(vertical) +{ + +} + +ScrollOvershootEffectGradient::ScrollOvershootEffectGradient(bool vertical) : + ScrollOvershootEffect(vertical), + mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT) +{ + Image overshootImage = Image::New( OVERSHOOT_OVERLAY_IMAGE_PATH ); + mOvershootImage = ImageActor::New( overshootImage ); + mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT); + mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mOvershootImage.SetDrawMode(DrawMode::OVERLAY); +} + +void ScrollOvershootEffectGradient::Apply(Scrollable& scrollable) +{ + Actor scrollableActor = scrollable.Self(); + int overshootXPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME); + int overshootYPropertyIndex = scrollableActor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME); + + Constraint constraint = Constraint::New( Actor::SIZE, + Source( scrollableActor, overshootXPropertyIndex ), + Source( scrollableActor, overshootYPropertyIndex ), + Source( scrollableActor, Actor::SIZE ), + boost::bind( &ScrollOvershootEffectGradient::SizeConstraint, this, _1, _2, _3, _4) ); + mSizeConstraint = mOvershootImage.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::ROTATION, + Source( scrollableActor, overshootXPropertyIndex ), + Source( scrollableActor, overshootYPropertyIndex ), + boost::bind( &ScrollOvershootEffectGradient::RotationConstraint, this, _1, _2, _3) ); + mRotationConstraint = mOvershootImage.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::POSITION, + Source( scrollableActor, Actor::SIZE ), + Source( scrollableActor, overshootXPropertyIndex ), + Source( scrollableActor, overshootYPropertyIndex ), + boost::bind( &ScrollOvershootEffectGradient::PositionConstraint, this, _1, _2, _3, _4) ); + mPositionConstraint = mOvershootImage.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::VISIBLE, + Source( scrollableActor, IsVertical() ? scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL) : scrollableActor.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL)), + boost::bind( &ScrollOvershootEffectGradient::VisibilityConstraint, this, _1, _2) ); + mVisibilityConstraint = mOvershootImage.ApplyConstraint(constraint); + scrollable.AddOverlay(mOvershootImage); + SetPropertyNotifications(scrollableActor); +} + +void ScrollOvershootEffectGradient::Remove(Scrollable& scrollable) +{ + if(mOvershootImage) + { + if(mSizeConstraint) + { + mOvershootImage.RemoveConstraint(mSizeConstraint); + mSizeConstraint = NULL; + } + if(mRotationConstraint) + { + mOvershootImage.RemoveConstraint(mRotationConstraint); + mRotationConstraint = NULL; + } + if(mPositionConstraint) + { + mOvershootImage.RemoveConstraint(mPositionConstraint); + mPositionConstraint = NULL; + } + if(mVisibilityConstraint) + { + mOvershootImage.RemoveConstraint(mVisibilityConstraint); + mVisibilityConstraint = NULL; + } + scrollable.RemoveOverlay(mOvershootImage); + } +} + +Vector3 ScrollOvershootEffectGradient::SizeConstraint(const Vector3& current, + const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY, + const PropertyInput& parentSizeProperty) +{ + float overshootx = overshootPropertyX.GetFloat(); + float overshooty = overshootPropertyY.GetFloat(); + const Vector3 parentSize = parentSizeProperty.GetVector3(); + + float overlayWidth = IsVertical() ? parentSize.x : parentSize.y; + float overlayHeight = mMaxOvershootImageSize * fabsf(IsVertical() ? overshooty : overshootx); + + return Vector3(overlayWidth, overlayHeight, current.z); +} + +Quaternion ScrollOvershootEffectGradient::RotationConstraint(const Quaternion& current, + const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY) +{ + float overshootx = overshootPropertyX.GetFloat(); + float overshooty = overshootPropertyY.GetFloat(); + + Quaternion rotation; + + if(IsVertical()) + { + if(overshooty < -Math::MACHINE_EPSILON_0) + { + rotation = Quaternion(Math::PI, Vector3::ZAXIS); + } + else if(overshooty > Math::MACHINE_EPSILON_0) + { + rotation = Quaternion(0.0f, Vector3::ZAXIS); + } + } + else + { + if(overshootx < -Math::MACHINE_EPSILON_0) + { + rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } + else if(overshootx > Math::MACHINE_EPSILON_0) + { + rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + } + } + + return rotation; +} + +Vector3 ScrollOvershootEffectGradient::PositionConstraint(const Vector3& current, + const PropertyInput& parentSizeProperty, + const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY) +{ + float overshootx = overshootPropertyX.GetFloat(); + float overshooty = overshootPropertyY.GetFloat(); + const Vector3 parentSize = parentSizeProperty.GetVector3(); + + Vector3 relativeOffset = Vector3::ZERO; + + if(IsVertical()) + { + if(overshooty > Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } + else if (overshooty < -Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + } + else + { + if(overshootx > Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + } + else if (overshootx < -Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } + } + + return relativeOffset * parentSize; +} + +bool ScrollOvershootEffectGradient::VisibilityConstraint(const bool& current, + const PropertyInput& canScrollProperty) +{ + return canScrollProperty.GetBoolean(); +} + +ScrollOvershootEffectGradientPtr ScrollOvershootEffectGradient::New(bool vertical) +{ + return new ScrollOvershootEffectGradient(vertical); +} + +namespace +{ + +const std::string OVERSHOOT_PROPERTY_NAME( "uOvershoot" ); +const std::string OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME( "uOvershootImageCount" ); + +} // namespace + +OvershootRippleEffect::OvershootRippleEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +OvershootRippleEffect::OvershootRippleEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +OvershootRippleEffect::~OvershootRippleEffect() +{ +} + +OvershootRippleEffect OvershootRippleEffect::New() +{ + std::string vertextShader( + "precision mediump float; \n" + "uniform float uOvershoot; \n" + "uniform float uOvershootImageCount; \n" + "void main() \n" + "{ \n" + " gl_Position = uProjection * uModelView * vec4(aPosition, 1.0); \n" + " vTexCoord = aTexCoord; \n" + " vTexCoord.y += (1.0 / uOvershootImageCount) * min(floor((abs(uOvershoot) * (uOvershootImageCount - 1.0)) + 0.5), (uOvershootImageCount - 1.0)); \n" + "} \n" ); + + std::string fragmentShader( + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(sTexture, vTexCoord); \n" + "} \n" ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + vertextShader, + fragmentShader); + + /* Pass ownership to OvershootRippleEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + OvershootRippleEffect handle( shaderEffectCustom ); + handle.SetUniform(OVERSHOOT_PROPERTY_NAME, 0.0f); + handle.SetUniform(OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, 10.0f); + return handle; +} + +void OvershootRippleEffect::SetOvershoot( float overshoot ) +{ + SetUniform( OVERSHOOT_PROPERTY_NAME, overshoot ); +} + +void OvershootRippleEffect::SetOvershootImageCount( float imageCount ) +{ + SetUniform( OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME, imageCount ); +} + +const std::string& OvershootRippleEffect::GetOvershootPropertyName() const +{ + return OVERSHOOT_PROPERTY_NAME; +} + +const std::string& OvershootRippleEffect::GetOvershootImageCountPropertyName() const +{ + return OVERSHOOT_IMAGE_COUNT_PROPERTY_NAME; +} + +ScrollOvershootEffectRipple::ScrollOvershootEffectRipple(bool vertical) : + ScrollOvershootEffect(vertical), + mMaxOvershootImageSize(DEFAULT_MAX_OVERSHOOT_HEIGHT) +{ + mRippleEffect = OvershootRippleEffect::New(); + Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH ); + mOvershootImage = ImageActor::New( overshootImage ); + mOvershootImage.SetParentOrigin(ParentOrigin::TOP_LEFT); + mOvershootImage.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mOvershootImage.SetDrawMode(DrawMode::OVERLAY); + mOvershootImage.SetShaderEffect(mRippleEffect); + mOvershootImage.SetPixelArea(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA); + mOvershootImage.SetVisible(false); + mAnimatingOvershootOn = false; + mAnimateOvershootOff = false; +} + +void ScrollOvershootEffectRipple::Apply(Scrollable& scrollable) +{ + Actor scrollableActor = scrollable.Self(); + + // make sure height is set, since we only create a constraint for image width + mOvershootImage.SetSize(OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_RIPPLE_IMAGE_1_PIXEL_AREA.height); + + UpdateConstraints(scrollableActor); + scrollable.AddOverlay(mOvershootImage); + + SetPropertyNotifications(scrollableActor); +} + +void ScrollOvershootEffectRipple::Remove(Scrollable& scrollable) +{ + if(mOvershootImage) + { + if(mSizeConstraint) + { + mOvershootImage.RemoveConstraint(mSizeConstraint); + mSizeConstraint = NULL; + } + if(mPositionConstraint) + { + mOvershootImage.RemoveConstraint(mPositionConstraint); + mPositionConstraint = NULL; + } + scrollable.RemoveOverlay(mOvershootImage); + } +} + +void ScrollOvershootEffectRipple::Reset() +{ + mAnimatingOvershootOn = false; + mAnimateOvershootOff = false; + mOvershootImage.SetVisible(false); + mRippleEffect.SetUniform(mRippleEffect.GetOvershootPropertyName(), 0.0f); + if(mScrollOvershootAnimation) + { + mScrollOvershootAnimation.Clear(); + mScrollOvershootAnimation.Reset(); + } +} + +void ScrollOvershootEffectRipple::UpdateConstraints(Actor& scrollable) +{ + int overshootPropertyIndex = mRippleEffect.GetPropertyIndex(mRippleEffect.GetOvershootPropertyName()); + Constraint constraint; + if(!mSizeConstraint) + { + constraint = Constraint::New( Actor::SIZE_WIDTH, + Source( scrollable, IsVertical() ? Actor::SIZE_WIDTH : Actor::SIZE_HEIGHT), + EqualToConstraint() ); + mSizeConstraint = mOvershootImage.ApplyConstraint(constraint); + } + + if(!mPositionConstraint) + { + constraint = Constraint::New( Actor::POSITION, + Source( scrollable, Actor::SIZE ), + Source( mRippleEffect, overshootPropertyIndex ), + boost::bind( &ScrollOvershootEffectRipple::PositionConstraint, this, _1, _2, _3) ); + mPositionConstraint = mOvershootImage.ApplyConstraint(constraint); + } +} + +void ScrollOvershootEffectRipple::SetPropertyNotifications(Actor& scrollable) +{ + int overshootXPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME); + int overshootYPropertyIndex = scrollable.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME); + mCanScrollPropertyIndex = scrollable.GetPropertyIndex(IsVertical() ? Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL : Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL); + if(scrollable.OnStage()) + { + if(!mOvershootNegativeNotification) + { + mOvershootNegativeNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, LessThanCondition(-Math::MACHINE_EPSILON_1)); + mOvershootNegativeNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged); + mOvershootNegativeNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnNegativeOvershootNotification); + } + + if(!mOvershootPositiveNotification) + { + mOvershootPositiveNotification = scrollable.AddPropertyNotification(IsVertical() ? overshootYPropertyIndex : overshootXPropertyIndex, GreaterThanCondition(Math::MACHINE_EPSILON_1)); + mOvershootPositiveNotification.SetNotifyMode(PropertyNotification::NotifyOnChanged); + mOvershootPositiveNotification.NotifySignal().Connect(this, &ScrollOvershootEffectRipple::OnPositiveOvershootNotification); + } + } +} + +Vector3 ScrollOvershootEffectRipple::PositionConstraint(const Vector3& current, + const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty) +{ + float overshoot = overshootProperty.GetFloat(); + const Vector3 parentSize = parentSizeProperty.GetVector3(); + + Vector3 relativeOffset = Vector3::ZERO; + + if(IsVertical()) + { + if(overshoot > Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } + else if (overshoot < -Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + } + else + { + if(overshoot > Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + } + else if (overshoot < -Math::MACHINE_EPSILON_0) + { + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } + } + + return relativeOffset * parentSize; +} + +void ScrollOvershootEffectRipple::OnPositiveOvershootNotification(PropertyNotification& source) +{ + Actor delegate = Actor::DownCast(source.GetTarget()); + float overshoot = delegate.GetProperty(source.GetTargetProperty()); + bool canScroll = delegate.GetProperty(mCanScrollPropertyIndex); + if(!canScroll) + { + mOvershootImage.SetVisible(false); + return; + } + mOvershootImage.SetVisible(true); + + if (fabsf(overshoot) < Math::MACHINE_EPSILON_1) + { + AnimateScrollOvershoot(0.0f); + return; + } + if(overshoot > 0.0f) + { + const Vector3 imageSize = mOvershootImage.GetCurrentSize(); + Vector3 relativeOffset = Vector3::ZERO; + const Vector3 parentSize = delegate.GetCurrentSize(); + AnimateScrollOvershoot(1.0f); + if(IsVertical()) + { + mOvershootImage.SetRotation(Quaternion(0.0f, Vector3::ZAXIS)); + mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth); + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } + else + { + mOvershootImage.SetRotation(Quaternion(1.5f * Math::PI, Vector3::ZAXIS)); + mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth); + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + } + mOvershootImage.SetPosition(relativeOffset * parentSize); + } +} + +void ScrollOvershootEffectRipple::OnNegativeOvershootNotification(PropertyNotification& source) +{ + Actor delegate = Actor::DownCast(source.GetTarget()); + float overshoot = delegate.GetProperty(source.GetTargetProperty()); + bool canScroll = delegate.GetProperty(mCanScrollPropertyIndex); + if(!canScroll) + { + mOvershootImage.SetVisible(false); + return; + } + mOvershootImage.SetVisible(true); + + if (fabsf(overshoot) < Math::MACHINE_EPSILON_1) + { + AnimateScrollOvershoot(0.0f); + return; + } + + if(overshoot < 0.0f) + { + const Vector3 imageSize = mOvershootImage.GetCurrentSize(); + Vector3 relativeOffset = Vector3::ZERO; + const Vector3 parentSize = delegate.GetCurrentSize(); + AnimateScrollOvershoot(-1.0f); + if(IsVertical()) + { + mOvershootImage.SetRotation(Quaternion(Math::PI, Vector3::ZAXIS)); + mOvershootImage.SetSize(parentSize.width, imageSize.height, imageSize.depth); + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + else + { + mOvershootImage.SetRotation(Quaternion(0.5f * Math::PI, Vector3::ZAXIS)); + mOvershootImage.SetSize(parentSize.height, imageSize.height, imageSize.depth); + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } + mOvershootImage.SetPosition(relativeOffset * parentSize); + } +} + +void ScrollOvershootEffectRipple::AnimateScrollOvershoot(float overshootAmount) +{ + bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1; + + // make sure we animate back if needed + mAnimateOvershootOff = (!animatingOn && mAnimatingOvershootOn); + + int overShootProperty = mRippleEffect.GetPropertyIndex(mRippleEffect.GetOvershootPropertyName()); + float currentOvershoot = mRippleEffect.GetProperty(overShootProperty); + if(((currentOvershoot < 0.0f && overshootAmount > 0.0f) + || (currentOvershoot > 0.0f && overshootAmount < 0.0f))) + { + // cancel current animation + mAnimatingOvershootOn = false; + // reset currentOvershoot to 0.0f + mRippleEffect.SetProperty(overShootProperty, 0.0f); + currentOvershoot = 0.0f; + } + if( mAnimatingOvershootOn ) + { + // animating on in same direction, do not allow animate off + return; + } + float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)); + + if(mScrollOvershootAnimation) + { + mScrollOvershootAnimation.Clear(); + mScrollOvershootAnimation.Reset(); + } + mScrollOvershootAnimation = Animation::New(duration); + mScrollOvershootAnimation.FinishedSignal().Connect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished); + mScrollOvershootAnimation.AnimateTo( Property(mRippleEffect, overShootProperty), overshootAmount, TimePeriod(0.0f, duration) ); + mScrollOvershootAnimation.Play(); + + mOvershootImage.SetVisible(true); + + mAnimatingOvershootOn = animatingOn; +} + +void ScrollOvershootEffectRipple::OnOvershootAnimFinished(Animation& animation) +{ + if(!mAnimatingOvershootOn && !mAnimateOvershootOff) + { + // just finished animating overshoot to 0 + mOvershootImage.SetVisible(false); + } + mAnimatingOvershootOn = false; + mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollOvershootEffectRipple::OnOvershootAnimFinished); + mScrollOvershootAnimation.Clear(); + mScrollOvershootAnimation.Reset(); + if(mAnimateOvershootOff) + { + AnimateScrollOvershoot(0.0f); + } +} + +ScrollOvershootEffectRipplePtr ScrollOvershootEffectRipple::New(bool vertical) +{ + return new ScrollOvershootEffectRipple(vertical); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h new file mode 100644 index 0000000..4358059 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.h @@ -0,0 +1,388 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_OVERSHOOT_INDICATOR_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_OVERSHOOT_INDICATOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +class Scrollable; +class ScrollOvershootEffect; +class ScrollOvershootEffectGradient; +class ScrollOvershootEffectRipple; +typedef IntrusivePtr ScrollOvershootEffectPtr; +typedef IntrusivePtr ScrollOvershootEffectGradientPtr; +typedef IntrusivePtr ScrollOvershootEffectRipplePtr; + +struct ScrollOvershootIndicator : public Dali::RefObject +{ +public: + + /** + * ScrollOvershootIndicator constructor. + * @param[in] scrollView reference to ScrollView implementation + * or horizontally (false) + */ + ScrollOvershootIndicator( Scrollable& scrollable); + + /** + * Virtual destructor + */ + virtual ~ScrollOvershootIndicator(); + + /** + * Enables and disables the indicator + * ¶m[in] enable true to enable, false to disable + */ + void Enable(bool enable); + + /** + * Resets the indicator + */ + void Reset(); + + /** + * Create an initialized ScrollOvershootIndicator + * @param[in] scrollView reference to ScrollView implementation + * or horizontally (false) + * @return A pointer to the created ScrollOvershootIndicator. + */ + static ScrollOvershootIndicator* New( Scrollable& scrollable); + +private: + Scrollable& mScrollable; ///< Internal::Scrollable object + ScrollOvershootEffectPtr mEffectX; ///< effect used for x-axis/horizontal display + ScrollOvershootEffectPtr mEffectY; ///< effect used for y-axis/vertical display +}; + +/** + * ScrollOvershootEffect is a derivable class, designed to allow the application programmer to create their own + * overshoot effect and apply it with minimal implementation required + */ +struct ScrollOvershootEffect : public Dali::RefObject +{ +public: + /** + * Create a new overshoot effect, passing in whether it is vertical or horizontal + * + * @param[in] vertical whether this effect is a vertical or horizontal one + */ + ScrollOvershootEffect(bool vertical); + + /** + * Virtual destructor + */ + virtual ~ScrollOvershootEffect() {} + + /** + * Returns if this is a vertical or horizontal overhoot effect + * + * @return true or false + */ + inline bool IsVertical() { return mVertical; } + + /** + * Applies the indicator effect, all derived effects must implement this function + * + * @param[in] scrollable the scrollable object to apply this effect to + */ + virtual void Apply(Scrollable& scrollable) = 0; + + /** + * Removes the indicator effect, all derived effects must implement this function + * + * @param[in] scrollable the scrollable object to remove this effect from + */ + virtual void Remove(Scrollable& scrollable) = 0; + + /** + * Resets this overshoot effect + */ + virtual void Reset() = 0; + + /** + * Updates the constraints used for the overshoot effect + * + * @param[in] scrollable the container for the overshoot effect + */ + virtual void UpdateConstraints(Actor& scrollable) {} + + /** + * Sets up property notifications for overshoot values + * + * @param[in] scrollable the container for the overshoot effect + */ + virtual void SetPropertyNotifications(Actor& scrollable) {} + +private: + bool mVertical; ///< whether this is a vertical/horizontal effect +}; + +/** + * OvershootRippleEffect is a custom shader effect for the overshoot indicator + */ +class OvershootRippleEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized OvershootRippleEffect; this can be initialized with OvershootRippleEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + OvershootRippleEffect(); + + /** + * Virtual destructor. + */ + virtual ~OvershootRippleEffect(); + + /** + * Create an initialized OvershootRippleEffect. + * + * @return A handle to a newly allocated Dali resource. + */ + static OvershootRippleEffect New(); + + /** + * Set the current overshoot value + * + * @param[in] overshoot current overshoot value in the range [0.0f,1.0f] + */ + void SetOvershoot(float overshoot); + + /** + * Set the number of sub images in the overshoot bounce image + * + * @param[in] imageCount number of sub images in main ripple effect image + */ + void SetOvershootImageCount(float imageCount); + + /** + * Get the name for the overshoot property + * which can be used in Animation API's + * + * @return A std::string containing the property name + */ + const std::string& GetOvershootPropertyName() const; + + /** + * Get the name for the sub image count property + * which can be used in Animation API's + * + * @return A std::string containing the property name + */ + const std::string& GetOvershootImageCountPropertyName() const; + +private: // Not intended for application developers + OvershootRippleEffect(ShaderEffect handle); +}; + +/** + * ScrollOvershootEffectGradient creates a gradiented effect at the end of the scrollable area if the user + * attempts to scroll past it + */ +struct ScrollOvershootEffectGradient : public ScrollOvershootEffect +{ +public: + /** + * Create a new gradient overshoot effect, passing in whether it is vertical or horizontal + * + * @param[in] vertical whether this effect is a vertical or horizontal one + */ + ScrollOvershootEffectGradient(bool vertical); + + /** + * @copydoc ScrollOvershootEffect::Apply + */ + virtual void Apply(Scrollable& scrollable); + + /** + * @copydoc ScrollOvershootEffect::Remove + */ + virtual void Remove(Scrollable& scrollable); + + /** + * @copydoc ScrollOvershootEffect::Reset + */ + virtual void Reset() {} + + /** + * Constrains the size of the gradient image + * @param[in] current current size of the image actor + * @param[in] overshootPropertyX current overshoot x amount + * @param[in] overshootPropertyY current overshoot y amount + * @param[in] parentSizeProperty size of the scrollable area so we can make sure the image stretches across it + * @return the new size of the image depending on the overshoot amount + */ + Vector3 SizeConstraint(const Vector3& current, const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY, const PropertyInput& parentSizeProperty); + + /** + * Constrains the size of the gradient image + * @param[in] current current rotation of the image actor + * @param[in] overshootPropertyX current overshoot x amount + * @param[in] overshootPropertyY current overshoot y amount + * @return new rotation os the gradient image actor + */ + Quaternion RotationConstraint(const Quaternion& current, const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY); + + /** + * Constrains the size of the gradient image + * @param[in] current current position of the image actor + * @param[in] parentSizeProperty size of the scrollable area so we can position image on the edge of it + * @param[in] overshootPropertyX current overshoot x amount + * @param[in] overshootPropertyY current overshoot y amount + * @return new position of the gradient image actor + */ + Vector3 PositionConstraint(const Vector3& current, const PropertyInput& parentSizeProperty, const PropertyInput& overshootPropertyX, const PropertyInput& overshootPropertyY); + + /** + * Constrains the size of the gradient image + * @param[in] current current visibility of the image actor + * @param[in] overshootPropertyX current overshoot x amount + * @param[in] overshootPropertyY current overshoot y amount + * @return new visibility property depending on overshoot values + */ + bool VisibilityConstraint(const bool& current, const PropertyInput& canScrollProperty); + + /** + * Creates a new ScrollOvershootEffectGradient objects and returns a pointer to it + * @param[in] vertical whether to create a vertical(true) or horizontal effect + * @return a pointer to the new effect + */ + static ScrollOvershootEffectGradientPtr New( bool vertical ); + +private: + float mMaxOvershootImageSize; ///< maximum size of the image when overshoot value is 1.0f + ImageActor mOvershootImage; ///< the overshoot image... + ActiveConstraint mSizeConstraint; ///< active constraint handle used to store the image width constraint + ActiveConstraint mRotationConstraint; ///< active constraint handle used to store the image rotation constraint + ActiveConstraint mPositionConstraint; ///< active constraint handle used to store the image position constraint + ActiveConstraint mVisibilityConstraint; ///< active constraint handle used to store the image visibility constraint +}; + +/** + * ScrollOvershootEffectRipple creates an animated bounce effect at the end of the scrollable area if the user + * attempts to scroll past it + */ +struct ScrollOvershootEffectRipple : public ScrollOvershootEffect, public ConnectionTracker +{ +public: + /** + * Create a new gradient overshoot effect, passing in whether it is vertical or horizontal + */ + ScrollOvershootEffectRipple(bool vertical); + + /** + * @copydoc ScrollOvershootEffect::Apply + */ + virtual void Apply(Scrollable& scrollable); + + /** + * @copydoc ScrollOvershootEffect::Remove + */ + virtual void Remove(Scrollable& scrollable); + + /** + * @copydoc ScrollOvershootEffect::Reset + */ + virtual void Reset(); + + /** + * @copydoc ScrollOvershootEffect::UpdateConstraints(Actor& scrollable) + */ + virtual void UpdateConstraints(Actor& scrollable); + + /** + * @copydoc ScrollOvershootEffect::SetPropertyNotification + */ + virtual void SetPropertyNotifications(Actor& scrollable); + + /** + * Constrains the size of the gradient image + * @param[in] current current position of the image actor + * @param[in] parentSizeProperty size of the scrollable area so we can position image on the edge of it + * @param[in] overshootProperty current overshoot amount for this indicator's axis + * @return new position of the gradient image actor + */ + Vector3 PositionConstraint(const Vector3& current, const PropertyInput& parentSizeProperty, const PropertyInput& overshootProperty); + + /** + * Informs overshoot effect to update image position and to animate effect overshoot value for a + * positive overshoot value from scrollview + * + * @param[in] source the property notification that triggered this callback + */ + void OnPositiveOvershootNotification(PropertyNotification& source); + + /** + * Informs overshoot effect to update image position and to animate effect overshoot value for a + * negative overshoot value from scrollview + * + * @param[in] source the property notification that triggered this callback + */ + void OnNegativeOvershootNotification(PropertyNotification& source); + + /** + * Function to animate effect overshoot value either to -1.0f/1.0f or 0.0f + * + * @param[in] overshootAmount the amount to animate overshoot to [-1.0f,0.0f,1.0f] + */ + void AnimateScrollOvershoot(float overshootAmount); + + /** + * Connects to the animation finished signal of our overshoot animation + * + * @param[in] animation the animation instance that has finished + */ + void OnOvershootAnimFinished(Animation& animation); + + /** + * Creates a new ScrollOvershootEffectGradient objects and returns a pointer to it + * @param[in] vertical whether to create a vertical(true) or horizontal effect + * @return a pointer to the new effect + */ + static ScrollOvershootEffectRipplePtr New( bool vertical ); + +private: + + float mMaxOvershootImageSize; ///< maximum size of the image when overshoot value is 1.0f + ImageActor mOvershootImage; ///< the overshoot image... + Animation mScrollOvershootAnimation; ///< animation + bool mAnimatingOvershootOn; ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off) + bool mAnimateOvershootOff; ///< whether we are currently animating overshoot to 1.0f/-1.0f (on) or to 0.0f (off) + int mCanScrollPropertyIndex; ///< property index to a property that informs indicator if it is needed + OvershootRippleEffect mRippleEffect; // the ripple vertex/fragment shader effect + PropertyNotification mOvershootPositiveNotification; // stores the property notification used for positive overshoot values + PropertyNotification mOvershootNegativeNotification; // stores the property notification used for negative overshoot values + ActiveConstraint mSizeConstraint; // active constraint handle used to store the image width constraint + ActiveConstraint mPositionConstraint; // active constraint handle used to store the image position constraint +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_OVERSHOOT_INDICATOR_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.cpp new file mode 100644 index 0000000..ea5c6eb --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.cpp @@ -0,0 +1,307 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +using namespace Dali; + +namespace // unnamed namespace +{ + +/** + * Gets a property index. If the property doesn't already exist, then + * it will create the property. + * @param[in] handle The handle that owns or will own the property + * @param[in] name The name for this property + * @param[in] propertyValue The initial value for this property + * @return The property index for this property is returned. + */ +Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue ) +{ + Property::Index index = handle.GetPropertyIndex( name ); + + if(index == Property::INVALID_INDEX) + { + index = handle.RegisterProperty( name, propertyValue ); + } + + return index; +} + +/** + * ScrollCarouselEffectInfo + * + * Visibility constraint: switches off the visibility when Actor + * is outside of bounds, for performance reasons. + * + * Rotate constraint: adjusts the angle of the Actors + * based on their position relative to the edges of the screen. + * When in the middle portion of the screen Angle does not change. + * When leaving the edge of the screen screen rotation changes. + * + * Position constraint: adjusts the position of the Actors + * based on their parent page's position relative to the edges of the screen. + * The position constraint compensates for the rotation which would otherwise + * move the Actor's edge visually away from the neighboring actor, as they rotate + * around their default anchor point. + */ +class ScrollCarouselEffectInfo : public Dali::RefObject +{ +public: + + ScrollCarouselEffectInfo(const Vector2& angleSwing) + : mAngleSwing(angleSwing), + mCanvasMargin( 0.0f, 0.0f ), + mVisibilityThreshold( 1.0f, 1.0f ) + { + } + + /** + * @param[in] current The current visibility of this Actor + * @param[in] positionProperty The Actor's Position. + * @param[in] scaleProperty The Actor's Scale. + * @param[in] sizeProperty The Actor's Size + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollSizeProperty The size of the scroll-view (scrollView SIZE) + * @return The new visibility of this Actor. + */ + bool VisibilityConstraint(const bool& current, + const PropertyInput& positionProperty, + const PropertyInput& scaleProperty, + const PropertyInput& sizeProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollSizeProperty) + { + const Vector2& anchor(AnchorPoint::CENTER.GetVectorXY()); + Vector2 position(positionProperty.GetVector3() + scrollPositionProperty.GetVector3()); + Vector2 scaledSize(sizeProperty.GetVector3() * scaleProperty.GetVector3()); + + Vector2 domain(scrollSizeProperty.GetVector3()); + + position -= (anchor - mVisibilityThreshold) * scaledSize; + domain -= (Vector2::ONE - mVisibilityThreshold * 2.0f) * scaledSize; + + return ( position.x >= 0 && + position.x <= domain.x && + position.y >= 0 && + position.y <= domain.y ); + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] positionProperty The Actor's Position. + * @param[in] scaleProperty The Actor's Scale. + * @param[in] sizeProperty The Actor's Size + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollSizeProperty The size of the scroll-view (scrollView SIZE) + * @param[in] activateProperty Activation value (0 - normal, 1.0 - full effect) + * @return The new orientation of this Actor. + */ + Quaternion RotationConstraint(const Quaternion& current, + const PropertyInput& positionProperty, + const PropertyInput& scaleProperty, + const PropertyInput& sizeProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollSizeProperty, + const PropertyInput& activateProperty) + { + const float activate(activateProperty.GetFloat()); + + if(activate <= Math::MACHINE_EPSILON_0) + { + return current; + } + + const Vector2& anchor(AnchorPoint::CENTER.GetVectorXY()); + Vector2 position(positionProperty.GetVector3() + scrollPositionProperty.GetVector3()); + Vector2 scaledSize(sizeProperty.GetVector3() * scaleProperty.GetVector3()); + Vector2 domain(scrollSizeProperty.GetVector3()); + + position -= (anchor - mCanvasMargin) * scaledSize; + domain -= (Vector2::ONE - mCanvasMargin * 2.0f) * scaledSize; + + Vector2 angle; + + if( position.y < 0 ) + { + angle.y = (-position.y / scaledSize.height) * mAngleSwing.y; + } + else if( position.y > domain.y ) + { + angle.y = ((domain.y - position.y) / scaledSize.height) * mAngleSwing.y; + } + + angle *= activate; + + return Quaternion(-angle.x, Vector3::YAXIS) * + Quaternion(angle.y, Vector3::XAXIS) * + current; + } + + /** + * @param[in] current The current position of this Actor + * @param[in] scaleProperty The Actor's Scale. + * @param[in] sizeProperty The Actor's Size + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollSizeProperty The size of the scroll-view (scrollView SIZE) + * @param[in] activateProperty Activation value (0 - normal, 1.0 - full effect) + * @return The new position of this Actor. + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& scaleProperty, + const PropertyInput& sizeProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollSizeProperty, + const PropertyInput& activateProperty) + { + const float activate(activateProperty.GetFloat()); + Vector3 position(current + scrollPositionProperty.GetVector3()); + + if(activate <= Math::MACHINE_EPSILON_0) + { + return position; + } + + const Vector2& anchor(AnchorPoint::CENTER.GetVectorXY()); + Vector2 scaledSize(sizeProperty.GetVector3() * scaleProperty.GetVector3()); + Vector2 domain(scrollSizeProperty.GetVector3()); + + position.GetVectorXY() -= (anchor - mCanvasMargin) * scaledSize; + domain -= (Vector2::ONE - mCanvasMargin * 2.0f) * scaledSize; + + Vector2 angle; + + if(position.y < 0) + { + angle.y = (-position.y / scaledSize.height) * mAngleSwing.y * activate; + position.y += (1.0f - cosf(angle.y)) * scaledSize.height * 0.5f; + position.z -= sinf(angle.y) * scaledSize.height * 0.5f; + } + else if(position.y > domain.y) + { + angle.y = ((domain.y - position.y) / scaledSize.height) * mAngleSwing.y * activate; + position.y -= (1.0f - cosf(angle.y)) * scaledSize.height * 0.5f; + position.z -= sinf(-angle.y) * scaledSize.height * 0.5f; + } + + position.GetVectorXY() += (anchor - mCanvasMargin) * scaledSize; + + return position; + } + + Vector2 mAngleSwing; ///< Maximum amount in X and Y axes to rotate. + Vector2 mCanvasMargin; ///< Margin around the canvas for when to start rotating + Vector2 mVisibilityThreshold; ///< Threshold for when to to switch off visibility of Actor (for performance) +}; + +typedef IntrusivePtr ScrollCarouselEffectInfoPtr; + +/** + * Helper: Applies the 3D scroll carousel constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] info The effect info for the constraints + */ +void ApplyScrollCarouselConstraints(Toolkit::ScrollView scrollView, + Actor child, + ScrollCarouselEffectInfoPtr info) +{ + // Apply constraints to this actor // + Constraint constraint; + + constraint = Constraint::New( Actor::VISIBLE, + LocalSource( Actor::POSITION ), + LocalSource( Actor::SCALE ), + LocalSource( Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewCarouselEffect::EFFECT_ACTIVATE ) ), + boost::bind( &ScrollCarouselEffectInfo::VisibilityConstraint, info, _1, _2, _3, _4, _5, _6) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::ROTATION, + LocalSource( Actor::POSITION ), + LocalSource( Actor::SCALE ), + LocalSource( Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewCarouselEffect::EFFECT_ACTIVATE ) ), + boost::bind( &ScrollCarouselEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + LocalSource( Actor::SCALE ), + LocalSource( Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewCarouselEffect::EFFECT_ACTIVATE ) ), + boost::bind( &ScrollCarouselEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewCarouselEffect::ScrollViewCarouselEffect() +: mPropertyActivate(Property::INVALID_INDEX) +{ +} + +ScrollViewCarouselEffect::~ScrollViewCarouselEffect() +{ +} + +void ScrollViewCarouselEffect::ApplyToActor(Actor child, const Vector2& angleSwing) +{ + ScrollCarouselEffectInfoPtr info(new ScrollCarouselEffectInfo(angleSwing)); + + ApplyScrollCarouselConstraints( GetScrollView(), child, info ); +} + +void ScrollViewCarouselEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ + if(mPropertyActivate == Property::INVALID_INDEX) + { + mPropertyActivate = SafeRegisterProperty( scrollView, Toolkit::ScrollViewCarouselEffect::EFFECT_ACTIVATE, 1.0f ); + } +} + +void ScrollViewCarouselEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.h new file mode 100644 index 0000000..a84b256 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.h @@ -0,0 +1,115 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CAROUSEL_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CAROUSEL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewCarouselEffect + */ +class ScrollViewCarouselEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewCarouselEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor(Actor child, const Vector2& angleSwing); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewCarouselEffect(); + +private: + + Property::Index mPropertyActivate; ///< Activation property (0.0 - deactivated, 1.0 - fully activated) + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewCarouselEffect& GetImpl(Dali::Toolkit::ScrollViewCarouselEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewCarouselEffect& GetImpl(const Dali::Toolkit::ScrollViewCarouselEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CAROUSEL_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.cpp new file mode 100644 index 0000000..f0c6f7a --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.cpp @@ -0,0 +1,393 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +using namespace Dali; + +namespace // unnamed namespace +{ + +/** + * ScrollCubeEffectInfo + * + * Rotate constraint: adjusts the angle of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of screen Angles on X and Y Axes is 0. + * When one screen away from the middle Angle is 90 degrees (pi/2) + * + * Color constraint: adjusts the alpha of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of screen Alpha is 100% opacity. + * When one screen away from middle Alpha is at 0% opacity (invisble). + * + * Position constraint: adjusts the position of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated about it's origin + mAnchor + */ +class ScrollCubeEffectInfo : public Dali::RefObject +{ +public: + + ScrollCubeEffectInfo(const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing) + : mAnchor(anchor), + mAngleSwing(angleSwing), + mPositionSwing(positionSwing) + { + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new orientation of this Actor. + */ + Quaternion RotationConstraint(const Quaternion& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: for orthognal view. + if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + bool wrap = scrollWrap.GetBoolean(); + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y; + } + } + + // short circuit: for pages outside of view. + if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) ) + { + return current; + } + + position.x /= pageSize.x; + position.y /= pageSize.y; + + Vector2 angle( Clamp(position.x, -1.0f,1.0f), + Clamp(position.y, -1.0f,1.0f) ); + + Quaternion rotation = Quaternion(angle.x * mAngleSwing.x, Vector3::YAXIS) * + Quaternion(-angle.y * mAngleSwing.y, Vector3::XAXIS) * + current; + + return rotation; + } + + /** + * @param[in] current The current color of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new color of this Actor. + */ + Vector4 ColorConstraint(const Vector4& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: for orthognal view. + if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + bool wrap = scrollWrap.GetBoolean(); + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y; + } + } + + // short circuit: for pages outside of view. + if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) ) + { + // note preserve color channels incase there is a shader/further constraint + // that wishes to do something with that information. + return Vector4(current.r, current.g, current.b, 0.0f); + } + + position.x /= pageSize.x; + position.y /= pageSize.y; + + Vector2 angle( Clamp(position.x, -1.0f,1.0f), + Clamp(position.y, -1.0f,1.0f) ); + + float f = (1.0f - fabsf(angle.x)) * (1.0f - fabsf(angle.y)); + f = f*f; + + Vector4 color = current; + color.a *= f; + + return color; + } + + /** + * @param[in] current The current position + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 relativePosition = pagePosition + scrollPosition; + + // short circuit: for orthognal view. + if( (fabsf(relativePosition.x) < Math::MACHINE_EPSILON_1) && (fabsf(relativePosition.y) < Math::MACHINE_EPSILON_1) ) + { + return current + scrollPosition; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + bool wrap = scrollWrap.GetBoolean(); + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + relativePosition.x = WrapInDomain(relativePosition.x + pageSize.x, min.x, max.x) - pageSize.x; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + relativePosition.y = WrapInDomain(relativePosition.y + pageSize.y, min.y, max.y) - pageSize.y; + } + } + + // short circuit: for pages outside of view. + if( (fabsf(relativePosition.x) >= pageSize.x) || (fabsf(relativePosition.y) >= pageSize.y) ) + { + // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this) + // they will be invisible so doesn't have to be precise, just away from stage. + return current + scrollPosition; + } + + relativePosition.x /= pageSize.x; + relativePosition.y /= pageSize.y; + relativePosition.z = 0.0f; + + Vector3 angle( Clamp(relativePosition.x, -1.0f,1.0f) * mAngleSwing.x, + Clamp(relativePosition.y, -1.0f,1.0f) * mAngleSwing.y, + 0.0f); + + // Rotate position (current) about point. + Vector3 position = current - mAnchor; + Quaternion rotatorY(angle.x, Vector3::YAXIS); + position = rotatorY.Rotate(position); + Quaternion rotatorX(-angle.y, Vector3::XAXIS); + position = rotatorX.Rotate(position); + position += mAnchor; + position += relativePosition * mPositionSwing; + + return position - pagePosition; + } + + Vector3 mAnchor; ///< Anchor point where Actor should rotate about. + Vector2 mAngleSwing; ///< Maximum amount in X and Y axes to rotate. + Vector3 mPositionSwing; ///< Maximum amount in X and Y axes to alter position. +}; + +typedef IntrusivePtr ScrollCubeEffectInfoPtr; + +/** + * Helper: Applies the 3D scroll cube constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] info The effect info for the constraints + */ +void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView, + Actor child, + Actor parentPage, + ScrollCubeEffectInfoPtr info) +{ + // Apply constraints to this actor // + Constraint constraint; + constraint = Constraint::New( Actor::ROTATION, + Source(parentPage, Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollCubeEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::COLOR, + Source(parentPage, Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollCubeEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + Source(parentPage, Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollCubeEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewCubeEffect::ScrollViewCubeEffect() +{ + +} + +ScrollViewCubeEffect::~ScrollViewCubeEffect() +{ +} + +void ScrollViewCubeEffect::ApplyToActor(Actor child, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing) +{ + ScrollCubeEffectInfoPtr info(new ScrollCubeEffectInfo(anchor, angleSwing, positionSwing)); + + ApplyScrollCubeConstraints( GetScrollView(), child, child.GetParent(), info ); +} + +void ScrollViewCubeEffect::ApplyToActor(Actor child, + Actor parentPage, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing) +{ + ScrollCubeEffectInfoPtr info(new ScrollCubeEffectInfo(anchor, angleSwing, positionSwing)); + + ApplyScrollCubeConstraints( GetScrollView(), child, parentPage, info ); +} + +void ScrollViewCubeEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewCubeEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.h new file mode 100644 index 0000000..4ad0ac3 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.h @@ -0,0 +1,127 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUBE_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUBE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewCubeEffect + */ +class ScrollViewCubeEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewCubeEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor(Actor child, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing); + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor(Actor child, + Actor parentPage, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewCubeEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewCubeEffect& GetImpl(Dali::Toolkit::ScrollViewCubeEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewCubeEffect& GetImpl(const Dali::Toolkit::ScrollViewCubeEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUBE_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.cpp new file mode 100644 index 0000000..4223a8f --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.cpp @@ -0,0 +1,1295 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +using namespace ScrollViewHelperFunctions; + +/** + * ScrollViewCustomEffectInfo + * + * ScrollAmountConstraint calculates the attached actor's current scroll position, -1.0f to 1.0f is from one side of the screen to the other. + * It also calculates if the other constraints can be skipped + * + * Color constraint: adjusts the alpha of the page based on their parent page's position relative + * to the middle of the screen. + * When at middle of screen Alpha is 100% opacity. + * When outside the viewable area, the opacity is 0%. + * + * Position constraint: adjusts the position of the page based on their parent page's position + * relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated as per expected in a 3D inner cube. + */ +class ScrollViewCustomEffectInfo : public Dali::RefObject +{ +public: + + ScrollViewCustomEffectInfo( uint flags, + Property::Index scrollAmountProperty, + Property::Index anchorProperty, + const Vector2& pageSpacing, + const Vector3& translateIn, const Vector3& translateOut, + const Quaternion& globalRotateIn, const Quaternion& globalRotateOut, + const Vector3& globalOriginIn, const Vector3& globalOriginOut, + const float swingAngleIn, const Vector3& swingAxisIn, const float swingAngleOut, const Vector3& swingAxisOut, + const Vector3& swingAnchorIn, const Vector3& swingAnchorOut, + const float opacityThresholdIn, const float opacityThresholdOut, + AlphaFunction globalRotateAlphaFunctionIn, AlphaFunction globalRotateAlphaFunctionOut, + AlphaFunction swingAlphaFunctionIn, AlphaFunction swingAlphaFunctionOut, + AlphaFunction swingAnchorAlphaFunctionIn, AlphaFunction swingAnchorAlphaFunctionOut, + AlphaFunction translateAlphaFunctionIn, AlphaFunction translateAlphaFunctionOut, + AlphaFunction opacityAlphaFunctionIn, AlphaFunction opacityAlphaFunctionOut ) : + mScrollAmountProperty(scrollAmountProperty), + mAnchorProperty(anchorProperty), + mFlags(flags), + mPageSpacing(pageSpacing), + mTranslateIn(translateIn), mTranslateOut(translateOut), + mGlobalRotateIn(globalRotateIn), mGlobalRotateOut(globalRotateOut), + mGlobalOriginIn(globalOriginIn), mGlobalOriginOut(globalOriginOut), + mSwingAngleIn(swingAngleIn), mSwingAxisIn(swingAxisIn), mSwingAngleOut(swingAngleOut), mSwingAxisOut(swingAxisOut), + mSwingAnchorIn(swingAnchorIn), mSwingAnchorOut(swingAnchorOut), + mOpacityThresholdIn(opacityThresholdIn), mOpacityThresholdOut(opacityThresholdOut), + mGlobalRotateAlphaFunctionIn(globalRotateAlphaFunctionIn), mGlobalRotateAlphaFunctionOut(globalRotateAlphaFunctionOut), + mSwingAlphaFunctionIn(swingAlphaFunctionIn), mSwingAlphaFunctionOut(swingAlphaFunctionOut), + mSwingAnchorAlphaFunctionIn(swingAnchorAlphaFunctionIn), mSwingAnchorAlphaFunctionOut(swingAnchorAlphaFunctionOut), + mTranslateAlphaFunctionIn(translateAlphaFunctionIn), mTranslateAlphaFunctionOut(translateAlphaFunctionOut), + mOpacityAlphaFunctionIn(opacityAlphaFunctionIn), mOpacityAlphaFunctionOut(opacityAlphaFunctionOut), + mPanning(false), + mScrolling(false), + mWasOutsideView(true), + mWrapped(false), + mWasWrapped(false), + mSkipConstraints(false), + mPassedCentreThisFrame(false), + mForceDirectionUpdate(true), + mDirectionFlags(0), + mLastDirectionFlags(0), + mCurrentOpacityAlphaFunction(NULL) + { + } + + Vector3 ScrollAmountConstraint(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + // store last scroll pos + mLastScrollPosition = mScrollPos; + mPagePos = pagePositionProperty.GetVector3(); + mScrollPos = scrollPositionProperty.GetVector3(); + mScrollMin = scrollPositionMin.GetVector3(); + mScrollMax = scrollPositionMax.GetVector3(); + mPageSize = pageSizeProperty.GetVector3(); + mWrap = scrollWrap.GetBoolean(); + mWasWrapped = mWrapped; + + mLastDirectionFlags = mDirectionFlags; + + // Get position of page. + mPosition = mPagePos + mScrollPos; + + // short circuit: if we're looking straight on at the page (jonny 5 is alive) + mIsStraightOnView = IsStraightOnView( mPosition ); + + mLastScrollAmount = mScrollAmount; + Vector3 newScrollAmount(mPosition / mPageSize); + mScrollAmount = newScrollAmount; + mWrapped = false; + if( !mIsStraightOnView && mWrap ) + { + // only need to wrap if not already straight on view + WrapPositionWithinDomain( mPosition, mPageSize, mScrollMin, mScrollMax ); + mIsStraightOnView = IsStraightOnView( mPosition ); + newScrollAmount = mPosition / mPageSize; + if((mScrollAmount.x > 0.0f && newScrollAmount.x < 0.0f) + || (mScrollAmount.x < 0.0f && newScrollAmount.x > 0.0f) + || (mScrollAmount.y > 0.0f && newScrollAmount.y < 0.0f) + || (mScrollAmount.y < 0.0f && newScrollAmount.y > 0.0f)) + { + mWrapped = true; + } + } + mScrollAmount = newScrollAmount; + + return mScrollAmount; + } + + Quaternion PageDirectionAndRotationConstraint(const Quaternion& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& panningProperty, + const PropertyInput& scrollingProperty) + { + bool panning = panningProperty.GetBoolean(); + bool scrolling = scrollingProperty.GetBoolean(); + + bool isOutsideView = IsOutsideView( mPosition, mPageSize ); + + mSkipConstraints = isOutsideView | mIsStraightOnView; + + bool bIsCurrentPage = mScrollAmount.x > -0.5f && mScrollAmount.x < 0.5f; + + if(mSkipConstraints) + { + mPanning = panning; + mScrolling = scrolling; + mWasOutsideView = isOutsideView; + mWasStraightOnView = mIsStraightOnView; + return current; + } + Vector3 scrollDirection = mScrollAmount - mLastScrollAmount; + mPassedCentreThisFrame = bIsCurrentPage && (((mLastScrollAmount.x < 0.0f) && (mScrollAmount.x > 0.0f)) || ((mLastScrollAmount.x > 0.0f) && (mScrollAmount.x < 0.0f)) || ((mLastScrollAmount.y < 0.0f) && (mScrollAmount.y > 0.0f)) || ((mLastScrollAmount.y > 0.0f) && (mScrollAmount.y < 0.0f)) || (mWasStraightOnView && !mIsStraightOnView)); + + // may have wrapped this frame and never gone out of view + bool bWrappedOffScreen = (mWrapped != mWasWrapped && (fabs(scrollDirection.x) > 1.0f || fabs(scrollDirection.y) > 1.0f)); + + mCanChangeDirection = (scrolling && !mScrolling) || mPassedCentreThisFrame || (!isOutsideView && mWasOutsideView) || bWrappedOffScreen; + + if(mCanChangeDirection) + { + // figure out if we have changed direction + if((mWrapped != mWasWrapped) && (fabs(scrollDirection.x) > 1.0f || fabs(scrollDirection.y) || (!isOutsideView && mWasOutsideView))) + { + if( fabs(scrollDirection.x) > 1.0f ) + { + if(scrollDirection.x < 0.0f) + { + scrollDirection.x += (mScrollMax.x - mScrollMin.x) / mPageSize.x; + } + else + { + scrollDirection.x -= (mScrollMax.x - mScrollMin.x) / mPageSize.x; + } + } + if( fabs(scrollDirection.y) > 1.0f ) + { + if(scrollDirection.y < 0.0f) + { + scrollDirection.y += (mScrollMax.y - mScrollMin.y) / mPageSize.y; + } + else + { + scrollDirection.y -= (mScrollMax.y - mScrollMin.y) / mPageSize.y; + } + } + } + + // clear direction flags + mDirectionFlags &= ~Toolkit::ScrollView::DirectionFlagMask_Direction; + if(scrollDirection.x < 0.0f) + { + mDirectionFlags |= Toolkit::ScrollView::DirectionFlagLeft; + } + else if( scrollDirection.x > 0.0f ) + { + mDirectionFlags |= Toolkit::ScrollView::DirectionFlagRight; + } + + if(scrolling && !mScrolling) + { + // have started moving + if(((mDirectionFlags & Toolkit::ScrollView::DirectionFlagLeft) + && (mScrollAmount.x > 0.0f)) + || ((mDirectionFlags & Toolkit::ScrollView::DirectionFlagRight) + && (mScrollAmount.x < 0.0f))) + { + // started moving towards the screen, allow transition change + mDirectionFlags = (mDirectionFlags & ~Toolkit::ScrollView::DirectionFlagMask_Transition) | Toolkit::ScrollView::DirectionFlagTransitionOn; + } + else if(((mDirectionFlags & Toolkit::ScrollView::DirectionFlagLeft) + && (mScrollAmount.x < 0.0f)) + || ((mDirectionFlags & Toolkit::ScrollView::DirectionFlagRight) + && (mScrollAmount.x > 0.0f))) + { + // started moving away from screen, allow transition change + mDirectionFlags = (mDirectionFlags & ~Toolkit::ScrollView::DirectionFlagMask_Transition) | Toolkit::ScrollView::DirectionFlagTransitionOff; + } + } + else + { + // have changed direction + if((((mDirectionFlags & Toolkit::ScrollView::DirectionFlagLeft) + && mScrollAmount.x > 0.0f) + || ((mDirectionFlags & Toolkit::ScrollView::DirectionFlagRight) + && mScrollAmount.x < 0.0f)) + && (isOutsideView || (!isOutsideView && mWasOutsideView) || bWrappedOffScreen)) + { + // went from moving away to moving towards and can change direction + mDirectionFlags = (mDirectionFlags & ~Toolkit::ScrollView::DirectionFlagMask_Transition) | Toolkit::ScrollView::DirectionFlagTransitionOn; + } + else if((((mDirectionFlags & Toolkit::ScrollView::DirectionFlagLeft) + && (mScrollAmount.x < 0.0f)) + || ((mDirectionFlags & Toolkit::ScrollView::DirectionFlagRight) + && (mScrollAmount.x > 0.0f))) + && (isOutsideView || mPassedCentreThisFrame || bWrappedOffScreen)) + { + // went from moving towards to moving away and can change direction + mDirectionFlags = (mDirectionFlags & (~Toolkit::ScrollView::DirectionFlagMask_Transition)) | Toolkit::ScrollView::DirectionFlagTransitionOff; + } + } + // now set up current values depending on direction + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslate) + { + // want to translate by specific amount + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslateIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn)) + { + mCurrentTranslation = mTranslateIn; + } + else if((mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslateOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + mCurrentTranslation = mTranslateOut; + } + else if(!(mFlags & (Toolkit::ScrollViewCustomEffect::FlagTranslateIn | Toolkit::ScrollViewCustomEffect::FlagTranslateOut))) + { + // using same value for both transitions + mCurrentTranslation = mTranslateIn; + } + else + { + // no value to use + mCurrentTranslation = Vector3::ZERO; + } + } + + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagRotate) + { + // want to rotate around an origin + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagRotateAngleForcedOrigin) + { + // the angle forces the origin position depending on page size + // also the page spacing is implemented by setting the 'fake' origin far enough back to add a small gap between pages + // use rotation origin since it isnt needed otherwise + mCurrentGlobalOrigin = mGlobalOriginIn; + } + else + { + mCurrentGlobalRotation = mGlobalRotateIn; + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagRotateOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + mCurrentGlobalRotation = mGlobalRotateOut; + } + } + } + + // now set up current values depending on direction + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngle) + { + // want to translate by specific amount + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngleIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn)) + { + // moving towards centre of screen and have a value for that + mCurrentSwingAngle = mSwingAngleIn; + mCurrentSwingAxis = mSwingAxisIn; + } + else if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngleOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + // moving away from centre of screen and have a value for that + mCurrentSwingAngle = mSwingAngleOut; + mCurrentSwingAxis = mSwingAxisOut; + } + else if(!(mFlags & (Toolkit::ScrollViewCustomEffect::FlagSwingAngleIn | Toolkit::ScrollViewCustomEffect::FlagSwingAngleOut))) + { + // using same value for both transitions + mCurrentSwingAngle = mSwingAngleIn; + mCurrentSwingAxis = mSwingAxisIn; + } + else + { + // no value to use + mCurrentSwingAngle = 0.0f; + mCurrentSwingAxis = Vector3(0.0f, -1.0f, 0.0f); + } + + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAnchor) + { + // want to translate by specific amount + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAnchorIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn)) + { + mCurrentSwingAnchor = mSwingAnchorIn; + } + else if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAnchorOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + mCurrentSwingAnchor = mSwingAnchorOut; + } + else if(!(mFlags & (Toolkit::ScrollViewCustomEffect::FlagSwingAnchorIn | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorOut))) + { + // using same value for both transitions + mCurrentSwingAnchor = mSwingAnchorIn; + } + else + { + // no value to use + mCurrentSwingAnchor = Vector3(0,0,0); + } + if(mDirectionFlags & Toolkit::ScrollView::DirectionFlagLeft) + { + mCurrentSwingAnchor *= -1.0f; + } + } + } + + // now set up current values depending on direction + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold) + { + mCurrentOpacity = mOpacityThresholdIn; + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + mCurrentOpacity = mOpacityThresholdOut; + } + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionMask) + { + // need to adjust using alpha functions + if( (mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn) ) + { + mCurrentOpacityAlphaFunction = mOpacityAlphaFunctionIn; + } + else if( (mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff) ) + { + mCurrentOpacityAlphaFunction = mOpacityAlphaFunctionOut; + } + else + { + mCurrentOpacityAlphaFunction = NULL; + } + } + } + } + + // if user panning OR any form of scroll direction (animated included) set panning to true + mPanning = panning; + mScrolling = scrolling; + mWasOutsideView = isOutsideView; + mWasStraightOnView = mIsStraightOnView; + + if(!(mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngle)) + { + return current; + } + Vector3 amount(mScrollAmount); + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionMask) + { + // need to apply alpha function + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn)) + { + amount.x = mSwingAlphaFunctionIn(fabs(mScrollAmount.x)); + amount.y = mSwingAlphaFunctionIn(fabs(mScrollAmount.y)); + if(mScrollAmount.x < 0) + { + amount.x *= -1.0f; + } + if(mScrollAmount.y < 0) + { + amount.y *= -1.0f; + } + } + else if((mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + amount.x = mSwingAlphaFunctionOut(fabs(mScrollAmount.x)); + amount.y = mSwingAlphaFunctionOut(fabs(mScrollAmount.y)); + if(mScrollAmount.x < 0) + { + amount.x *= -1.0f; + } + if(mScrollAmount.y < 0) + { + amount.y *= -1.0f; + } + } + } + + // TODO - swing angle seems very slightly off... SORT IT!! + //Quaternion rotation = Quaternion::Slerp(current, mCurrentSwingAngle, mScrollAmount.x); + return Quaternion(mCurrentSwingAngle * amount.x, mCurrentSwingAxis) * current; // Quaternion::Lerp(current, mCurrentSwingAngle, mScrollAmount.x); + } + + /** + * @param[in] current The current color of this Actor + * @param[in] scrollAmountProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @return The new color of this Actor. + */ + Vector4 ColorConstraint(const Vector4& current, + const PropertyInput& scrollAmountProperty) + { + if(mSkipConstraints) + { + if(!mIsStraightOnView) + { + // will be off screen, set alpha to 0 to stop drawing it + return Vector4(current.r, current.g, current.b, 0.0f); + } + return current; + } + + if( !(mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold) ) + { + return current; + } + + float amount = fabsf(mScrollAmount.x); + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionMask) + && mCurrentOpacityAlphaFunction) + { + amount = mCurrentOpacityAlphaFunction(amount); + } + Vector4 newColour(current.r, current.g, current.b, fmaxf(((1.0f - amount) / (1.0f - mCurrentOpacity)), 0.0f)); + return newColour; + } + + /** + * @brief PositionConstraint2 + * @param current + * @param scrollPositionProperty + * @param startPagePosition + * @param startDirection + * @return + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& scrollAmountProperty, + const PropertyInput& anchorProperty, + const PropertyInput& rotationProperty) + { + if(mSkipConstraints) + { + return mPosition; + } + + Vector3 newPosition; + + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagRotateAngleForcedOrigin) + { + Quaternion qx(mScrollAmount.x * mCurrentGlobalOrigin.x, Vector3(0, 1, 0)); + Quaternion qy(mScrollAmount.y * mCurrentGlobalOrigin.y, Vector3(1, 0, 0)); + + float thetaBx = (Math::PI - mCurrentGlobalOrigin.x) * 0.5f; + float radiusx = ((mPageSize.width + mPageSpacing.width) * 0.5f) * tanf(thetaBx); + Vector3 originPositionVec = Vector3(0, 0, radiusx); + Vector3 horizontalPos = qx.Rotate(originPositionVec); + newPosition.x = horizontalPos.x; + newPosition.z = radiusx - horizontalPos.z; + // need to create an origin based on current horizontal/vertical scrolling page size + //Vector2 thetaA(mScrollAmount.x * mCurrentGlobalOrigin.x, mScrollAmount.y * mCurrentGlobalOrigin.y); + float thetaBy = (Math::PI - mCurrentGlobalOrigin.y) * 0.5f; + float radiusy = ((mPageSize.height + mPageSpacing.height) * 0.5f) * tanf(thetaBy); + originPositionVec = Vector3(0, 0, radiusy); + horizontalPos = qy.Rotate(originPositionVec); + newPosition.y = horizontalPos.y; + if(mDirectionFlags & (Toolkit::ScrollView::DirectionFlagUp | Toolkit::ScrollView::DirectionFlagDown)) + { + newPosition.z = radiusy - horizontalPos.z; + } + + //Vector3 vRadius(sinf(thetaA.x) * radius, sinf(thetaA.y) * radius, z); + //newPosition = Vector3(vRadius.x, vRadius.y, -vRadius.z + radius); + } + else if(mFlags & Toolkit::ScrollViewCustomEffect::FlagRotate) + { + // rotate around our origin which is relative to the scene + Vector3 vec = newPosition - mCurrentGlobalOrigin; + newPosition -= vec; + vec = mCurrentGlobalRotation.Rotate(vec); + newPosition += vec; + } + + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslate) + { + //Vector3 spacing(mPageSpacing.x, 0, 0); + Vector3 amount(mScrollAmount); + amount.z = fabs(mScrollAmount.x); + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionMask) + { + // need to apply alpha function + if((mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionIn) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOn)) + { + amount.x = mTranslateAlphaFunctionIn(fabs(mScrollAmount.x)); + amount.y = mTranslateAlphaFunctionIn(fabs(mScrollAmount.y)); + amount.z = mTranslateAlphaFunctionIn(fabs(mScrollAmount.x)); + if(mScrollAmount.x < 0) + { + amount.x *= -1.0f; + } + if(mScrollAmount.y < 0) + { + amount.y *= -1.0f; + } + } + else if((mFlags & Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionOut) + && (mDirectionFlags & Toolkit::ScrollView::DirectionFlagTransitionOff)) + { + amount.x = mTranslateAlphaFunctionOut(fabs(mScrollAmount.x)); + amount.y = mTranslateAlphaFunctionOut(fabs(mScrollAmount.y)); + amount.z = mTranslateAlphaFunctionIn(fabs(mScrollAmount.x)); + if(mScrollAmount.x < 0) + { + amount.x *= -1.0f; + } + if(mScrollAmount.y < 0) + { + amount.y *= -1.0f; + } + } + } + newPosition += mCurrentTranslation * amount; // (mCurrentTranslation + ((!(mFlags & Toolkit::ScrollViewCustomEffect::FlagRotateAngleForcedOrigin)) ? (spacing * 0.5f) : Vector3(0,0,0))) * mScrollAmount; + } + + if(mFlags & Toolkit::ScrollViewCustomEffect::FlagSwingAnchor) + { + // rotate around our anchor point which is local to our actor + Quaternion rotation(mCurrentSwingAngle * mScrollAmount.x, mCurrentSwingAxis); + Vector3 offset = mCurrentSwingAnchor * mPageSize; + newPosition += offset; + offset = rotation.Rotate(-offset); + newPosition += offset; + } + + return newPosition; + } + + // input parameters + + Property::Index mScrollAmountProperty; + Property::Index mAnchorProperty; + uint mFlags; ///< flags describing functionality, set automatically depending on functions called during effect setup + Vector2 mPageSpacing; ///< space between pages... kinda obvious really + Vector3 mTranslateIn; ///< translation offset to use when scrolling a page onto the screen + Vector3 mTranslateOut; ///< translation offset to use when scrolling a page off the screen + Quaternion mGlobalRotateIn; ///< rotates the page's position around a point + Quaternion mGlobalRotateOut; ///< rotates the page's position around a point + Vector3 mGlobalOriginIn; ///< the point to rotate a page around when scrolling onto screen + Vector3 mGlobalOriginOut; ///< the point to rotate a page around when scrolling off screen + float mSwingAngleIn; ///< angle to rotate a page around its anchor when scrolling onto screen + Vector3 mSwingAxisIn; ///< angle to rotate a page around its anchor when scrolling off screen + float mSwingAngleOut; ///< angle to rotate a page around its anchor when scrolling onto screen + Vector3 mSwingAxisOut; ///< angle to rotate a page around its anchor when scrolling off screen + Vector3 mSwingAnchorIn; ///< the page anchor point to use when scrolling onto screen + Vector3 mSwingAnchorOut; ///< the page anchor point to use when scrolling off screen + float mOpacityThresholdIn; ///< the point at which opacity will change as page scrolls onto screen + float mOpacityThresholdOut; ///< the point at which opacity will change as page scrolls off screen + AlphaFunction mGlobalRotateAlphaFunctionIn; + AlphaFunction mGlobalRotateAlphaFunctionOut; + AlphaFunction mSwingAlphaFunctionIn; + AlphaFunction mSwingAlphaFunctionOut; + AlphaFunction mSwingAnchorAlphaFunctionIn; + AlphaFunction mSwingAnchorAlphaFunctionOut; + AlphaFunction mTranslateAlphaFunctionIn; + AlphaFunction mTranslateAlphaFunctionOut; + AlphaFunction mOpacityAlphaFunctionIn; + AlphaFunction mOpacityAlphaFunctionOut; + + // constraint update params + // taken from property inputs every constraint update + Vector3 mPagePos; + Vector3 mScrollPos; + Vector3 mScrollMin; + Vector3 mScrollMax; + Vector3 mPageSize; + bool mWrap:1; + bool mPanning:1; + bool mScrolling:1; + bool mWasOutsideView:1; + bool mIsStraightOnView:1; + bool mWasStraightOnView:1; + bool mWrapped:1; ///< whether the scroll page was wrapped this frame + bool mWasWrapped:1; ///< whether the scroll page was wrapped last frame + bool mCanChangeDirection:1; + bool mSkipConstraints:1; ///< whether we can skip the main constraints + bool mPassedCentreThisFrame:1; ///< true if control has moved passed centre of screen + bool mForceDirectionUpdate:1; + bool mDirectionChanged:1; + + // calculated each constraint update depending on flags set + int mDirectionFlags; + int mLastDirectionFlags; + Vector2 mLastScrollPosition; + + Vector3 mPosition; + Vector3 mScrollAmount; + Vector3 mLastScrollAmount; + Vector3 mCurrentTranslation; + Quaternion mCurrentGlobalRotation; + Vector3 mCurrentGlobalOrigin; + float mCurrentSwingAngle; + Vector3 mCurrentSwingAxis; + Vector3 mCurrentSwingAnchor; + float mCurrentOpacity; + AlphaFunction mCurrentOpacityAlphaFunction; +}; + +typedef IntrusivePtr ScrollViewCustomEffectInfoPtr; + +} // unnamed namespace + +const std::string ScrollViewCustomEffect::SCROLL_AMOUNT_PROPERTY_STRING( "scroll-amount" ); +const std::string ScrollViewCustomEffect::ANCHOR_POINT_PROPERTY_STRING( "custom-anchor-point" ); + +ScrollViewCustomEffect::ScrollViewCustomEffect() : + mFlags(0), + mPageSpacing(0,0), + mTranslateIn(0,0,0), + mTranslateOut(0,0,0), + mGlobalRotateIn(0.0f, Vector3(0, 1.0f, 0.0f)), + mGlobalRotateOut(0.0f, Vector3(0, 1.0f, 0.0f)), + mGlobalOriginIn(0,0,0), + mGlobalOriginOut(0,0,0), + mSwingAngleIn(0.0f), + mSwingAxisIn(0.0f, 1.0f, 0.0f), + mSwingAngleOut(0.0f), + mSwingAxisOut(0.0f, 1.0f, 0.0f), + mSwingAnchorIn(0,0,0), + mSwingAnchorOut(0,0,0), + mOpacityThresholdIn(0), + mOpacityThresholdOut(0), + mGlobalRotateAlphaFunctionIn(NULL), + mGlobalRotateAlphaFunctionOut(NULL), + mSwingAlphaFunctionIn(NULL), + mSwingAlphaFunctionOut(NULL), + mSwingAnchorAlphaFunctionIn(NULL), + mSwingAnchorAlphaFunctionOut(NULL), + mTranslateAlphaFunctionIn(NULL), + mTranslateAlphaFunctionOut(NULL), + mOpacityAlphaFunctionIn(NULL), + mOpacityAlphaFunctionOut(NULL) +{ + +} + +ScrollViewCustomEffect::~ScrollViewCustomEffect() +{ +} + +void ScrollViewCustomEffect::SetPageSpacing(const Vector2& spacing) +{ + mPageSpacing = spacing; +} + +void ScrollViewCustomEffect::SetPageTranslation(const Vector3& translation) +{ + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagTranslateMask) | Toolkit::ScrollViewCustomEffect::FlagTranslate; + mTranslateIn = mTranslateOut = translation; +} + +void ScrollViewCustomEffect::SetPageTranslation(const Vector3& translationIn, const Vector3& translationOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagTranslateMask) + | Toolkit::ScrollViewCustomEffect::FlagTranslate + | Toolkit::ScrollViewCustomEffect::FlagTranslateIn + | Toolkit::ScrollViewCustomEffect::FlagTranslateOut; + + mTranslateIn = translationIn; + mTranslateOut = translationOut; +} + +void ScrollViewCustomEffect::SetPageTranslationIn(const Vector3& translation) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagTranslateMask | Toolkit::ScrollViewCustomEffect::FlagTranslateOut)) + | Toolkit::ScrollViewCustomEffect::FlagTranslate + | Toolkit::ScrollViewCustomEffect::FlagTranslateIn; + + mTranslateIn = translation; +} + +void ScrollViewCustomEffect::SetPageTranslationOut(const Vector3& translation) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagTranslateMask | Toolkit::ScrollViewCustomEffect::FlagTranslateIn)) + | Toolkit::ScrollViewCustomEffect::FlagTranslate + | Toolkit::ScrollViewCustomEffect::FlagTranslateOut; + + mTranslateOut = translation; +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunction(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionMask; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionMask; + } + mTranslateAlphaFunctionIn = mTranslateAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + if(funcIn) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionIn; + } + if(funcOut) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionOut; + } + mTranslateAlphaFunctionIn = funcIn; + mTranslateAlphaFunctionOut = funcOut; +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunctionIn(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionIn; + } + mTranslateAlphaFunctionIn = func; +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunctionOut(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagTranslationAlphaFunctionOut; + } + mTranslateAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetGlobalPageRotation(float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagRotateMask) // reset rotate flags + | Toolkit::ScrollViewCustomEffect::FlagRotate; // set new rotate flag + + mGlobalRotateIn = mGlobalRotateOut = Quaternion(angle, axis); +} + +void ScrollViewCustomEffect::SetAngledOriginPageRotation(const Vector3& angle) +{ + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagRotateMask) + | Toolkit::ScrollViewCustomEffect::FlagRotate + | Toolkit::ScrollViewCustomEffect::FlagRotateAngleForcedOrigin; + + // set this angle into global originin for now, the flag will let us know what value to use in constraints + mGlobalOriginIn = angle; +} + +void ScrollViewCustomEffect::SetGlobalPageRotation(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagRotateMask) + | Toolkit::ScrollViewCustomEffect::FlagRotate + | Toolkit::ScrollViewCustomEffect::FlagRotateIn + | Toolkit::ScrollViewCustomEffect::FlagRotateOut; + + mGlobalRotateIn = Quaternion(angleIn, axisIn); + mGlobalRotateOut = Quaternion(angleOut, axisOut); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationIn(float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagRotateMask | Toolkit::ScrollViewCustomEffect::FlagRotateOut)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagRotate + | Toolkit::ScrollViewCustomEffect::FlagRotateIn; + + mGlobalRotateIn = Quaternion(angle, axis); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOut(float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagRotateMask | Toolkit::ScrollViewCustomEffect::FlagRotateIn)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagRotate + | Toolkit::ScrollViewCustomEffect::FlagRotateOut; + + mGlobalRotateOut = Quaternion(angle, axis); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOrigin(const Vector3& origin) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagRotateOriginMask) // reset all rotation flags + | Toolkit::ScrollViewCustomEffect::FlagRotateOrigin; + + mGlobalOriginIn = mGlobalOriginOut = origin; +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOrigin(const Vector3& originIn, const Vector3& originOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagRotateOriginMask) // reset all rotation flags + | Toolkit::ScrollViewCustomEffect::FlagRotateOrigin + | Toolkit::ScrollViewCustomEffect::FlagRotateOriginIn + | Toolkit::ScrollViewCustomEffect::FlagRotateOriginOut; + + mGlobalOriginIn = originIn; + mGlobalOriginOut = originOut; +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOriginIn(const Vector3& origin) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagRotateOriginMask | Toolkit::ScrollViewCustomEffect::FlagRotateOriginOut)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagRotateOrigin + | Toolkit::ScrollViewCustomEffect::FlagRotateOriginIn; + + mGlobalOriginIn = origin; +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOriginOut(const Vector3& origin) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagRotateOriginMask | Toolkit::ScrollViewCustomEffect::FlagRotateOriginIn)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagRotateOrigin + | Toolkit::ScrollViewCustomEffect::FlagRotateOriginOut; + + mGlobalOriginOut = origin; +} + +void ScrollViewCustomEffect::SetSwingAngle(const float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleMask) // reset rotate flags + | Toolkit::ScrollViewCustomEffect::FlagSwingAngle; // set new rotate flag + + mSwingAngleIn = mSwingAngleOut = angle; + mSwingAxisIn = mSwingAxisOut = axis; +} + +void ScrollViewCustomEffect::SetSwingAngle(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleMask) + | Toolkit::ScrollViewCustomEffect::FlagSwingAngle + | Toolkit::ScrollViewCustomEffect::FlagSwingAngleIn + | Toolkit::ScrollViewCustomEffect::FlagSwingAngleOut; + + mSwingAngleIn = angleIn; + mSwingAngleOut = angleOut; + mSwingAxisIn = axisIn; + mSwingAxisOut = axisOut; +} + +void ScrollViewCustomEffect::SetSwingAngleIn(float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagSwingAngleMask | Toolkit::ScrollViewCustomEffect::FlagSwingAngleOut)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagSwingAngle + | Toolkit::ScrollViewCustomEffect::FlagSwingAngleIn; + + mSwingAngleIn = angle; + mSwingAxisIn = axis; +} + +void ScrollViewCustomEffect::SetSwingAngleOut(float angle, const Vector3& axis) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagSwingAngleMask | Toolkit::ScrollViewCustomEffect::FlagSwingAngleIn)) // reset all rotation flags except RotateOut, since they may be calling these functions separately + | Toolkit::ScrollViewCustomEffect::FlagSwingAngle + | Toolkit::ScrollViewCustomEffect::FlagSwingAngleOut; + + mSwingAngleOut = angle; + mSwingAxisOut = axis; +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunction(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionMask; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionMask; + } + mSwingAlphaFunctionIn = mSwingAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + if(funcIn) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionIn; + } + if(funcOut) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionOut; + } + mSwingAlphaFunctionIn = funcIn; + mSwingAlphaFunctionOut = funcOut; +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunctionIn(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionIn; + } + mSwingAlphaFunctionIn = func; +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunctionOut(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAngleAlphaFunctionOut; + } + mSwingAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetSwingAnchor(const Vector3& anchor) +{ + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorMask) + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchor; + mSwingAnchorIn = mSwingAnchorOut = anchor; +} + +void ScrollViewCustomEffect::SetSwingAnchor(const Vector3& anchorIn, const Vector3& anchorOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorMask) + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchor + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorIn + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorOut; + + mSwingAnchorIn = anchorIn; + mSwingAnchorOut = anchorOut; +} + +void ScrollViewCustomEffect::SetSwingAnchorIn(const Vector3& anchor) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorMask | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorOut)) + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchor + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorIn; + + mSwingAnchorIn = anchor; +} + +void ScrollViewCustomEffect::SetSwingAnchorOut(const Vector3& anchor) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorMask | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorIn)) + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchor + | Toolkit::ScrollViewCustomEffect::FlagSwingAnchorOut; + + mSwingAnchorOut = anchor; +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunction(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionMask; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionMask; + } + mSwingAnchorAlphaFunctionIn = mSwingAnchorAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + if(funcIn) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionIn; + } + if(funcOut) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionOut; + } + mSwingAnchorAlphaFunctionIn = funcIn; + mSwingAnchorAlphaFunctionOut = funcOut; +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunctionIn(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionIn; + } + mSwingAnchorAlphaFunctionIn = func; +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunctionOut(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagSwingAnchorAlphaFunctionOut; + } + mSwingAnchorAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetOpacityThreshold(float thresh) +{ + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdMask) + | Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdIn + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdOut; + + mOpacityThresholdIn = mOpacityThresholdOut = thresh; +} + +void ScrollViewCustomEffect::SetOpacityThreshold(float threshIn, float threshOut) +{ + // set flags describing translation with separate in out translation + mFlags = (mFlags & ~Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdMask) + | Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdIn + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdOut; + + mOpacityThresholdIn = threshIn; + mOpacityThresholdOut = threshOut; +} + +void ScrollViewCustomEffect::SetOpacityThresholdIn(float thresh) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdMask | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdOut)) + | Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdIn; + + mOpacityThresholdIn = thresh; +} + +void ScrollViewCustomEffect::SetOpacityThresholdOut(float thresh) +{ + mFlags = (mFlags & (~Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdMask | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdIn)) + | Toolkit::ScrollViewCustomEffect::FlagOpacityThreshold + | Toolkit::ScrollViewCustomEffect::FlagOpacityThresholdOut; + + mOpacityThresholdOut = thresh; +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunction(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionMask; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionMask; + } + mOpacityAlphaFunctionIn = mOpacityAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + if(funcIn) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionIn; + } + if(funcOut) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionOut; + } + mOpacityAlphaFunctionIn = funcIn; + mOpacityAlphaFunctionOut = funcOut; +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunctionIn(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionIn; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionIn; + } + mOpacityAlphaFunctionIn = func; +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunctionOut(AlphaFunction func) +{ + if(func) + { + mFlags |= Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionOut; + } + else + { + mFlags &= ~Toolkit::ScrollViewCustomEffect::FlagOpacityAlphaFunctionOut; + } + mOpacityAlphaFunctionOut = func; +} + +void ScrollViewCustomEffect::ApplyToPage( Actor page, Vector3 pageSize) +{ + // may have already called register for these properties, so check before registering + Dali::Toolkit::ScrollView scrollView = GetScrollView(); + Property::Index scrollPropertyIndex = page.GetPropertyIndex(SCROLL_AMOUNT_PROPERTY_STRING); + if(scrollPropertyIndex == Property::INVALID_INDEX) + { + scrollPropertyIndex = page.RegisterProperty(SCROLL_AMOUNT_PROPERTY_STRING, Vector3::ZERO); + } + + Property::Index anchorPropertyIndex = page.GetPropertyIndex(ANCHOR_POINT_PROPERTY_STRING); + if(anchorPropertyIndex == Property::INVALID_INDEX) + { + anchorPropertyIndex = page.RegisterProperty(ANCHOR_POINT_PROPERTY_STRING, Vector3::ZERO); + } + + ScrollViewCustomEffectInfoPtr info(new ScrollViewCustomEffectInfo( + mFlags, + scrollPropertyIndex, + anchorPropertyIndex, + mPageSpacing, + mTranslateIn, mTranslateOut, + mGlobalRotateIn, mGlobalRotateOut, + mGlobalOriginIn, mGlobalOriginOut, + mSwingAngleIn, mSwingAxisIn, mSwingAngleOut, mSwingAxisOut, + mSwingAnchorIn - AnchorPoint::CENTER, mSwingAnchorOut - AnchorPoint::CENTER, + mOpacityThresholdIn, mOpacityThresholdOut, + mGlobalRotateAlphaFunctionIn, mGlobalRotateAlphaFunctionOut, + mSwingAlphaFunctionIn, mSwingAlphaFunctionOut, + mSwingAnchorAlphaFunctionIn, mSwingAnchorAlphaFunctionOut, + mTranslateAlphaFunctionIn, mTranslateAlphaFunctionOut, + mOpacityAlphaFunctionIn, mOpacityAlphaFunctionOut)); + + ScrollViewCustomEffectInfo effectInfo( *info ); + Property::Index scrollAmountProperty = effectInfo.mScrollAmountProperty; + Property::Index anchProperty = effectInfo.mAnchorProperty; + // Apply constraints to this actor // + Constraint constraint; + constraint = Constraint::New( scrollAmountProperty, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollViewCustomEffectInfo::ScrollAmountConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + page.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::ROTATION, + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source( scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_PANNING_PROPERTY_NAME ) ), + Source( scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_SCROLLING_PROPERTY_NAME ) ), + boost::bind( &ScrollViewCustomEffectInfo::PageDirectionAndRotationConstraint, info, _1, _2, _3, _4) ); + + constraint.SetRemoveAction( Constraint::Discard ); + page.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::COLOR, + Source(page, scrollAmountProperty ), + boost::bind( &ScrollViewCustomEffectInfo::ColorConstraint, info, _1, _2) ); + + constraint.SetRemoveAction( Constraint::Discard ); + page.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + Source(page, scrollAmountProperty ), + Source(page, anchProperty ), + LocalSource(Actor::ROTATION), + boost::bind( &ScrollViewCustomEffectInfo::PositionConstraint, info, _1, _2, _3, _4) ); + + constraint.SetRemoveAction( Constraint::Discard ); + page.ApplyConstraint( constraint ); +} + +void ScrollViewCustomEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewCustomEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.h new file mode 100644 index 0000000..c6c952c --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.h @@ -0,0 +1,418 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUSTOM_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUSTOM_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollGroup; +class ScrollView; + +namespace Internal +{ + +/** + * @copydoc Toolkit::ScrollViewCustomEffect + */ +class ScrollViewCustomEffect : public ScrollViewEffect +{ + static const std::string SCROLL_AMOUNT_PROPERTY_STRING; + static const std::string ANCHOR_POINT_PROPERTY_STRING; + +public: + + /** + * Constructor + */ + ScrollViewCustomEffect(); + +public: + + /** + * @brief SetPageSpacing + * @param spacing + */ + void SetPageSpacing(const Vector2& spacing); + + /** + * @brief SetPageTranslation sets a simple translate on/off value + * @param translation + */ + void SetPageTranslation(const Vector3& translation); + + /** + * @brief SetPageTranslation + * @param translationIn + * @param translationOut + */ + void SetPageTranslation(const Vector3& translationIn, const Vector3& translationOut); + + /** + * @brief SetPageTranslationIn + * @param translation + */ + void SetPageTranslationIn(const Vector3& translation); + + /** + * @brief SetPageTranslationOut + * @param translation + */ + void SetPageTranslationOut(const Vector3& translation); + + /** + * @brief SetPageTranslateAlphaFunction + * @param func + */ + void SetPageTranslateAlphaFunction(AlphaFunction func); + + /** + * @brief SetPageTranslateAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetPageTranslateAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetPageTranslateAlphaFunctionIn + * @param func + */ + void SetPageTranslateAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetPageTranslateAlphaFunctionOut + * @param func + */ + void SetPageTranslateAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetGlobalPageRotation + * @param angle + * @param axis + */ + void SetGlobalPageRotation(float angle, const Vector3& axis); + + /** + * @brief SetAnglePageRotation uses the angle and page size passed in on creation to create a faked origin (inner cube needs this method) + * @param angle + */ + void SetAngledOriginPageRotation(const Vector3& angle); + + /** + * @brief SetGlobalPageRotation + * @param angleIn + * @param axisIn + * @param angleOut + * @param axisOut + */ + void SetGlobalPageRotation(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut); + + /** + * @brief SetGlobalPageRotationIn + * @param angle + * @param axis + */ + void SetGlobalPageRotationIn(float angle, const Vector3& axis); + + /** + * @brief SetGlobalPageRotationOut + * @param angle + * @param axis + */ + void SetGlobalPageRotationOut(float angle, const Vector3& axis); + + /** + * @brief SetPageRotationOrigin Set the origin to rotate all the pages around + * - The default value is (0,0,0) + * @param origin + */ + void SetGlobalPageRotationOrigin(const Vector3& origin); + + /** + * @brief SetGlobalPageRotationOrigin + * @param originIn + * @param originOut + */ + void SetGlobalPageRotationOrigin(const Vector3& originIn, const Vector3& originOut); + + /** + * @brief SetGlobalPageRotationOriginIn + * @param origin + */ + void SetGlobalPageRotationOriginIn(const Vector3& origin); + + /** + * @brief SetGlobalPageRotationOriginOut + * @param origin + */ + void SetGlobalPageRotationOriginOut(const Vector3& origin); + + /** + * @brief SetSwingAngle + * @param angle + * @param axis + */ + void SetSwingAngle(const float angle, const Vector3& axis); + + /** + * @brief SetSwingAngle + * @param angleIn + * @param axisIn + * @param angleOut + * @param axisOut + */ + void SetSwingAngle(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut); + + /** + * @brief SetSwingAngleIn + * @param angle + * @param axis + */ + void SetSwingAngleIn(float angle, const Vector3& axis); + + /** + * @brief SetSwingAngleOut + * @param angle + * @param axis + */ + void SetSwingAngleOut(float angle, const Vector3& axis); + + /** + * @brief SetSwingAngleAlphaFunction + * @param func + */ + void SetSwingAngleAlphaFunction(AlphaFunction func); + + /** + * @brief SetSwingAngleAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetSwingAngleAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetSwingAngleAlphaFunctionIn + * @param func + */ + void SetSwingAngleAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetSwingAngleAlphaFunctionOut + * @param func + */ + void SetSwingAngleAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetSwingAnchor + * @param anchor + */ + void SetSwingAnchor(const Vector3& anchor); + + /** + * @brief SetSwingAnchor + * @param anchorIn + * @param anchorOut + */ + void SetSwingAnchor(const Vector3& anchorIn, const Vector3& anchorOut); + + /** + * @brief SetSwingAnchorIn + * @param anchor + */ + void SetSwingAnchorIn(const Vector3& anchor); + + /** + * @brief SetSwingAnchorOut + * @param anchor + */ + void SetSwingAnchorOut(const Vector3& anchor); + + /** + * @brief SetSwingAnchorAlphaFunction + * @param func + */ + void SetSwingAnchorAlphaFunction(AlphaFunction func); + + /** + * @brief SetSwingAnchorAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetSwingAnchorAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetSwingAnchorAlphaFunctionIn + * @param func + */ + void SetSwingAnchorAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetSwingAnchorAlphaFunctionOut + * @param func + */ + void SetSwingAnchorAlphaFunctionOut(AlphaFunction func); + + /** + * @brief SetOpacityThreshold + * @param thresh + */ + void SetOpacityThreshold(float thresh); + + /** + * @brief SetOpacityThreshold + * @param threshIn + * @param threshOut + */ + void SetOpacityThreshold(float threshIn, float threshOut); + + /** + * @brief SetOpacityThresholdIn + * @param thresh + */ + void SetOpacityThresholdIn(float thresh); + + /** + * @brief SetOpacityThresholdOut + * @param thresh + */ + void SetOpacityThresholdOut(float thresh); + + /** + * @brief SetOpacityAlphaFunction + * @param func + */ + void SetOpacityAlphaFunction(AlphaFunction func); + + /** + * @brief SetOpacityAlphaFunction + * @param funcIn + * @param funcOut + */ + void SetOpacityAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut); + + /** + * @brief SetOpacityAlphaFunctionIn + * @param func + */ + void SetOpacityAlphaFunctionIn(AlphaFunction func); + + /** + * @brief SetOpacityAlphaFunctionOut + * @param func + */ + void SetOpacityAlphaFunctionOut(AlphaFunction func); + + + /** + * ApplyToPage This is the full internal ApplyToPage function and ALL other ApplyToPage + * functions should call this one. Making this internal allows us to change this function + * in the future without affecting demo apps + * @param page + * @param pageSize + */ + void ApplyToPage( Actor page, Vector3 pageSize); + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach( Toolkit::ScrollView& scrollView ); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach( Toolkit::ScrollView& scrollView ); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewCustomEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. + + uint mFlags; ///< flags describing functionality, set automatically depending on functions called during effect setup + Vector2 mPageSpacing; ///< space between pages... kinda obvious really + Vector3 mTranslateIn; ///< translation offset to use when scrolling a page onto the screen + Vector3 mTranslateOut; ///< translation offset to use when scrolling a page off the screen + Quaternion mGlobalRotateIn; ///< rotates the page's position around a point + Quaternion mGlobalRotateOut; ///< rotates the page's position around a point + Vector3 mGlobalOriginIn; ///< the point to rotate a page around when scrolling onto screen + Vector3 mGlobalOriginOut; ///< the point to rotate a page around when scrolling off screen + float mSwingAngleIn; ///< angle to rotate a page around its anchor when scrolling onto screen + Vector3 mSwingAxisIn; + float mSwingAngleOut; ///< angle to rotate a page around its anchor when scrolling off screen + Vector3 mSwingAxisOut; + Vector3 mSwingAnchorIn; ///< the page anchor point to use when scrolling onto screen + Vector3 mSwingAnchorOut; ///< the page anchor point to use when scrolling off screen + float mOpacityThresholdIn; ///< the point at which opacity will change as page scrolls onto screen + float mOpacityThresholdOut; ///< the point at which opacity will change as page scrolls off screen + AlphaFunction mGlobalRotateAlphaFunctionIn; + AlphaFunction mGlobalRotateAlphaFunctionOut; + AlphaFunction mSwingAlphaFunctionIn; + AlphaFunction mSwingAlphaFunctionOut; + AlphaFunction mSwingAnchorAlphaFunctionIn; + AlphaFunction mSwingAnchorAlphaFunctionOut; + AlphaFunction mTranslateAlphaFunctionIn; + AlphaFunction mTranslateAlphaFunctionOut; + AlphaFunction mOpacityAlphaFunctionIn; + AlphaFunction mOpacityAlphaFunctionOut; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewCustomEffect& GetImpl(Dali::Toolkit::ScrollViewCustomEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewCustomEffect& GetImpl(const Dali::Toolkit::ScrollViewCustomEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUSTOM_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.cpp new file mode 100644 index 0000000..cdef6d5 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.cpp @@ -0,0 +1,401 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace // unnamed namespace +{ + +// constraints //////////////////////////////////////////////////////////////// + +/** + * Ramp equation is a variable easing equation + * of the form f(x) = |x|^y * x / |x| + * + * An exponent (y) of 1 will result in a fast (linear graph) + * Increasing the exponent will increase the Ease In + * + * @param[in] x mantissa (value from -1.0f to 1.0f) + * @param[in] y exponent (+ve value) + * @return The signed progress value from -1.0f to 1.0f + */ +inline float RampFunction(float x, float y) +{ + if(x < 0.0f) + { + return -powf(fabsf(x), y); + } + + return powf(fabsf(x), y); +} + +/** + * ScrollDepthScaleConstraint + * + * Scale constraint adjusts the scale of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of the screen the scale is not altered. + * As the page is moved away from the middle, Actors shrink in scale but at + * different rates defined by the RampFunction(x, f). + * All Actors eventually shrink to the same amount once at their destination. + */ +struct ScrollDepthScaleConstraint +{ + /** + * The scaling constraint uses the amount the actors + * have moved in position to determine scaling extent. + * So essentially very similar calculations are used here. + * + * @param[in] positionExtent Controls how much Actor's X and Y + * position affects their alpha function's exponent value + * @param[in] offsetExtent exponent offset for X and Y scrolling + * axes. + * @param[in] positionScale Changes the amount the page as a whole + * moves by. + * @param[in] scaleExtent Scale factor to reach when page is one whole + * page away from screen. + */ + ScrollDepthScaleConstraint( Vector2 positionExtent = Vector2(0.5f, 1.0f), + Vector2 offsetExtent = Vector2(1.0f, 1.0f), + float positionScale = 1.5f, + float scaleExtent = 0.5f) + : mPositionExtent(positionExtent), + mOffsetExtent(offsetExtent), + mMaxExtent(positionExtent.x + positionExtent.y), + mPositionScale(positionScale), + mScaleExtent(scaleExtent) + { + } + + /** + * @param[in] current The current scale + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new scale of this Actor. + */ + Vector3 operator()(const Vector3& currentScale, + const PropertyInput& currentPositionProperty, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty) + { + const Vector3& currentPosition = currentPositionProperty.GetVector3(); + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: for orthognal view. + if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) ) + { + return currentScale; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + // Don't have enough parameters, to provide Wrap mode (need a way of having 'uniforms' instead of scrollWrap.GetBoolean()) + const bool wrap = true; + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y; + } + } + + // short circuit: for pages outside of view. + if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) ) + { + return currentScale; + } + + // Calculate scale //////////////////////////////////////////////////////// + + position.x /= pageSize.x; + position.y /= pageSize.y; + + position *= mPositionScale; + + Vector3 relCurrentPosition = currentPosition; + relCurrentPosition.x = relCurrentPosition.x / pageSize.x + 0.5f; + relCurrentPosition.y = relCurrentPosition.y / pageSize.y + 0.5f; + + Vector3 extent( (relCurrentPosition.x * mPositionExtent.x + relCurrentPosition.y * mPositionExtent.y) * 1.0f, + (relCurrentPosition.x * mPositionExtent.y + relCurrentPosition.y * mPositionExtent.x) * 1.0f, + 0.0f); + + if(position.x>0.0f) + { + extent.x = mMaxExtent - extent.x; // Flip for right. + } + if(position.y>0.0f) + { + extent.y = mMaxExtent - extent.y; // Flip for bottom. + } + + position.x = RampFunction(position.x, mOffsetExtent.x + extent.x); + position.y = RampFunction(position.y, mOffsetExtent.y + extent.y); + + float f = mScaleExtent + cos(position.x * Math::PI_2) * cos(position.y * Math::PI_2) * (1.0f - mScaleExtent); + + return currentScale * f; + } + + const Vector2 mPositionExtent; ///< Determines how much of the Actor's X and Y position affects exponent value. + const Vector2 mOffsetExtent; ///< Offset for exponent value. + const float mMaxExtent; ///< Maximum possible extent (mOffsetExtent.x + mOffsetExtent.y) + const float mPositionScale; ///< Position scaling factor (spreads out pages, to avoid overlap) + const float mScaleExtent; ///< Scale factor when page is at furthest from +}; + +/** + * ScrollDepthPositionConstraint + * + * Position constraint adjusts the position of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * As the page is moved away from the middle, Actors move away but at + * different rates defined by the RampFunction(x, f). + * All Actors eventually arrive at their destination at the same time. + */ +struct ScrollDepthPositionConstraint +{ + /** + * @param [in] positionExtent Controls how much Actor's X and Y + * position affects their alpha function's exponent value + * @param [in] offsetExtent exponent offset for X and Y scrolling + * axes. + * @param [in] positionScale Changes the amount the page as a whole + * moves by. + */ + ScrollDepthPositionConstraint( Vector2 positionExtent = Vector2(0.5f, 1.0f), + Vector2 offsetExtent = Vector2(1.0f, 1.0f), + float positionScale = 1.5f ) + : mPositionExtent(positionExtent), + mOffsetExtent(offsetExtent), + mMaxExtent(positionExtent.x + positionExtent.y), + mPositionScale(positionScale) + { + } + + /** + * @param[in] current The current position + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 operator()(const Vector3& currentPosition, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: for orthognal view. + if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) ) + { + return currentPosition + scrollPosition; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + bool wrap = scrollWrap.GetBoolean(); + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y; + } + } + + // short circuit: for pages outside of view. + if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) ) + { + // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this) + // they will be invisible so doesn't have to be precise, just away from stage. + return currentPosition + scrollPosition; + } + + // Calculate position ///////////////////////////////////////////////////// + + position.x /= pageSize.x; + position.y /= pageSize.y; + + position *= mPositionScale; + + Vector3 finalPosition(currentPosition - pagePosition); + + Vector3 relCurrentPosition = currentPosition; + relCurrentPosition.x = relCurrentPosition.x / pageSize.x + 0.5f; + relCurrentPosition.y = relCurrentPosition.y / pageSize.y + 0.5f; + + Vector3 extent( (relCurrentPosition.x * mPositionExtent.x + relCurrentPosition.y * mPositionExtent.y) * 1.0f, + (relCurrentPosition.x * mPositionExtent.y + relCurrentPosition.y * mPositionExtent.x) * 1.0f, + 0.0f); + + if(position.x>0.0f) + { + extent.x = mMaxExtent - extent.x; // Flip for right. + } + if(position.y>0.0f) + { + extent.y = mMaxExtent - extent.y; // Flip for bottom. + } + + position.x = RampFunction(position.x, mOffsetExtent.x + extent.x); + position.y = RampFunction(position.y, mOffsetExtent.y + extent.y); + + finalPosition += pageSize * position; + + return finalPosition; + } + + const Vector2 mPositionExtent; ///< Determines how much of the Actor's X and Y position affects exponent value. + const Vector2 mOffsetExtent; ///< Offset for exponent value. + const float mMaxExtent; ///< Maximum possible extent (mOffsetExtent.x + mOffsetExtent.y) + const float mPositionScale; ///< Position scaling factor (spreads out pages, to avoid overlap) +}; + +/** + * Applies the scroll depth constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] positionExtent Controls how much Actor's X and Y + * position affects their alpha function's exponent value + * @param[in] offsetExtent exponent offset for X and Y scrolling + * axes. + * @param[in] positionScale Changes the amount the page as a whole + * moves by. + * @param[in] scaleExtent Scale factor to reach when page is one whole + * page away from screen. + */ +void ApplyScrollDepthConstraints(Toolkit::ScrollView scrollView, + Actor child, + const Vector2& positionExtent, + const Vector2& offsetExtent, + float positionScale, + float scaleExtent) +{ + // Scale Constraint + Constraint constraint = Constraint::New( Actor::SCALE, + LocalSource(Actor::POSITION), + ParentSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + ScrollDepthScaleConstraint( positionExtent, offsetExtent, positionScale, scaleExtent ) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + // Position Constraint (apply last as other constraints use Actor::POSITION as a function input) + constraint = Constraint::New( Actor::POSITION, + ParentSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + ScrollDepthPositionConstraint( positionExtent, offsetExtent, positionScale ) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewDepthEffect::ScrollViewDepthEffect() +{ + +} + +ScrollViewDepthEffect::~ScrollViewDepthEffect() +{ +} + +void ScrollViewDepthEffect::ApplyToActor(Actor child, + const Vector2& positionExtent, + const Vector2& offsetExtent, + float positionScale, + float scaleExtent) +{ + ApplyScrollDepthConstraints( GetScrollView(), child, positionExtent, offsetExtent, positionScale, scaleExtent ); +} + +void ScrollViewDepthEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewDepthEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.h new file mode 100644 index 0000000..8372fbe --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.h @@ -0,0 +1,119 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_DEPTH_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_DEPTH_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewDepthEffect + */ +class ScrollViewDepthEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewDepthEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor(Actor child, + const Vector2& positionExtent, + const Vector2& offsetExtent, + float positionScale, + float scaleExtent); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewDepthEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewDepthEffect& GetImpl(Dali::Toolkit::ScrollViewDepthEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewDepthEffect& GetImpl(const Dali::Toolkit::ScrollViewDepthEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_CUBE_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp new file mode 100644 index 0000000..365aeaa --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp @@ -0,0 +1,70 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewEffect::ScrollViewEffect() +: mScrollViewImpl(NULL) +{ +} + +ScrollViewEffect::~ScrollViewEffect() +{ +} + +void ScrollViewEffect::Attach(Toolkit::ScrollView& scrollView) +{ + DALI_ASSERT_ALWAYS( (!mScrollViewImpl) && "Already attached to a ScrollView" ); + + mScrollViewImpl = &GetImpl(scrollView); + + OnAttach(scrollView); +} + +void ScrollViewEffect::Detach(Toolkit::ScrollView& scrollView) +{ + DALI_ASSERT_ALWAYS( (mScrollViewImpl) && "Already detached from ScrollView" ); + DALI_ASSERT_ALWAYS( (&GetImpl(scrollView) == mScrollViewImpl) && "Effect attached to a different ScrollView"); + + OnDetach(scrollView); + + mScrollViewImpl = NULL; +} + +Toolkit::ScrollView ScrollViewEffect::GetScrollView() +{ + DALI_ASSERT_ALWAYS(mScrollViewImpl); + + return DownCast( mScrollViewImpl->Self() ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h new file mode 100644 index 0000000..2bb3d32 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h @@ -0,0 +1,136 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewEffect + */ +class ScrollViewEffect : public Dali::BaseObject, public ConnectionTracker +{ + +public: + + ScrollViewEffect(); + + /** + * Attaches this effect to scrollView. + * @pre must not be already attached to a scrollView + * @note internally the scrollView effect holds a weak reference + * to scrollView. + * @param[in] scrollView The scrollView instance to attach to. + */ + void Attach(Toolkit::ScrollView& scrollView); + + /** + * Attaches this effect to scrollView. + * @pre must not be already attached to a scrollView + * @param[in] scrollView The scrollView instance to attach to. + */ + void Detach(Toolkit::ScrollView& scrollView); + +public: + + /** + * Called upon Attaching of effect to a scrollView instance. + * + * This will be called once. + * + * @param[in] scrollView The attached scrollView instance. + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView) = 0; + + /** + * Called upon Detaching of effect from a scrollView instance. + * + * This will be called once. + * + * @param[in] scrollView The attached scrollView instance. + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView) = 0; + +protected: + + /** + * Returns the ScrollView handle that this effect is + * attached to. + * @note if it's not attached to any ScrollView then + * will return an uninitialized handle. + * @return The scrollView handle is returned. + */ + Toolkit::ScrollView GetScrollView(); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewEffect(); + +private: + + Toolkit::Internal::ScrollView *mScrollViewImpl; ///< Attached ScrollView instance (pointer to implementation) + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewEffect& GetImpl(Dali::Toolkit::ScrollViewEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewEffect& GetImpl(const Dali::Toolkit::ScrollViewEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.cpp new file mode 100644 index 0000000..39808e6 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.cpp @@ -0,0 +1,62 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace ScrollViewHelperFunctions +{ + +bool IsStraightOnView( const Vector3& position ) +{ + return ( fabsf(position.x) < Math::MACHINE_EPSILON_1 ) && ( fabsf( position.y ) < Math::MACHINE_EPSILON_1 ); +} + +void WrapPositionWithinDomain( Vector3& position, const Vector3& pageSize, const Vector3& min, const Vector3& max ) +{ + if( fabsf( min.x - max.x ) > Math::MACHINE_EPSILON_1 ) + { + // WRAP X (based on the position of the right side) + position.x = WrapInDomain( position.x + pageSize.width, min.x, max.x ) - pageSize.width; + } + + if( fabsf( min.y - max.y ) > Math::MACHINE_EPSILON_1 ) + { + // WRAP Y (based on the position of the bottom side) + position.y = WrapInDomain( position.y + pageSize.height, min.y, max.y ) - pageSize.height; + } +} + +bool IsOutsideView( const Vector3& position, const Vector3& pageSize ) +{ + return ( fabsf( position.x ) >= pageSize.width ) || ( fabsf( position.y ) >= pageSize.height ); +} + +} // namespace ScrollViewHelperFunctions + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.h new file mode 100644 index 0000000..c9227d8 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.h @@ -0,0 +1,69 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_HELPER_FUNCTIONS_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_HELPER_FUNCTIONS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/// Some helper methods with common functionality used in scroll view constraints +namespace ScrollViewHelperFunctions +{ + +/** + * Checks whether the we're looking straight at the page and if we are, it returns true. + * + * @param[in] position The position of the page. + */ +bool IsStraightOnView( const Vector3& position ); + +/** + * Modifies the position to wrap within the given domain. + * + * @param[in/out] position The position of the page, this is modified accordingly. + * @param[in] pageSize The size of each page. + * @param[in] min The minimum position of the scroll-view. + * @param[in] max The maximum position of the scroll-view. + */ +void WrapPositionWithinDomain( Vector3& position, const Vector3& pageSize, const Vector3& min, const Vector3& max ); + +/** + * Checks whether the page is positioned outside of our view and returns true if it is. + * + * @param[in] position The position of the page. + * @param[in] pageSize The size of each page. + */ +bool IsOutsideView( const Vector3& position, const Vector3& pageSize ); + +} // namespace ScrollViewHelperFunctions + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_HELPER_FUNCTIONS_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp new file mode 100644 index 0000000..1acf2a6 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp @@ -0,0 +1,2628 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include +#include +#include +#include +#include + +// TODO: Change to two class system: +// 1. DraggableActor (is an actor which can be dragged anywhere/scaled/rotated, can be set to range using the ruler) +// 2. ScrollView (contains a draggable actor that can a) be dragged in the negative X, and Y domain, b) has a hitArea for touches) +// TODO: Rotation +// TODO: Asymetrical scaling +// TODO: external components (page and status overlays). +// TODO: Orientation. +// TODO: upgrade Vector2/3 to support returning Unit vectors, normals, & cross product (dot product is already provided) + +using namespace Dali; + +namespace +{ + +const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; ///< Refresh rate TODO: Animation should have an update signal (and see item-view-impl) +const float FLICK_SPEED_THRESHOLD = 500.0f; ///< Flick threshold in pixels/ms +const float FREE_FLICK_SPEED_THRESHOLD = 200.0f; ///< Free-Flick threshold in pixels/ms +const float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f; ///< Auto-lock axis after minimum distance squared. +const float FLICK_ORTHO_ANGLE_RANGE = 60.0f; ///< degrees. (if >45, then supports diagonal flicking) +const unsigned int MAXIMUM_NUMBER_OF_VALUES = 5; ///< Number of values to use for weighted pan calculation. +const Vector2 DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = Vector2(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each mouse wheel event received. +const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u ); + +// predefined effect values +const Vector3 ANGLE_CAROUSEL_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f); +const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.2f, Math::PI * 0.2f, 0.0f); ///< Cube page rotates as if it has ten sides with the camera positioned inside +const Vector2 ANGLE_CUSTOM_CUBE_SWING(-Math::PI * 0.45f, -Math::PI * 0.45f); ///< outer cube pages swing 90 degrees as they pan offscreen +const Vector2 ANGLE_SPIRAL_SWING_IN(Math::PI * 0.5f, Math::PI * 0.5f); +const Vector2 ANGLE_SPIRAL_SWING_OUT(Math::PI * 0.35f, Math::PI * 0.35f); +const Vector2 ANGLE_OUTER_CUBE_SWING(Math::PI * 0.5f, Math::PI * 0.5f); ///< outer cube pages swing 90 degrees as they pan offscreen + +// Helpers //////////////////////////////////////////////////////////////////////////////////////// + +// TODO: GetAngle for Vector2 can be moved. +// GetAngle for Vector3 needs to be measured against a normal/plane. + +/** + * @param[in] vector The 3D vector to be measured + * @return angle in radians from 0 to 2PI + */ +float GetAngle(const Vector3& vector) +{ + return atan2(vector.y, vector.x) + Math::PI; +} + +/** + * @param[in] vector The 2D vector to be measured + * @return angle in radians from 0 to 2PI + */ +float GetAngle(const Vector2& vector) +{ + return atan2(vector.y, vector.x) + Math::PI; +} + +/** + * Find the vector (distance) from (a) to (b) + * in domain (start) to (end) + * (\ / start) (\ / end) + * |-a b<----| + * + * @note assumes both (a) and (b) are already with the domain + * (start) to (end) + * + * @param[in] a the current point + * @param[in] b the target point + * @param[in] start the start of the domain + * @param[in] end the end of the domain + * @param[in] bias whether to only take the right direction or the left direction, + * or the shortest direction. + * @return the shortest direction and distance + */ +float VectorInDomain(float a, float b, float start, float end, Dali::Toolkit::DirectionBias bias) +{ + if(bias == Dali::Toolkit::DirectionBiasNone) + { + return ShortestDistanceInDomain( a, b, start, end ); + } + // (a-start + end-b) + float size = end-start; + float vect = b-a; + + if(vect > 0) + { + // +ve vector + if(bias == Dali::Toolkit::DirectionBiasRight) // going right, take the vector. + { + return vect; + } + else + { + float aRight = a+size; + return b-aRight; + } + } + else + { + // -ve vector + if(bias == Dali::Toolkit::DirectionBiasLeft) // going left, take the vector. + { + return vect; + } + else + { + float aLeft = a-size; + return b-aLeft; + } + } +} + +/** + * Returns the position of the anchor within actor + * + * @param actor The Actor + * @param anchor The Anchor point of interest. + * @return The position of the Anchor + */ +Vector3 GetPositionOfAnchor(Actor &actor, const Vector3 &anchor) +{ + Vector3 childPosition = actor.GetCurrentPosition(); + Vector3 childAnchor = - actor.GetCurrentAnchorPoint() + anchor; + Vector3 childSize = actor.GetCurrentSize(); + + return childPosition + childAnchor * childSize; +} + +// AlphaFunctions ///////////////////////////////////////////////////////////////////////////////// + +float FinalDefaultAlphaFunction(float offset) +{ + return offset * 0.5f; +} + +/** + * ConstantDecelerationAlphaFunction + * Newtoninan distance for constant deceleration + * v = 1 - t, s = t - 1/2 t^2 + * when t = 0, s = 0.0 (min distance) + * when t = 1, s = 0.5 (max distance) + * progress = s / (max-min) = 2t - t^2 + * + * @param[in] offset The input progress + * @return The output progress + */ +float ConstantDecelerationAlphaFunction(float progress) +{ + return progress * 2.0f - progress * progress; +} + +// Internal Constraints /////////////////////////////////////////////////////////////////////////// + +/** + * Internal Relative position Constraint + * Generates the relative position value of the scroll view + * based on the absolute position, and it's relation to the + * scroll domain. This is a value from 0.0f to 1.0f in each + * scroll position axis. + */ +Vector3 InternalRelativePositionConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollMinProperty, + const PropertyInput& scrollMaxProperty, + const PropertyInput& scrollSizeProperty) +{ + const Vector3& position = -scrollPositionProperty.GetVector3(); + const Vector3& min = scrollMinProperty.GetVector3(); + const Vector3& max = scrollMaxProperty.GetVector3(); + const Vector3& size = scrollSizeProperty.GetVector3(); + + Vector3 relativePosition; + Vector3 domainSize = (max - min) - size; + + relativePosition.x = domainSize.x > Math::MACHINE_EPSILON_1 ? fabsf((position.x - min.x) / domainSize.x) : 0.0f; + relativePosition.y = domainSize.y > Math::MACHINE_EPSILON_1 ? fabsf((position.y - min.y) / domainSize.y) : 0.0f; + + return relativePosition; +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +/** + * Internal Pre-Position Property Constraint. + * + * Generates position property based on current position + gesture displacement. + * Or generates position property based on positionX/Y. + * Note: This is the position prior to any clamping at scroll boundaries. + * TODO: Scale & Rotation Transforms. + */ +struct InternalPrePositionConstraint +{ + InternalPrePositionConstraint(const Vector2& initialPanMask, + bool axisAutoLock, + float axisAutoLockGradient) + : mInitialPanMask(initialPanMask), + mAxisAutoLock(axisAutoLock), + mLockAxis(ScrollView::LockPossible), + mAxisAutoLockGradient(axisAutoLockGradient), + mPrePosition(Vector3::ZERO), + mWasPanning(false) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& gesturePositionProperty, + const PropertyInput& gestureDisplacementProperty, + const PropertyInput& scrollPositionXProperty, + const PropertyInput& scrollPositionYProperty, + const PropertyInput& panningProperty) + { + const bool panning = panningProperty.GetBoolean(); + Vector3 scrollPostPosition; + + if(panning) + { + // Check if panning has just started... + if(!mWasPanning) + { + mLocalStart = gesturePositionProperty.GetVector2() - gestureDisplacementProperty.GetVector2(); + mPrePosition = current; + mLockAxis = ScrollView::LockPossible; + + mCurrentPanMask = mInitialPanMask; + } + + // Calculate Deltas... + Vector2 currentPosition = gesturePositionProperty.GetVector2(); + Vector2 panDelta( currentPosition - mLocalStart ); + + // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan + // appears mostly horizontal or mostly vertical respectively... + AxisAutoLock(panDelta); + + // Restrict deltas based on ruler enable/disable and axis-lock state... + panDelta *= mCurrentPanMask; + + // Perform Position transform based on input deltas... + scrollPostPosition = mPrePosition; + scrollPostPosition.GetVectorXY() += panDelta; + } + else + { + scrollPostPosition.x = scrollPositionXProperty.GetFloat(); + scrollPostPosition.y = scrollPositionYProperty.GetFloat(); + } + + mWasPanning = panning; + return scrollPostPosition; + } + + void AxisAutoLock(Vector2& panDelta) + { + if(mAxisAutoLock) + { + if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 && + mLockAxis == ScrollView::LockPossible) + { + float dx = fabsf(panDelta.x); + float dy = fabsf(panDelta.y); + if(dx * mAxisAutoLockGradient >= dy) + { + // 0.36:1 gradient to the horizontal (deviate < 20 degrees) + mLockAxis = ScrollView::LockVertical; + mCurrentPanMask.y = 0.0f; + } + else if(dy * mAxisAutoLockGradient > dx) + { + // 0.36:1 gradient to the vertical (deviate < 20 degrees) + mLockAxis = ScrollView::LockHorizontal; + mCurrentPanMask.x = 0.0f; + } + else + { + mLockAxis = ScrollView::LockNone; + } + } + } // end if mAxisAutoLock + } + + Vector2 mLocalStart; + Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings) + Vector2 mCurrentPanMask; ///< Current pan mask that can be altered by axis lock mode. + + bool mAxisAutoLock; ///< Set by ScrollView + ScrollView::LockAxis mLockAxis; + float mAxisAutoLockGradient; ///< Set by ScrollView + Vector3 mPrePosition; + bool mWasPanning; +}; + +/** + * Internal Position Property Constraint. + * + * Generates position property based on pre-position + * Note: This is the position after clamping. + * (uses result of InternalPrePositionConstraint) + */ +struct InternalPositionConstraint +{ + InternalPositionConstraint(const RulerDomain& domainX, const RulerDomain& domainY) + : mDomainMin( -domainX.min, -domainY.min ), + mDomainMax( -domainX.max, -domainY.max ), + mClampX( domainX.enabled ), + mClampY( domainY.enabled ) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollSizeProperty) + { + Vector3 position = scrollPositionProperty.GetVector3(); + const Vector2& size = scrollSizeProperty.GetVector3().GetVectorXY(); + + position.x = mClampX ? Clamp(position.x, mDomainMax.x + size.x, mDomainMin.x ) : position.x; + position.y = mClampY ? Clamp(position.y, mDomainMax.y + size.y, mDomainMin.y ) : position.y; + + return position; + } + + Vector2 mDomainMin; + Vector2 mDomainMax; + bool mClampX; + bool mClampY; + +}; + +/** + * This constraint updates the X overshoot property using the difference + * mPropertyPrePosition.x and mPropertyPosition.x, returning a relative value between 0.0f and 1.0f + */ +struct OvershootXConstraint +{ + OvershootXConstraint(float maxOvershoot) : mLastOvershoot(0.0f), mMaxOvershoot(maxOvershoot) {} + + float operator()(const float& current, + const PropertyInput& scrollPrePositionProperty, + const PropertyInput& scrollPostPositionProperty) + { + Vector3 scrollPrePosition = scrollPrePositionProperty.GetVector3(); + Vector3 scrollPostPosition = scrollPostPositionProperty.GetVector3(); + float newOvershoot = scrollPrePosition.x - scrollPostPosition.x; + return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot; + } + + float mLastOvershoot; + float mMaxOvershoot; +}; + +/** + * This constraint updates the Y overshoot property using the difference + * mPropertyPrePosition.y and mPropertyPosition.y, returning a relative value between 0.0f and 1.0f + */ +struct OvershootYConstraint +{ + OvershootYConstraint(float maxOvershoot) : mLastOvershoot(0.0f), mMaxOvershoot(maxOvershoot) {} + + float operator()(const float& current, + const PropertyInput& scrollPrePositionProperty, + const PropertyInput& scrollPostPositionProperty) + { + Vector3 scrollPrePosition = scrollPrePositionProperty.GetVector3(); + Vector3 scrollPostPosition = scrollPostPositionProperty.GetVector3(); + float newOvershoot = scrollPrePosition.y - scrollPostPosition.y; + return (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot; + } + + float mLastOvershoot; + float mMaxOvershoot; +}; + +/** + * When panning, this constraint updates the X property, otherwise + * it has no effect on the X property. + */ +float InternalXConstraint(const float& current, + const PropertyInput& scrollPosition, + const PropertyInput& panningProperty) +{ + return scrollPosition.GetVector3().x; +} + +/** + * When panning, this constraint updates the Y property, otherwise + * it has no effect on the Y property. + */ +float InternalYConstraint(const float& current, + const PropertyInput& scrollPosition, + const PropertyInput& panningProperty) +{ + return scrollPosition.GetVector3().y; +} + +/** + * Internal Position-Delta Property Constraint. + * + * Generates position-delta property based on scroll-position + scroll-offset properties. + */ +Vector3 InternalPositionDeltaConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollOffsetProperty) +{ + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + const Vector3& scrollOffset = scrollOffsetProperty.GetVector3(); + + return scrollPosition + scrollOffset; +} + +/** + * Internal Final Position Constraint + * The position of content is: + * of scroll-position + f(scroll-overshoot) + * where f(...) function defines how overshoot + * should affect final-position. + */ +struct InternalFinalConstraint +{ + InternalFinalConstraint(AlphaFunction functionX, + AlphaFunction functionY) + : mFunctionX(functionX), + mFunctionY(functionY) + { + } + + Vector3 operator()(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollOvershootXProperty, + const PropertyInput& scrollOvershootYProperty) + { + const float& overshootx = scrollOvershootXProperty.GetFloat(); + const float& overshooty = scrollOvershootYProperty.GetFloat(); + Vector3 offset( mFunctionX(overshootx), + mFunctionY(overshooty), + 0.0f); + + return scrollPositionProperty.GetVector3() - offset; + } + + AlphaFunction mFunctionX; + AlphaFunction mFunctionY; +}; + + +BaseHandle Create() +{ + return Toolkit::ScrollView::New(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::ScrollView), typeid(Toolkit::Scrollable), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::ScrollView::SIGNAL_SNAP_STARTED, &ScrollView::DoConnectSignal ); + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ScrollView +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::ScrollView ScrollView::New() +{ + // Create the implementation + ScrollViewPtr scrollView(new ScrollView()); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::ScrollView handle(*scrollView); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + scrollView->Initialize(); + + return handle; +} + +ScrollView::ScrollView() +: ScrollBase(), + mInitialized(false), + mScrolling(false), + mScrollInterrupted(false), + mTouchDownTime(0u), + mSensitive(true), + mGestureStackDepth(0), + mRotationDelta(0.0f), + mScrollPreRotation(0.0f), + mScrollPostRotation(0.0f), + mTouchDownReceived(false), + mActorAutoSnapEnabled(false), + mAutoResizeContainerEnabled(false), + mWrapMode(false), + mAxisAutoLock(false), + mMinTouchesForPanning(1), + mMaxTouchesForPanning(1), + mLockAxis(LockPossible), + mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS), + mAlterChild(false), + mOvershootDelay(1.0f), + mMaxOvershoot(Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT, Toolkit::ScrollView::DEFAULT_MAX_OVERSHOOT), + mDefaultMaxOvershoot(true), + mSnapOvershootDuration(Toolkit::ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION), + mSnapOvershootAlphaFunction(AlphaFunctions::EaseOut), + mSnapDuration(Toolkit::ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION), + mSnapAlphaFunction(AlphaFunctions::EaseOut), + mFlickDuration(Toolkit::ScrollView::DEFAULT_FAST_SNAP_ANIMATION_DURATION), + mFlickAlphaFunction(AlphaFunctions::EaseOut), + mAxisAutoLockGradient(Toolkit::ScrollView::DEFAULT_AXIS_AUTO_LOCK_GRADIENT), + mFrictionCoefficient(Toolkit::ScrollView::DEFAULT_FRICTION_COEFFICIENT), + mFlickSpeedCoefficient(Toolkit::ScrollView::DEFAULT_FLICK_SPEED_COEFFICIENT), + mMaxFlickSpeed(Toolkit::ScrollView::DEFAULT_MAX_FLICK_SPEED) +{ + SetRequiresMouseWheelEvents(true); +} + +void ScrollView::OnInitialize() +{ + Actor self = Self(); + self.SetLeaveRequired(true); + + // Internal Actor, used to hide actors from enumerations. + // Also actors added to Internal actor appear as overlays e.g. ScrollBar components. + mInternalActor = Actor::New(); + mInternalActor.SetDrawMode(DrawMode::OVERLAY); + self.Add(mInternalActor); + mInternalActor.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mInternalActor.SetParentOrigin(ParentOrigin::CENTER); + mInternalActor.SetAnchorPoint(AnchorPoint::CENTER); + + mAlterChild = true; + + // Register Scroll Properties. + RegisterProperties(); + + mScrollPostPosition = mScrollPrePosition = Vector3::ZERO; + mScrollPostScale = mScrollPreScale = Vector3::ONE; + mScrollPostRotation = mScrollPreRotation = 0.0f; + + mMouseWheelScrollDistanceStep = Stage::GetCurrent().GetSize() * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION; + + mInitialized = true; + + mGestureStackDepth = 0; + + EnableGestureDetection( Gesture::Type( Gesture::Pan | Gesture::Pinch ) ); + + // For pan, default to only 1 touch required, ignoring touches outside this range. + SetTouchesRequiredForPanning(1, 1, false); + + // By default we'll allow the user to freely drag the scroll view, + // while disabling the other rulers. + RulerPtr ruler = new DefaultRuler(); + RulerPtr rulerDisabled = new DefaultRuler(); + rulerDisabled->Disable(); + mRulerX = ruler; + mRulerY = ruler; + mRulerScaleX = rulerDisabled; + mRulerScaleY = rulerDisabled; + mRulerRotation = rulerDisabled; + + EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator); + + Vector3 size = GetControlSize(); + UpdatePropertyDomain(size); + SetInternalConstraints(); +} + +void ScrollView::OnControlStageConnection() +{ + if ( mSensitive ) + { + SetScrollSensitive( false ); + SetScrollSensitive( true ); + } + if(IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator)) + { + // try and make sure property notifications are set + EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator); + } +} + +void ScrollView::OnControlStageDisconnection() +{ + if ( mSnapOvershootAnimation ) + { + SetOvershootToOrigin(); + } + + StopAnimation(); +} + +ScrollView::~ScrollView() +{ +} + +AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const +{ + return mSnapAlphaFunction; +} + +void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha) +{ + mSnapAlphaFunction = alpha; +} + +AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const +{ + return mFlickAlphaFunction; +} + +void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha) +{ + mFlickAlphaFunction = alpha; +} + +float ScrollView::GetScrollSnapDuration() const +{ + return mSnapDuration; +} + +void ScrollView::SetScrollSnapDuration(float time) +{ + mSnapDuration = time; +} + +float ScrollView::GetScrollFlickDuration() const +{ + return mFlickDuration; +} + +void ScrollView::SetScrollFlickDuration(float time) +{ + mFlickDuration = time; +} + +void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect) +{ + Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self()); + + // Assertion check to ensure effect doesn't already exist in this scrollview + bool effectAlreadyExistsInScrollView(false); + for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) + { + if(*iter==effect) + { + effectAlreadyExistsInScrollView = true; + break; + } + } + + DALI_ASSERT_ALWAYS(!effectAlreadyExistsInScrollView); + + // add effect to effects list + mEffects.push_back(effect); + + // invoke Attachment request to ScrollView first + GetImpl(effect).Attach(self); +} + +Toolkit::ScrollViewEffect ScrollView::ApplyEffect(Toolkit::ScrollView::PageEffect effect) +{ + Toolkit::ScrollViewEffect scrollEffect; + switch(effect) + { + case Toolkit::ScrollView::PageEffectNone: + { + break; + } + case Toolkit::ScrollView::PageEffectOuterCube: + { + Toolkit::ScrollViewCustomEffect customEffect; + scrollEffect = customEffect = Toolkit::ScrollViewCustomEffect::New(); + Vector2 pageSize = Stage::GetCurrent().GetSize(); + // set the page translation to the slide off distance, also add an extra value to space the pages, having a smaller spacing on translationOut will allow the spacing to reduce over time + // the page moving onto screen will start 50.0f further out (1.0f * 50.0f) and the spacing will reduce as its position reaches the centre (0.0f * 50.0f) + // the page moving off screen will slowly build a spacing from 0.0f to 20.0f + // the spacing from each page is added together for the final spacing between the two pages. + customEffect.SetPageTranslation(Vector3(pageSize.x, pageSize.y, 0) + Vector3(50.0f, 50.0f, 0.0f), Vector3(pageSize.x, pageSize.y, 0) + Vector3(20.0f, 20.0f, 0.0f)); + customEffect.SetSwingAngleOut(ANGLE_CUSTOM_CUBE_SWING.x, Vector3(0.0f, -1.0f, 0.0f)); + customEffect.SetSwingAnchor(AnchorPoint::CENTER, AnchorPoint::CENTER_LEFT); + customEffect.SetOpacityThreshold(0.7f); + break; + } + case Toolkit::ScrollView::PageEffectDepth: + { + Toolkit::ScrollViewCustomEffect customEffect; + scrollEffect = customEffect = Toolkit::ScrollViewCustomEffect::New(); + break; + } + case Toolkit::ScrollView::PageEffectInnerCube: + { + Toolkit::ScrollViewCustomEffect customEffect; + scrollEffect = customEffect = Toolkit::ScrollViewCustomEffect::New(); + customEffect.SetPageSpacing(Vector2(30.0f, 30.0f)); + customEffect.SetAngledOriginPageRotation(ANGLE_CUBE_PAGE_ROTATE); + customEffect.SetSwingAngle(ANGLE_CUBE_PAGE_ROTATE.x, Vector3(0,-1,0)); + customEffect.SetOpacityThreshold(0.5f); + break; + } + case Toolkit::ScrollView::PageEffectCarousel: + { + Toolkit::ScrollViewCustomEffect customEffect; + scrollEffect = customEffect = Toolkit::ScrollViewCustomEffect::New(); + customEffect.SetPageTranslation(Vector3(0,0,0), Vector3(-30, 0, 0)); + customEffect.SetPageSpacing(Vector2(60.0f, 60.0f)); + customEffect.SetAngledOriginPageRotation(-ANGLE_CUBE_PAGE_ROTATE); + customEffect.SetOpacityThreshold(0.2f, 0.6f); + break; + } + case Toolkit::ScrollView::PageEffectSpiral: + { + Toolkit::ScrollViewCustomEffect customEffect; + scrollEffect = customEffect = Toolkit::ScrollViewCustomEffect::New(); + + Vector2 pageSize = Stage::GetCurrent().GetSize(); + customEffect.SetSwingAngle(-ANGLE_SPIRAL_SWING_IN.x, Vector3(0.0f, -1.0f, 0.0f), ANGLE_SPIRAL_SWING_OUT.x, Vector3(0.0f, -1.0f, 0.0f)); + //customEffect.SetSwingAngleAlphaFunctionOut(AlphaFunctions::EaseOut); + customEffect.SetSwingAnchor(AnchorPoint::CENTER_RIGHT); + customEffect.SetPageTranslation(Vector3(pageSize.x, pageSize.y, 0) + Vector3(100.0f, 100.0f, 0.0f), Vector3(pageSize.x, pageSize.y, -pageSize.y * 2.0f) * 0.33f); + //customEffect.SetPageTranslateAlphaFunctionOut(AlphaFunctions::EaseOut); + customEffect.SetOpacityThreshold(0.75f, 0.6f); + customEffect.SetOpacityAlphaFunctionIn(AlphaFunctions::EaseInOut); + break; + } + default: + { + DALI_ASSERT_DEBUG(0 && "unknown scroll view effect"); + } + } + RemoveConstraintsFromChildren(); + if(scrollEffect) + { + ApplyEffect(scrollEffect); + } + return scrollEffect; +} + +void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect) +{ + Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self()); + + // remove effect from effects list + bool effectExistedInScrollView(false); + for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) + { + if(*iter==effect) + { + mEffects.erase(iter); + effectExistedInScrollView = true; + break; + } + } + + // Assertion check to ensure effect existed. + DALI_ASSERT_ALWAYS(effectExistedInScrollView); + + // invoke Detachment request to ScrollView last + GetImpl(effect).Detach(self); +} + +void ScrollView::RemoveAllEffects() +{ + Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self()); + + for (ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter) + { + Toolkit::ScrollViewEffect effect = *effectIter; + + // invoke Detachment request to ScrollView last + GetImpl(effect).Detach(self); + } + + mEffects.clear(); +} + +void ScrollView::ApplyConstraintToChildren(Constraint constraint) +{ + ApplyConstraintToBoundActors(constraint); +} + +void ScrollView::RemoveConstraintsFromChildren() +{ + RemoveConstraintsFromBoundActors(); +} + +const RulerPtr ScrollView::GetRulerX() const +{ + return mRulerX; +} + +const RulerPtr ScrollView::GetRulerY() const +{ + return mRulerY; +} + +void ScrollView::SetRulerX(RulerPtr ruler) +{ + mRulerX = ruler; + + Vector3 size = GetControlSize(); + UpdatePropertyDomain(size); + UpdateMainInternalConstraint(); +} + +void ScrollView::SetRulerY(RulerPtr ruler) +{ + mRulerY = ruler; + + Vector3 size = GetControlSize(); + UpdatePropertyDomain(size); + UpdateMainInternalConstraint(); +} + +void ScrollView::UpdatePropertyDomain(const Vector3& size) +{ + Vector3 min; + Vector3 max; + + bool canScrollVertical = false; + bool canScrollHorizontal = false; + Actor self = Self(); + if(mRulerX->IsEnabled()) + { + const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain(); + min.x = rulerDomain.min; + max.x = rulerDomain.max; + + // make sure new scroll value is within new domain + float newScroll = min.x; + int scrollXPropertyIndex = self.GetPropertyIndex(Toolkit::ScrollView::SCROLL_X_PROPERTY_NAME); + if((fabsf(max.x - min.x) - size.x) > Math::MACHINE_EPSILON_1) + { + canScrollHorizontal = true; + float currentScroll = self.GetProperty(scrollXPropertyIndex); + newScroll = Clamp(currentScroll, -(max.x - size.x), -min.x); + } + self.SetProperty(scrollXPropertyIndex, newScroll); + } + + if(mRulerY->IsEnabled()) + { + const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain(); + min.y = rulerDomain.min; + max.y = rulerDomain.max; + + // make sure new scroll value is within new domain + float newScroll = min.y; + int scrollYPropertyIndex = self.GetPropertyIndex(Toolkit::ScrollView::SCROLL_Y_PROPERTY_NAME); + if((fabsf(max.y - min.y) - size.y) > Math::MACHINE_EPSILON_1) + { + canScrollVertical = true; + float currentScroll = self.GetProperty(scrollYPropertyIndex); + newScroll = Clamp(currentScroll, -(max.y - size.y), -min.y); + } + self.SetProperty(scrollYPropertyIndex, newScroll); + } + self.SetProperty(mPropertyCanScrollVertical, canScrollVertical); + self.SetProperty(mPropertyCanScrollHorizontal, canScrollHorizontal); + + self.SetProperty(mPropertyPositionMin, min ); + self.SetProperty(mPropertyPositionMax, max ); +} + +void ScrollView::SetRulerScaleX(RulerPtr ruler) +{ + mRulerScaleX = ruler; + UpdateMainInternalConstraint(); +} + +void ScrollView::SetRulerScaleY(RulerPtr ruler) +{ + mRulerScaleY = ruler; + UpdateMainInternalConstraint(); +} + +void ScrollView::SetRulerRotation(RulerPtr ruler) +{ + mRulerRotation = ruler; + UpdateMainInternalConstraint(); +} + +void ScrollView::SetScrollSensitive(bool sensitive) +{ + Actor self = Self(); + PanGestureDetector panGesture( GetPanGestureDetector() ); + PinchGestureDetector pinchGesture( GetPinchGestureDetector() ); + + if((!mSensitive) && (sensitive)) + { + mSensitive = sensitive; + panGesture.Attach(self); + pinchGesture.Attach(self); + } + else if((mSensitive) && (!sensitive)) + { + mSensitive = sensitive; + panGesture.Detach(self); + pinchGesture.Detach(self); + + mGestureStackDepth = 0; + self.SetProperty(mPropertyPanning, false); + + // Remove X & Y position constraints as they are not required when we are not panning. + self.RemoveConstraint(mScrollMainInternalXConstraint); + self.RemoveConstraint(mScrollMainInternalYConstraint); + } +} + +void ScrollView::SetMaxOvershoot(float overshootX, float overshootY) +{ + mMaxOvershoot.x = overshootX; + mMaxOvershoot.y = overshootY; + mDefaultMaxOvershoot = false; + UpdateMainInternalConstraint(); +} + +void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha) +{ + mSnapOvershootAlphaFunction = alpha; +} + +void ScrollView::SetSnapOvershootDuration(float duration) +{ + mSnapOvershootDuration = duration; +} + +void ScrollView::SetTouchesRequiredForPanning(unsigned int minTouches, unsigned int maxTouches, bool endOutside) +{ + PanGestureDetector panGesture( GetPanGestureDetector() ); + + mMinTouchesForPanning = minTouches; + mMaxTouchesForPanning = maxTouches; + + if(endOutside) + { + panGesture.SetMinimumTouchesRequired(minTouches); + panGesture.SetMaximumTouchesRequired(maxTouches); + } + else + { + panGesture.SetMinimumTouchesRequired(1); + panGesture.SetMaximumTouchesRequired(UINT_MAX); + } +} + +void ScrollView::SetActorAutoSnap(bool enable) +{ + mActorAutoSnapEnabled = enable; +} + +void ScrollView::SetAutoResize(bool enable) +{ + mAutoResizeContainerEnabled = enable; + // TODO: This needs a lot of issues to be addressed before working. +} + +bool ScrollView::GetWrapMode() const +{ + return mWrapMode; +} + +void ScrollView::SetWrapMode(bool enable) +{ + mWrapMode = enable; + Self().SetProperty(mPropertyWrap, enable); +} + +int ScrollView::GetRefreshInterval() const +{ + return mRefreshIntervalMilliseconds; +} + +void ScrollView::SetRefreshInterval(int milliseconds) +{ + mRefreshIntervalMilliseconds = milliseconds; +} + +bool ScrollView::GetAxisAutoLock() const +{ + return mAxisAutoLock; +} + +void ScrollView::SetAxisAutoLock(bool enable) +{ + mAxisAutoLock = enable; + UpdateMainInternalConstraint(); +} + +float ScrollView::GetAxisAutoLockGradient() const +{ + return mAxisAutoLockGradient; +} + +void ScrollView::SetAxisAutoLockGradient(float gradient) +{ + DALI_ASSERT_DEBUG( gradient >= 0.0f && gradient <= 1.0f ); + mAxisAutoLockGradient = gradient; + UpdateMainInternalConstraint(); +} + +float ScrollView::GetFrictionCoefficient() const +{ + return mFrictionCoefficient; +} + +void ScrollView::SetFrictionCoefficient(float friction) +{ + DALI_ASSERT_DEBUG( friction > 0.0f ); + mFrictionCoefficient = friction; +} + +float ScrollView::GetFlickSpeedCoefficient() const +{ + return mFlickSpeedCoefficient; +} + +void ScrollView::SetFlickSpeedCoefficient(float speed) +{ + mFlickSpeedCoefficient = speed; +} + +float ScrollView::GetMaxFlickSpeed() const +{ + return mMaxFlickSpeed; +} + +void ScrollView::SetMaxFlickSpeed(float speed) +{ + mMaxFlickSpeed = speed; +} + +void ScrollView::SetMouseWheelScrollDistanceStep(Vector2 step) +{ + mMouseWheelScrollDistanceStep = step; +} + +Vector2 ScrollView::GetMouseWheelScrollDistanceStep() const +{ + return mMouseWheelScrollDistanceStep; +} + +unsigned int ScrollView::GetCurrentPage() const +{ + // in case animation is currently taking place. + Vector3 position = GetPropertyPrePosition(); + + Actor self = Self(); + unsigned int page = 0; + unsigned int pagesPerVolume = 1; + unsigned int volume = 0; + + // if rulerX is enabled, then get page count (columns) + page = mRulerX->GetPageFromPosition(-position.x, mWrapMode); + volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode); + pagesPerVolume = mRulerX->GetTotalPages(); + + return volume * pagesPerVolume + page; +} + +Vector3 ScrollView::GetCurrentScrollPosition() const +{ + // in case animation is currently taking place. + return -GetPropertyPrePosition(); +} + +Vector3 ScrollView::GetCurrentScrollScale() const +{ + // in case animation is currently taking place. + return GetPropertyScale(); +} + +Vector3 ScrollView::GetDomainSize() const +{ + Vector3 size = Self().GetCurrentSize(); + + const RulerDomain& xDomain = GetRulerX()->GetDomain(); + const RulerDomain& yDomain = GetRulerY()->GetDomain(); + + Vector3 domainSize = Vector3( xDomain.max - xDomain.min, yDomain.max - yDomain.min, 0.0f ) - size; + return domainSize; +} + +void ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation, + DirectionBias horizontalBias, DirectionBias verticalBias) +{ + TransformTo(position, scale, rotation, mSnapDuration, horizontalBias, verticalBias); +} + +void ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation, float duration, + DirectionBias horizontalBias, DirectionBias verticalBias) +{ + // Guard against destruction during signal emission + // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo() + Toolkit::ScrollView handle( GetOwner() ); + + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + Self().SetProperty( mPropertyScrollStartPagePosition, currentScrollPosition ); + + if(mScrolling) // are we interrupting a current scroll? + { + // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete. + mScrolling = false; + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + } + + Self().SetProperty(mPropertyScrolling, true); + mScrolling = true; + mScrollStartedSignalV2.Emit( currentScrollPosition ); + bool animating = AnimateTo(-position, + Vector3::ONE * duration, + scale, + Vector3::ONE * duration, + rotation, + duration, + mSnapAlphaFunction, + true, + horizontalBias, + verticalBias, + Snap); + + if(!animating) + { + // if not animating, then this pan has completed right now. + Self().SetProperty(mPropertyScrolling, false); + mScrolling = false; + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + } +} + +void ScrollView::ScrollTo(const Vector3& position) +{ + ScrollTo(position, mSnapDuration ); +} + +void ScrollView::ScrollTo(const Vector3& position, float duration) +{ + ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone); +} + +void ScrollView::ScrollTo(const Vector3& position, float duration, + DirectionBias horizontalBias, DirectionBias verticalBias) +{ + TransformTo(position, mScrollPostScale, mScrollPostRotation, duration, horizontalBias, verticalBias); +} + +void ScrollView::ScrollTo(unsigned int page) +{ + ScrollTo(page, mSnapDuration); +} + +void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias) +{ + Vector3 position; + unsigned int volume; + unsigned int libraries; + + // The position to scroll to is continuous and linear + // unless a domain has been enabled on the X axis. + // or if WrapMode has been enabled. + bool carryX = mRulerX->GetDomain().enabled | mWrapMode; + bool carryY = mRulerY->GetDomain().enabled | mWrapMode; + + position.x = mRulerX->GetPositionFromPage(page, volume, carryX); + position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY); + + ScrollTo(position, duration, bias, bias); +} + +void ScrollView::ScrollTo(Actor &actor) +{ + ScrollTo(actor, mSnapDuration); +} + +void ScrollView::ScrollTo(Actor &actor, float duration) +{ + DALI_ASSERT_ALWAYS(actor.GetParent() == Self()); + + Actor self = Self(); + Vector3 size = self.GetCurrentSize(); + Vector3 position = actor.GetCurrentPosition(); + position -= GetPropertyPrePosition(); + + ScrollTo(Vector3(position.x - size.width * 0.5f, position.y - size.height * 0.5f, 0.0f), duration); +} + +Actor ScrollView::FindClosestActor() +{ + Actor self = Self(); + Vector3 size = self.GetCurrentSize(); + + return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f)); +} + +Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ) +{ + Actor closestChild; + float closestDistance2 = 0.0f; + Vector3 actualPosition = position; + + unsigned int numChildren = Self().GetChildCount(); + + for(unsigned int i = 0; i < numChildren; ++i) + { + Actor child = Self().GetChildAt(i); + + if(mInternalActor == child) // ignore internal actor. + { + continue; + } + + Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); + + Vector3 delta = childPosition - actualPosition; + + // X-axis checking (only find Actors to the [dirX] of actualPosition) + if(dirX > All) // != All,None + { + FindDirection deltaH = delta.x > 0 ? Right : Left; + if(dirX != deltaH) + { + continue; + } + } + + // Y-axis checking (only find Actors to the [dirY] of actualPosition) + if(dirY > All) // != All,None + { + FindDirection deltaV = delta.y > 0 ? Down : Up; + if(dirY != deltaV) + { + continue; + } + } + + // Z-axis checking (only find Actors to the [dirZ] of actualPosition) + if(dirZ > All) // != All,None + { + FindDirection deltaV = delta.y > 0 ? In : Out; + if(dirZ != deltaV) + { + continue; + } + } + + // compare child to closest child in terms of distance. + float distance2 = 0.0f; + + // distance2 = the Square of the relevant dimensions of delta + if(dirX != None) + { + distance2 += delta.x * delta.x; + } + + if(dirY != None) + { + distance2 += delta.y * delta.y; + } + + if(dirZ != None) + { + distance2 += delta.z * delta.z; + } + + if(closestChild) // Next time. + { + if(distance2 < closestDistance2) + { + closestChild = child; + closestDistance2 = distance2; + } + } + else // First time. + { + closestChild = child; + closestDistance2 = distance2; + } + } + + return closestChild; +} + +bool ScrollView::ScrollToSnapPoint() +{ + Vector2 stationaryVelocity = Vector2(0.0f, 0.0f); + return SnapWithVelocity( stationaryVelocity ); +} + +void ScrollView::ScaleTo(const Vector3& scale) +{ + ScaleTo(scale, mSnapDuration); +} + +void ScrollView::ScaleTo(const Vector3& scale, float duration) +{ + TransformTo(mScrollPostPosition, scale, mScrollPostRotation, duration); +} + + +// TODO: In situations where axes are different (X snap, Y free) +// Each axis should really have their own independent animation (time and equation) +// Consider, X axis snapping to nearest grid point (EaseOut over fixed time) +// Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time) +// Currently, the axes have been split however, they both use the same EaseOut equation. +bool ScrollView::SnapWithVelocity(Vector2 velocity) +{ + // Animator takes over now, touches are assumed not to interfere. + // And if touches do interfere, then we'll stop animation, update PrePosition + // to current mScroll's properties, and then resume. + // Note: For Flicking this may work a bit different... + + float angle = atan2(velocity.y, velocity.x); + float speed2 = velocity.LengthSquared(); + AlphaFunction alphaFunction = mSnapAlphaFunction; + Vector3 positionDuration = Vector3::ONE * mSnapDuration; + Vector3 scaleDuration = Vector3::ONE * mSnapDuration; + float rotationDuration = mSnapDuration; + float biasX = 0.5f; + float biasY = 0.5f; + FindDirection horizontal = None; + FindDirection vertical = None; + + // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction + // that will be accepted as a general N,E,S,W flick direction. + + const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f; + const float flickSpeedThreshold2 = FLICK_SPEED_THRESHOLD*FLICK_SPEED_THRESHOLD; + + // Flick logic X Axis + + if(mRulerX->IsEnabled()) + { + horizontal = All; + + if(speed2 > flickSpeedThreshold2) // exceeds flick threshold + { + if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East + { + biasX = 0.0f, horizontal = Left; + } + else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West + { + biasX = 1.0f, horizontal = Right; + } + } + } + + // Flick logic Y Axis + + if(mRulerY->IsEnabled()) + { + vertical = All; + + if(speed2 > flickSpeedThreshold2) // exceeds flick threshold + { + if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South + { + biasY = 0.0f, vertical = Up; + } + else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North + { + biasY = 1.0f, vertical = Down; + } + } + } + + // isFlick: Whether this gesture is a flick or not. + bool isFlick = (horizontal != All || vertical != All); + // isFreeFlick: Whether this gesture is a flick under free panning criteria. + bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD); + + if(isFlick || isFreeFlick) + { + positionDuration = Vector3::ONE * mFlickDuration; + alphaFunction = mFlickAlphaFunction; + } + + // Position Snap //////////////////////////////////////////////////////////// + Vector3 positionSnap = mScrollPostPosition; + + if(mActorAutoSnapEnabled) + { + Vector3 size = Self().GetCurrentSize(); + + Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical ); + + if(!child && isFlick ) + { + // If we conducted a direction limited search and found no actor, then just snap to the closest actor. + child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) ); + } + + if(child) + { + Vector3 position = Self().GetProperty(mPropertyPosition); + + // Get center-point of the Actor. + Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); + + if(mRulerX->IsEnabled()) + { + positionSnap.x = position.x - childPosition.x + size.width * 0.5f; + } + if(mRulerY->IsEnabled()) + { + positionSnap.y = position.y - childPosition.y + size.height * 0.5f; + } + } + } + + Vector3 startPosition = positionSnap; + positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system. + positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. + + Vector3 clampDelta(Vector3::ZERO); + ClampPosition(positionSnap); + + if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free) + && isFreeFlick && !mActorAutoSnapEnabled) + { + // Calculate target position based on velocity of flick. + + // a = Deceleration (Set to diagonal stage length * friction coefficient) + // u = Initial Velocity (Flick velocity) + // v = 0 (Final Velocity) + // t = Time (Velocity / Deceleration) + Vector2 stageSize = Stage::GetCurrent().GetSize(); + float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length(); + float a = (stageLength * mFrictionCoefficient); + Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient; + float speed = u.Length(); + u/= speed; + + // TODO: Change this to a decay function. (faster you flick, the slower it should be) + speed = std::min(speed, stageLength * mMaxFlickSpeed ); + u*= speed; + alphaFunction = ConstantDecelerationAlphaFunction; + + float t = speed / a; + + if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free) + { + positionSnap.x += t*u.x*0.5f; + } + + if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free) + { + positionSnap.y += t*u.y*0.5f; + } + + clampDelta = positionSnap; + ClampPosition(positionSnap); + if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0) + { + clampDelta -= positionSnap; + clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x); + clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y); + } + else + { + clampDelta = Vector3::ZERO; + } + + // If Axis is Free and has velocity, then calculate time taken + // to reach target based on velocity in axis. + if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free) + { + float deltaX = fabsf(startPosition.x - positionSnap.x); + + if(fabsf(u.x) > Math::MACHINE_EPSILON_1) + { + positionDuration.x = fabsf(deltaX / u.x); + } + else + { + positionDuration.x = 0; + } + } + + if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free) + { + float deltaY = fabsf(startPosition.y - positionSnap.y); + + if(fabsf(u.y) > Math::MACHINE_EPSILON_1) + { + positionDuration.y = fabsf(deltaY / u.y); + } + else + { + positionDuration.y = 0; + } + } + } + positionSnap += clampDelta; + + // Scale Snap /////////////////////////////////////////////////////////////// + Vector3 scaleSnap = mScrollPostScale; + + scaleSnap.x = mRulerScaleX->Snap(scaleSnap.x); + scaleSnap.y = mRulerScaleY->Snap(scaleSnap.y); + + ClampScale(scaleSnap); + + // Rotation Snap //////////////////////////////////////////////////////////// + float rotationSnap = mScrollPostRotation; + // TODO: implement rotation snap + + bool animating = AnimateTo(positionSnap, positionDuration, + scaleSnap, scaleDuration, + rotationSnap, rotationDuration, + alphaFunction, false, + DirectionBiasNone, DirectionBiasNone, + isFlick || isFreeFlick ? Flick : Snap); + + if(animating) + { + AnimateOvershootToOrigin(positionDuration.x, positionDuration.y); + } + + return animating; +} + +void ScrollView::StopAnimation(void) +{ + // Clear Snap animation if exists. + if(mSnapAnimation) + { + mSnapAnimation.Stop(); + mSnapAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapAnimationFinished); + mSnapAnimation.Clear(); + mSnapAnimation = NULL; + } + if(mSnapXAnimation) + { + mSnapXAnimation.Stop(); + mSnapXAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapXAnimationFinished); + mSnapXAnimation.Clear(); + mSnapXAnimation = NULL; + } + if(mSnapYAnimation) + { + mSnapYAnimation.Stop(); + mSnapYAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapYAnimationFinished); + mSnapYAnimation.Clear(); + mSnapYAnimation = NULL; + } + if(mSnapOvershootAnimation) + { + mSnapOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapOvershootAnimationFinished); + mSnapOvershootAnimation.Stop(); + mSnapOvershootAnimation.Clear(); + mSnapOvershootAnimation = NULL; + } + HandleStoppedAnimation(); +} + +bool ScrollView::AnimateTo(const Vector3& position, const Vector3& positionDuration, + const Vector3& scale, const Vector3& scaleDuration, + float rotation, float rotationDuration, + AlphaFunction alpha, bool findShortcuts, + DirectionBias horizontalBias, DirectionBias verticalBias, + SnapType snapType) +{ + // Here we perform an animation on a number of properties (depending on which have changed) + // The animation is applied to all ScrollBases + Actor self = Self(); + bool startAnimation = false; + Vector3 positionTransformed = position; + float totalDuration = 0.0f; + + bool positionChanged = (positionTransformed != mScrollPostPosition); + bool scaleChanged = (scale != mScrollPostScale); + bool rotationChanged = fabsf(rotation - mScrollPostRotation) > Math::MACHINE_EPSILON_0; + + if(positionChanged) + { + totalDuration = std::max(totalDuration, positionDuration.x); + totalDuration = std::max(totalDuration, positionDuration.y); + } + + if(scaleChanged) + { + totalDuration = std::max(totalDuration, scaleDuration.x); + totalDuration = std::max(totalDuration, scaleDuration.y); + } + + if(rotationChanged) + { + totalDuration = std::max(totalDuration, rotationDuration); + } + + if(totalDuration > Math::MACHINE_EPSILON_1) + { + StopAnimation(); + mSnapAnimation = Animation::New(totalDuration); + mSnapAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapAnimationFinished); + mSnapXAnimation = Animation::New(positionDuration.x); + mSnapXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapXAnimationFinished); + mSnapYAnimation = Animation::New(positionDuration.y); + mSnapYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapYAnimationFinished); + startAnimation = true; + + // Position Delta /////////////////////////////////////////////////////// + if(positionChanged) + { + if(mWrapMode && findShortcuts) + { + // In Wrap Mode, the shortest distance is a little less intuitive... + const RulerDomain rulerDomainX = mRulerX->GetDomain(); + const RulerDomain rulerDomainY = mRulerY->GetDomain(); + + if(mRulerX->IsEnabled()) + { + float dir = VectorInDomain(-mScrollPostPosition.x, -positionTransformed.x, rulerDomainX.min, rulerDomainX.max, horizontalBias); + positionTransformed.x = mScrollPostPosition.x + -dir; + } + + if(mRulerY->IsEnabled()) + { + float dir = VectorInDomain(-mScrollPostPosition.y, -positionTransformed.y, rulerDomainY.min, rulerDomainY.max, verticalBias); + positionTransformed.y = mScrollPostPosition.y + -dir; + } + } + + // note we have two separate animations for X & Y, this deals with sliding diagonally and hitting + // a horizonal/vertical wall.delay + mSnapXAnimation.AnimateTo( Property(self, mPropertyX), positionTransformed.x, alpha, TimePeriod(0.0f, positionDuration.x)); + mSnapYAnimation.AnimateTo( Property(self, mPropertyY), positionTransformed.y, alpha, TimePeriod(0.0f, positionDuration.y)); + } + + // Scale Delta /////////////////////////////////////////////////////// + if(scaleChanged) + { + // TODO: for non-uniform scaling to different bounds e.g. scaling a square to a 4:3 aspect ratio screen with a velocity + // the height will hit first, and then the width, so that would require two different animation times just like position. + mSnapAnimation.AnimateTo( Property(self, mPropertyScale), scale, alpha, TimePeriod(0.0f, scaleDuration.x)); + } + + mSnapAnimation.AnimateTo( Property(self, mPropertyTime), totalDuration, AlphaFunctions::Linear ); + + mSnapAnimation.Play(); + mSnapXAnimation.Play(); + mSnapYAnimation.Play(); + StartRefreshTimer(); + } // end if(totalDuration > Math::MACHINE_EPSILON_1) + else // totalDuration == 0 + { + // instantly set transform. + if(positionChanged) + { + self.SetProperty(mPropertyX, positionTransformed.x); + self.SetProperty(mPropertyY, positionTransformed.y); + + mScrollPrePosition = mScrollPostPosition = positionTransformed; + } + + if(scaleChanged) + { + self.SetProperty(mPropertyScale, scale); + + mScrollPreScale = mScrollPostScale = scale; + } + } + + // Always send a snap event when AnimateTo is called. + Toolkit::ScrollView::SnapEvent snapEvent; + snapEvent.type = snapType; + snapEvent.position = positionTransformed; + snapEvent.scale = scale; + snapEvent.rotation = rotation; + snapEvent.duration = totalDuration; + + mSnapStartedSignalV2.Emit( snapEvent ); + + return startAnimation; +} + +void ScrollView::SetOvershootEnabled(bool enabled) +{ + if(enabled && !mOvershootIndicator) + { + mOvershootIndicator = ScrollOvershootIndicator::New(*this); + } + mOvershootIndicator->Enable(enabled); +} + +void ScrollView::AddOverlay(Actor actor) +{ + mInternalActor.Add( actor ); +} + +void ScrollView::RemoveOverlay(Actor actor) +{ + mInternalActor.Remove( actor ); +} + +void ScrollView::SetScrollingDirection( Radian direction, Radian threshold ) +{ + PanGestureDetector panGesture( GetPanGestureDetector() ); + + // First remove just in case we have some set, then add. + panGesture.RemoveDirection( direction ); + panGesture.AddDirection( direction, threshold ); +} + +void ScrollView::RemoveScrollingDirection( Radian direction ) +{ + PanGestureDetector panGesture( GetPanGestureDetector() ); + panGesture.RemoveDirection( direction ); +} + +Toolkit::ScrollView::SnapStartedSignalV2& ScrollView::SnapStartedSignal() +{ + return mSnapStartedSignalV2; +} + +void ScrollView::FindAndUnbindActor(Actor child) +{ + UnbindActor(child); +} + +Vector3 ScrollView::GetPropertyPrePosition() const +{ + Vector3 position(Self().GetProperty(mPropertyX), Self().GetProperty(mPropertyY), 0.0f); + WrapPosition(position); + + return position; +} + +Vector3 ScrollView::GetPropertyPosition() const +{ + Vector3 position = Self().GetProperty(mPropertyPosition); + WrapPosition(position); + + return position; +} + +Vector3 ScrollView::GetPropertyScale() const +{ + return Self().GetProperty(mPropertyScale); +} + +void ScrollView::HandleStoppedAnimation() +{ + // Animation has stopped, so stop sending the scroll-update signal. + CancelRefreshTimer(); + + // cement transform now, and allow interactivity to resume. + mScrollPostPosition = GetPropertyPosition(); + + mScrollPostScale = GetPropertyScale(); + + // Update Actor position with this wrapped value. + + Self().SetProperty(mPropertyX, mScrollPostPosition.x); + Self().SetProperty(mPropertyY, mScrollPostPosition.y); + // TODO Rotation + + mScrollPrePosition = mScrollPostPosition; + mScrollPreScale = mScrollPostScale; + mScrollPreRotation = mScrollPostRotation; +} + +void ScrollView::HandleSnapAnimationFinished() +{ + // Emit Signal that scrolling has completed. + mScrolling = false; + Self().SetProperty(mPropertyScrolling, false); + + Vector3 deltaPosition(Self().GetProperty(mPropertyX), + Self().GetProperty(mPropertyY), + 0.0f); + + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + + mDomainOffset += deltaPosition - mScrollPostPosition; + Self().SetProperty(mPropertyDomainOffset, mDomainOffset); + HandleStoppedAnimation(); +} + +bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle ); + + if( Toolkit::ScrollView::SIGNAL_SNAP_STARTED == signalName ) + { + view.SnapStartedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize) +{ + // need to update domain properties for new size + UpdatePropertyDomain(targetSize); +} + +void ScrollView::OnControlSizeSet( const Vector3& size ) +{ + // need to update domain properties for new size + if( mDefaultMaxOvershoot ) + { + mMaxOvershoot.x = size.x * 0.5f; + mMaxOvershoot.y = size.y * 0.5f; + } + UpdatePropertyDomain(size); + UpdateMainInternalConstraint(); + if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) ) + { + mOvershootIndicator->Reset(); + } +} + +void ScrollView::OnChildAdd(Actor& child) +{ + if(mAlterChild) + { + BindActor(child); + } +} + +void ScrollView::OnChildRemove(Actor& child) +{ + // TODO: Actor needs a RemoveConstraint method to take out an individual constraint. + UnbindActor(child); +} + +bool ScrollView::OnTouchEvent(const TouchEvent& event) +{ + if(!mSensitive) + { + // Ignore this touch event, if scrollview is insensitive. + return false; + } + + // Ignore events with multiple-touch points + if (event.GetPointCount() != 1) + { + return false; + } + + if (event.GetPoint(0).state == TouchPoint::Down) + { + mTouchDownTime = event.time; + mTouchDownReceived = true; + mTouchDownPosition = event.GetPoint(0).local; + + if( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation ) + { + mScrollInterrupted = true; + StopAnimation(); + } + + if(mScrolling) // are we interrupting a current scroll? + { + // reset domain offset as scrolling from original plane. + mDomainOffset = Vector3::ZERO; + Self().SetProperty(mPropertyDomainOffset, Vector3::ZERO); + + mScrolling = false; + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + } + } + else if(event.GetPoint(0).state == TouchPoint::Up) + { + // if the user touches and releases without enough movement to go + // into a gesture state, then we should snap to nearest point. + // otherwise our scroll could be stopped (interrupted) half way through an animation. + if(mGestureStackDepth==0 && mTouchDownReceived) + { + unsigned timeDelta( event.time - mTouchDownTime ); + if ( timeDelta >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) + { + // Reset the velocity only if down was received a while ago + mLastVelocity = Vector2( 0.0f, 0.0f ); + } + else + { + Vector2 positionDelta( mTouchDownPosition - event.GetPoint(0).local ); + mLastVelocity = positionDelta / timeDelta; + } + + // Only finish the transform if scrolling was interrupted on down or if we are scrolling + if ( mSnapAnimation || mSnapXAnimation || mSnapYAnimation || mSnapOvershootAnimation || mScrollInterrupted || mScrolling ) + { + FinishTransform(); + } + } + mTouchDownReceived = false; + mScrollInterrupted = false; + } + + return true; // consume since we're potentially scrolling +} + +bool ScrollView::OnMouseWheelEvent(const MouseWheelEvent& event) +{ + if(!mSensitive) + { + // Ignore this mouse wheel event, if scrollview is insensitive. + return false; + } + + Vector3 targetScrollPosition = GetPropertyPosition(); + + if(mRulerX->IsEnabled() && !mRulerY->IsEnabled()) + { + // If only the ruler in the X axis is enabled, scroll in the X axis. + if(mRulerX->GetType() == Ruler::Free) + { + // Free panning mode + targetScrollPosition.x -= event.z * mMouseWheelScrollDistanceStep.x; + ClampPosition(targetScrollPosition); + ScrollTo(-targetScrollPosition); + } + else if(!mScrolling) + { + // Snap mode, only respond to the event when the previous snap animation is finished. + ScrollTo(GetCurrentPage() + event.z); + } + } + else + { + // If the ruler in the Y axis is enabled, scroll in the Y axis. + if(mRulerY->GetType() == Ruler::Free) + { + // Free panning mode + targetScrollPosition.y -= event.z * mMouseWheelScrollDistanceStep.y; + ClampPosition(targetScrollPosition); + ScrollTo(-targetScrollPosition); + } + else if(!mScrolling) + { + // Snap mode, only respond to the event when the previous snap animation is finished. + ScrollTo(GetCurrentPage() + event.z * mRulerX->GetTotalPages()); + } + } + + return true; +} + +void ScrollView::OnSnapAnimationFinished( Animation& source ) +{ + mSnapAnimation.FinishedSignal().Disconnect( this, &ScrollView::OnSnapAnimationFinished ); + mSnapAnimation = NULL; +} + +void ScrollView::OnSnapXAnimationFinished( Animation& source ) +{ + // Guard against destruction during signal emission + // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished() + Toolkit::ScrollView handle( GetOwner() ); + + if(!mSnapYAnimation) + { + HandleSnapAnimationFinished(); + } + if(mScrollMainInternalOvershootXConstraint) + { + Self().RemoveConstraint(mScrollMainInternalOvershootXConstraint); + mScrollMainInternalOvershootXConstraint.Reset(); + mScrollMainInternalOvershootXConstraint = 0; + } + mSnapXAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapXAnimationFinished); + mSnapXAnimation.Reset(); + mSnapXAnimation = NULL; + if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) ) + { + // kick start animation to 0 + Self().SetProperty(mPropertyOvershootX, 0.0f); + } +} + +void ScrollView::OnSnapYAnimationFinished( Animation& source ) +{ + // Guard against destruction during signal emission + // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished() + Toolkit::ScrollView handle( GetOwner() ); + + if(!mSnapXAnimation) + { + HandleSnapAnimationFinished(); + } + if(mScrollMainInternalOvershootYConstraint) + { + Self().RemoveConstraint(mScrollMainInternalOvershootYConstraint); + mScrollMainInternalOvershootYConstraint.Reset(); + mScrollMainInternalOvershootYConstraint = 0; + } + mSnapYAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapYAnimationFinished); + mSnapYAnimation.Reset(); + mSnapYAnimation = NULL; + if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) ) + { + // kick start animation to 0 + Self().SetProperty(mPropertyOvershootY, 0.0f); + } +} + +void ScrollView::GestureStarted() +{ + // we handle the first gesture. + // if we're currently doing a gesture and receive another + // we continue and combine the effects of the gesture instead of reseting. + if(mGestureStackDepth++==0) + { + StopAnimation(); + mPanDelta = Vector3::ZERO; + mScaleDelta = Vector3::ONE; + mRotationDelta = 0.0f; + mLastVelocity = Vector2(0.0f, 0.0f); + mLockAxis = LockPossible; + + if(mScrolling) // are we interrupting a current scroll? + { + // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete. + mScrolling = false; + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + } + } +} + +void ScrollView::GestureContinuing(Vector2 panDelta, Vector2 scaleDelta, float rotationDelta) +{ + mPanDelta.x+= panDelta.x; + mPanDelta.y+= panDelta.y; + mScaleDelta.x*= scaleDelta.x; + mScaleDelta.y*= scaleDelta.y; + mRotationDelta+= rotationDelta; + + // Save the velocity, there is a bug in PanGesture + // Whereby the Gesture::Finished's velocity is either: + // NaN (due to time delta of zero between the last two events) + // or 0 (due to position being the same between the last two events) + + // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan + // appears mostly horizontal or mostly vertical respectively. + if(mAxisAutoLock) + { + if(mPanDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 && + mLockAxis == LockPossible) + { + float dx = fabsf(mPanDelta.x); + float dy = fabsf(mPanDelta.y); + if(dx * mAxisAutoLockGradient >= dy) + { + // 0.36:1 gradient to the horizontal (deviate < 20 degrees) + mLockAxis = LockVertical; + } + else if(dy * mAxisAutoLockGradient > dx) + { + // 0.36:1 gradient to the vertical (deviate < 20 degrees) + mLockAxis = LockHorizontal; + } + else + { + mLockAxis = LockNone; + } + } + } // end if mAxisAutoLock +} + +// TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture) +// TODO: Reimplement Scaling (pinching 2+ points) +// TODO: Reimplment Rotation (pinching 2+ points) +// BUG: Gesture::Finished doesn't always return velocity on release (due to +// timeDelta between last two events being 0 sometimes, or posiiton being the same) +void ScrollView::OnPan(PanGesture gesture) +{ + // Guard against destruction during signal emission + // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx() + Actor self( Self() ); + + if(!mSensitive) + { + // If another callback on the same original signal disables sensitivity, + // this callback will still be called, so we must suppress it. + return; + } + + // translate Gesture input to get useful data... + switch(gesture.state) + { + case Gesture::Started: + { + GestureStarted(); + self.SetProperty( mPropertyPanning, true ); + self.SetProperty( mPropertyScrollStartPagePosition, GetCurrentScrollPosition() ); + + // Update property: X & Y = Position (only when in panning mode - in snapping mode, X & Y are animated). + Constraint constraint = Constraint::New( mPropertyX, + LocalSource( mPropertyPosition ), + Source( self, mPropertyPanning ), + InternalXConstraint ); + mScrollMainInternalXConstraint = self.ApplyConstraint(constraint); + + constraint = Constraint::New( mPropertyY, + LocalSource( mPropertyPosition ), + Source( self, mPropertyPanning ), + InternalYConstraint ); + mScrollMainInternalYConstraint = self.ApplyConstraint(constraint); + // When panning we want to make sure overshoot values are affected by pre position and post position + SetOvershootConstraintsEnabled(true); + break; + } + + case Gesture::Continuing: + { + // Nothing to do, handled in constraint. + break; + } + + case Gesture::Finished: + case Gesture::Cancelled: + { + mLastVelocity = gesture.velocity; + self.SetProperty( mPropertyPanning, false ); + + // Remove X & Y position constraints as they are not required when we are not panning. + self.RemoveConstraint(mScrollMainInternalXConstraint); + self.RemoveConstraint(mScrollMainInternalYConstraint); + break; + } + + case Gesture::Possible: + case Gesture::Clear: + { + // Nothing to do, not needed. + break; + } + + } // end switch(gesture.state) + + OnGestureEx(gesture.state); +} + +void ScrollView::OnPinch(PinchGesture gesture) +{ + // TODO: Reintroduce the pinch functionality for scaling. +} + +void ScrollView::OnGestureEx(Gesture::State state) +{ + // call necessary signals for application developer + + if(state == Gesture::Started) + { + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + Self().SetProperty(mPropertyScrolling, true); + mScrolling = true; + mScrollStartedSignalV2.Emit( currentScrollPosition ); + } + else if( (state == Gesture::Finished) || + (state == Gesture::Cancelled) ) // Finished/default + { + // when all the gestures have finished, we finish the transform. + // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures) + // then stop panning (back to 1 gesture), and then stop zooming (0 gestures). + // this is the point we end, and perform necessary snapping. + mGestureStackDepth--; + if(mGestureStackDepth==0) + { + FinishTransform(); + } + } +} + +void ScrollView::UpdateTransform() +{ +// TODO: notify clamps using property notifications (or see if we need this, can deprecate it) +} + +void ScrollView::FinishTransform() +{ + const Vector3& scrollPosition = Self().GetProperty(mPropertyPosition); + + mScrollPostPosition.x = scrollPosition.x; + mScrollPostPosition.y = scrollPosition.y; + + Vector3 deltaPosition(mScrollPostPosition); + // Cement PRE transform (PRE = POST), and Begin Snap Animation if necessary. + WrapPosition(mScrollPostPosition); + + mDomainOffset += deltaPosition - mScrollPostPosition; + Self().SetProperty(mPropertyDomainOffset, mDomainOffset); + + bool animating = SnapWithVelocity(mLastVelocity * 1000.0f); + + if(!animating) + { + AnimateOvershootToOrigin(0.0f, 0.0f); + // if not animating, then this pan has completed right now. + mScrolling = false; + Self().SetProperty(mPropertyScrolling, false); + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + mScrollCompletedSignalV2.Emit( currentScrollPosition ); + } +} + +Vector3 ScrollView::GetOvershoot(Vector3& position) const +{ + Vector3 size = Self().GetCurrentSize(); + Vector3 overshoot; + + const RulerDomain rulerDomainX = mRulerX->GetDomain(); + const RulerDomain rulerDomainY = mRulerY->GetDomain(); + + if(mRulerX->IsEnabled() && rulerDomainX.enabled) + { + const float left = rulerDomainX.min - position.x; + const float right = size.width - rulerDomainX.max - position.x; + if(left<0) + { + overshoot.x = left; + } + else if(right>0) + { + overshoot.x = right; + } + } + + if(mRulerY->IsEnabled() && rulerDomainY.enabled) + { + const float top = rulerDomainY.min - position.y; + const float bottom = size.height - rulerDomainY.max - position.y; + if(top<0) + { + overshoot.y = top; + } + else if(bottom>0) + { + overshoot.y = bottom; + } + } + + return overshoot; +} + +bool ScrollView::OnAccessibilityPan(PanGesture gesture) +{ + OnPan(gesture); + return true; +} + +void ScrollView::ClampPosition(Vector3& position) const +{ + ClampState3 clamped; + ClampPosition(position, clamped); +} + +void ScrollView::ClampPosition(Vector3& position, ClampState3 &clamped) const +{ + Vector3 size = Self().GetCurrentSize(); + + // determine size of viewport relative to current scaled size. + // e.g. if you're zoomed in 200%, then each pixel on screen is only 0.5 pixels on subject. + if(fabsf(mScrollPostScale.x) > Math::MACHINE_EPSILON_0) + { + size.x /= mScrollPostScale.x; + } + + if(fabsf(mScrollPostScale.y) > Math::MACHINE_EPSILON_0) + { + size.y /= mScrollPostScale.y; + } + + position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x); // NOTE: X & Y rulers think in -ve coordinate system. + position.y = -mRulerY->Clamp(-position.y, size.height, 1.0f, clamped.y); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. + + clamped.z = NotClamped; +} + +void ScrollView::WrapPosition(Vector3& position) const +{ + if(mWrapMode) + { + const RulerDomain rulerDomainX = mRulerX->GetDomain(); + const RulerDomain rulerDomainY = mRulerY->GetDomain(); + + if(mRulerX->IsEnabled()) + { + position.x = -WrapInDomain(-position.x, rulerDomainX.min, rulerDomainX.max); + } + + if(mRulerY->IsEnabled()) + { + position.y = -WrapInDomain(-position.y, rulerDomainY.min, rulerDomainY.max); + } + } +} + +void ScrollView::ClampScale(Vector3& scale) const +{ + ClampState3 clamped; + ClampScale(scale, clamped); +} + +void ScrollView::ClampScale(Vector3& scale, ClampState3 &clamped) const +{ + scale.x = mRulerScaleX->Clamp(scale.x, 0.0f, 1.0f, clamped.x); + scale.y = mRulerScaleY->Clamp(scale.y, 0.0f, 1.0f, clamped.y); + clamped.z = NotClamped; +} + +void ScrollView::UpdateMainInternalConstraint() +{ + // TODO: Only update the constraints which have changed, rather than remove all and add all again. + // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible. + Actor self = Self(); + PanGestureDetector detector( GetPanGestureDetector() ); + + if(mScrollMainInternalPrePositionConstraint) + { + self.RemoveConstraint(mScrollMainInternalPrePositionConstraint); + self.RemoveConstraint(mScrollMainInternalPositionConstraint); + self.RemoveConstraint(mScrollMainInternalDeltaConstraint); + self.RemoveConstraint(mScrollMainInternalFinalConstraint); + self.RemoveConstraint(mScrollMainInternalRelativeConstraint); + } + + // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences + // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz). + + // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place) + Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f); + + Constraint constraint = Constraint::New( mPropertyPrePosition, + Source( detector, PanGestureDetector::LOCAL_POSITION ), + Source( detector, PanGestureDetector::LOCAL_DISPLACEMENT ), + LocalSource( mPropertyX ), + LocalSource( mPropertyY ), + Source( self, mPropertyPanning ), + InternalPrePositionConstraint( initialPanMask, mAxisAutoLock, mAxisAutoLockGradient ) ); + mScrollMainInternalPrePositionConstraint = self.ApplyConstraint(constraint); + + // 2. Second calculate the clamped position (actual position) + constraint = Constraint::New( mPropertyPosition, + LocalSource( mPropertyPrePosition ), + Source( self, Actor::SIZE ), + InternalPositionConstraint( mRulerX->GetDomain(), + mRulerY->GetDomain()) ); + mScrollMainInternalPositionConstraint = self.ApplyConstraint(constraint); + + constraint = Constraint::New( mPropertyPositionDelta, + LocalSource( mPropertyPosition ), + LocalSource( mPropertyDomainOffset ), + InternalPositionDeltaConstraint ); + mScrollMainInternalDeltaConstraint = self.ApplyConstraint(constraint); + + constraint = Constraint::New( mPropertyFinal, + LocalSource( mPropertyPosition ), + LocalSource( mPropertyOvershootX ), + LocalSource( mPropertyOvershootY ), + InternalFinalConstraint( FinalDefaultAlphaFunction, + FinalDefaultAlphaFunction ) ); + mScrollMainInternalFinalConstraint = self.ApplyConstraint(constraint); + + constraint = Constraint::New( mPropertyRelativePosition, + LocalSource( mPropertyPosition ), + LocalSource( mPropertyPositionMin ), + LocalSource( mPropertyPositionMax ), + LocalSource( Actor::SIZE ), + InternalRelativePositionConstraint ); + mScrollMainInternalRelativeConstraint = self.ApplyConstraint(constraint); + + if(mScrollMainInternalOvershootXConstraint) + { + // reset these constraints in correct order + self.RemoveConstraint(mScrollMainInternalOvershootXConstraint); + mScrollMainInternalOvershootXConstraint.Reset(); + + Constraint constraint = Constraint::New( mPropertyOvershootX, + LocalSource( mPropertyPrePosition ), + LocalSource( mPropertyPosition ), + OvershootXConstraint(mMaxOvershoot.x) ); + mScrollMainInternalOvershootXConstraint = self.ApplyConstraint(constraint); + } + + if(mScrollMainInternalOvershootYConstraint) + { + // reset these constraints in correct order + self.RemoveConstraint(mScrollMainInternalOvershootYConstraint); + mScrollMainInternalOvershootYConstraint.Reset(); + + Constraint constraint = Constraint::New( mPropertyOvershootY, + LocalSource( mPropertyPrePosition ), + LocalSource( mPropertyPosition ), + OvershootXConstraint(mMaxOvershoot.y) ); + mScrollMainInternalOvershootYConstraint = self.ApplyConstraint(constraint); + } +} + +void ScrollView::SetOvershootConstraintsEnabled(bool enabled) +{ + Actor self( Self() ); + // remove and reset, it may now be in wrong order with the main internal constraints + if(mScrollMainInternalOvershootXConstraint) + { + self.RemoveConstraint(mScrollMainInternalOvershootXConstraint); + mScrollMainInternalOvershootXConstraint.Reset(); + } + if(mScrollMainInternalOvershootYConstraint) + { + self.RemoveConstraint(mScrollMainInternalOvershootYConstraint); + mScrollMainInternalOvershootYConstraint.Reset(); + } + if(enabled) + { + Constraint constraint = Constraint::New( mPropertyOvershootX, + LocalSource( mPropertyPrePosition ), + LocalSource( mPropertyPosition ), + OvershootXConstraint(mMaxOvershoot.x) ); + mScrollMainInternalOvershootXConstraint = self.ApplyConstraint(constraint); + constraint = Constraint::New( mPropertyOvershootY, + LocalSource( mPropertyPrePosition ), + LocalSource( mPropertyPosition ), + OvershootYConstraint(mMaxOvershoot.y) ); + mScrollMainInternalOvershootYConstraint = self.ApplyConstraint(constraint); + } +} + +void ScrollView::SetInternalConstraints() +{ + // Internal constraints (applied to target ScrollBase Actor itself) ///////// + UpdateMainInternalConstraint(); + + // User definable constraints to apply to all child actors ////////////////// + Actor self = Self(); + + // LocalSource - The Actors to be moved. + // self - The ScrollView + + // Apply some default constraints to ScrollView. + // Movement + Scaling + Wrap function + + Constraint constraint; + + // MoveScaledActor (scrolling/zooming) + constraint = Constraint::New( Actor::POSITION, + Source( self, mPropertyPosition ), + Source( self, mPropertyScale ), + MoveScaledActorConstraint ); + constraint.SetRemoveAction(Constraint::Discard); + ApplyConstraintToBoundActors(constraint); + + // ScaleActor (scrolling/zooming) + constraint = Constraint::New( Actor::SCALE, + Source( self, mPropertyScale ), + ScaleActorConstraint ); + constraint.SetRemoveAction(Constraint::Discard); + ApplyConstraintToBoundActors(constraint); + + // WrapActor (wrap functionality) + constraint = Constraint::New( Actor::POSITION, + LocalSource( Actor::SCALE ), + LocalSource( Actor::ANCHOR_POINT ), + LocalSource( Actor::SIZE ), + Source( self, mPropertyPositionMin ), + Source( self, mPropertyPositionMax ), + Source( self, mPropertyWrap ), + WrapActorConstraint ); + constraint.SetRemoveAction(Constraint::Discard); + ApplyConstraintToBoundActors(constraint); +} + +void ScrollView::SetOvershootToOrigin() +{ + // Clear Snap animation if exists. + if(mSnapOvershootAnimation) + { + mSnapOvershootAnimation.FinishedSignal().Disconnect(this, &ScrollView::OnSnapOvershootAnimationFinished); + mSnapOvershootAnimation.Stop(); + mSnapOvershootAnimation.Clear(); + mSnapOvershootAnimation = NULL; + } + SetOvershootConstraintsEnabled(false); + Self().SetProperty(mPropertyOvershootX, 0.0f); + Self().SetProperty(mPropertyOvershootY, 0.0f); +} + +void ScrollView::AnimateOvershootToOrigin(float xDelay, float yDelay) +{ + if( IsScrollComponentEnabled(Toolkit::Scrollable::OvershootIndicator) ) + { + if(xDelay < Math::MACHINE_EPSILON_1) + { + // kick start animation to 0 + Self().SetProperty(mPropertyOvershootX, 0.0f); + } + if(yDelay < Math::MACHINE_EPSILON_1) + { + // kick start animation to 0 + Self().SetProperty(mPropertyOvershootY, 0.0f); + } + return; + } + // When we need to animate overshoot to 0 + if(mSnapOvershootDuration > Math::MACHINE_EPSILON_1) + { + Actor self = Self(); + // Clear Snap animation if exists. + if(mSnapOvershootAnimation) + { + mSnapOvershootAnimation.FinishedSignal().Disconnect( this, &ScrollView::OnSnapOvershootAnimationFinished ); + mSnapOvershootAnimation.Stop(); + mSnapOvershootAnimation.Clear(); + mSnapOvershootAnimation = NULL; + } + if(!mSnapXAnimation && mScrollMainInternalOvershootXConstraint) + { + // need to remove the x overshoot constraint now or it will override animation to 0 + Self().RemoveConstraint(mScrollMainInternalOvershootXConstraint); + mScrollMainInternalOvershootXConstraint.Reset(); + mScrollMainInternalOvershootXConstraint = 0; + } + if(!mSnapYAnimation && mScrollMainInternalOvershootYConstraint) + { + // need to remove the y overshoot constraint now or it will override animation to 0 + Self().RemoveConstraint(mScrollMainInternalOvershootYConstraint); + mScrollMainInternalOvershootYConstraint.Reset(); + mScrollMainInternalOvershootYConstraint = 0; + } + // setup the new overshoot to 0 animation + float totalDuration = (xDelay > yDelay ? xDelay : yDelay) + mSnapOvershootDuration; + mSnapOvershootAnimation = Animation::New(totalDuration); + mSnapOvershootAnimation.FinishedSignal().Connect( this, &ScrollView::OnSnapOvershootAnimationFinished ); + + mSnapOvershootAnimation.AnimateTo( Property(self, mPropertyOvershootX), 0.0f, mSnapOvershootAlphaFunction, TimePeriod(xDelay, mSnapOvershootDuration) ); + mSnapOvershootAnimation.AnimateTo( Property(self, mPropertyOvershootY), 0.0f, mSnapOvershootAlphaFunction, TimePeriod(yDelay, mSnapOvershootDuration) ); + + mSnapOvershootAnimation.SetDuration(totalDuration); + mSnapOvershootAnimation.Play(); + } + else + { + SetOvershootToOrigin(); + } +} + +void ScrollView::OnSnapOvershootAnimationFinished( Animation& source ) +{ + mSnapOvershootAnimation = NULL; +} + +void ScrollView::StartRefreshTimer() +{ + if(mRefreshIntervalMilliseconds > 0) + { + if (!mRefreshTimer) + { + mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds ); + mRefreshTimer.TickSignal().Connect( this, &ScrollView::OnRefreshTick ); + } + + if (!mRefreshTimer.IsRunning()) + { + mRefreshTimer.Start(); + } + } +} + +void ScrollView::CancelRefreshTimer() +{ + if (mRefreshTimer) + { + mRefreshTimer.Stop(); + } +} + +bool ScrollView::OnRefreshTick() +{ + // Guard against destruction during signal emission + Toolkit::ScrollView handle( GetOwner() ); + + Vector3 currentScrollPosition = GetCurrentScrollPosition(); + mScrollUpdatedSignalV2.Emit( currentScrollPosition ); + + return true; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h new file mode 100644 index 0000000..23bc81f --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h @@ -0,0 +1,931 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include + +// predefined effect includes +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class ScrollView; +typedef IntrusivePtr ScrollViewPtr; + +class ScrollInternalConstraints; +typedef IntrusivePtr ScrollInternalConstraintsPtr; + +class ScrollOvershootIndicator; +typedef IntrusivePtr ScrollOvershootIndicatorPtr; + +/** + * @copydoc Toolkit::ScrollView + */ +class ScrollView : public ScrollBase +{ +public: + + /** + * FindDirection specifies how searching is conducted within the Find... routines. + */ + enum FindDirection + { + None = -3, ///< Includes none within the search query. + All = -2, ///< Includes all within the search query. + Left = -1, ///< Includes only those not right !(>) + Right = 1, ///< Includes only those right (>) + Up = -1, ///< Includes only those not below !(>) + Down = 1, ///< Includes only those below (>) + Out = -1, ///< Includes only those not infront !(>) + In = 1 ///< Includes only those infront (>) + }; + + enum LockAxis + { + LockPossible = 0, ///< Locking is possible, but not set in stone yet. + LockHorizontal, ///< Locking is set to horizontal. (can pan vertically) + LockVertical, ///< Locking is set to vertical. (can pan horizontally) + LockNone ///< Locking is set to none (free panning). + }; + +public: + + /** + * Create a new ScrollView. + * @return A public handle to the newly allocated ScrollView. + */ + static Dali::Toolkit::ScrollView New(); + +public: + + /** + * @copydoc Toolkit::ScrollView::GetScrollSnapAlphaFunction + */ + AlphaFunction GetScrollSnapAlphaFunction() const; + + /** + * @copydoc Toolkit::ScrollView::SetScrollSnapAlphaFunction + */ + void SetScrollSnapAlphaFunction(AlphaFunction alpha); + + /** + * @copydoc Toolkit::ScrollView::GetScrollFlickAlphaFunction + */ + AlphaFunction GetScrollFlickAlphaFunction() const; + + /** + * @copydoc Toolkit::ScrollView::SetScrollFlickAlphaFunction + */ + void SetScrollFlickAlphaFunction(AlphaFunction alpha); + + /** + * @copydoc Toolkit::ScrollView::GetScrollSnapDuration + */ + float GetScrollSnapDuration() const; + + /** + * @copydoc Toolkit::ScrollView::SetScrollSnapDuration + */ + void SetScrollSnapDuration(float time); + + /** + * @copydoc Toolkit::ScrollView::GetScrollFlickDuration + */ + float GetScrollFlickDuration() const; + + /** + * @copydoc Toolkit::ScrollView::SetScrollFlickDuration + */ + void SetScrollFlickDuration(float time); + + /** + * @copydoc Toolkit::ScrollView::ApplyEffect + */ + void ApplyEffect(Toolkit::ScrollViewEffect effect); + + /** + * @brief ApplyEffect Applies a predefined effect + * @param effect + */ + Toolkit::ScrollViewEffect ApplyEffect(Toolkit::ScrollView::PageEffect effect); + + /** + * @copydoc Toolkit::ScrollView::RemoveEffect + */ + void RemoveEffect(Toolkit::ScrollViewEffect effect); + + /** + * @copydoc Toolkit::ScrollView::RemoveAllEffects + */ + void RemoveAllEffects(); + + /** + * @copydoc Toolkit::ScrollView::ApplyConstraintToChildren + */ + void ApplyConstraintToChildren(Constraint constraint); + + /** + * @copydoc Toolkit::ScrollView::RemoveConstraintsFromChildren + */ + void RemoveConstraintsFromChildren(); + + /** + * @copydoc Toolkit::ScrollView::GetRulerX + */ + const RulerPtr GetRulerX() const; + + /** + * @copydoc Toolkit::ScrollView::GetRulerY + */ + const RulerPtr GetRulerY() const; + + /** + * @copydoc Toolkit::ScrollView::SetRulerX + */ + void SetRulerX(RulerPtr ruler); + + /** + * @copydoc Toolkit::ScrollView::SetRulerY + */ + void SetRulerY(RulerPtr ruler); + + /** + * @copydoc Toolkit::ScrollView::SetRulerScaleX + */ + void SetRulerScaleX(RulerPtr ruler); + + /** + * @copydoc Toolkit::ScrollView::SetRulerScaleY + */ + void SetRulerScaleY(RulerPtr ruler); + + /** + * Set Rotation axis ruler (defines how rotating is snapped in radians) + * @param[in] ruler The ruler to be used for the Rotation axis + */ + void SetRulerRotation(RulerPtr ruler); + + /** + * @copydoc Toolkit::ScrollView::SetScrollSensitive + */ + void SetScrollSensitive(bool sensitive); + + /** + * @copydoc Toolkit::ScrollView::SetMaxOvershoot + */ + void SetMaxOvershoot(float overshootX, float overshootY); + + /** + * @copydoc Toolkit::ScrollView::SetSnapOvershootAlphaFunction + */ + void SetSnapOvershootAlphaFunction(AlphaFunction alpha); + + /** + * @copydoc Toolkit::ScrollView::SetSnapOvershootDuration + */ + void SetSnapOvershootDuration(float duration); + + /** + * @copydoc Toolkit::ScrollView::SetTouchesRequiredForPanning + */ + void SetTouchesRequiredForPanning(unsigned int minTouches, unsigned int maxTouches, bool endOutside); + + /** + * @copydoc Toolkit::ScrollView::SetActorAutoSnap + */ + void SetActorAutoSnap(bool enable); + + /** + * Enables or Disables Auto Resizing mode for ScrollView contents. + * + * When enabled, the ScrollView's X/Y Domains are restricted to the + * dimensions of the content's bounds, which may change as Actors are + * Added/Removed, and repositioned. + * + * @note This has been disabled for now, as this requires some fundamental + * changes to the way Actors positions and bounds are retrieved. + * (currently only constraints have these initial state knowledge) + * + * @param[in] enable Enables (true), or disables (false) Auto Resize. + */ + void SetAutoResize(bool enable); + + /** + * Returns whether the wrap mode has been enabled (true) or not (false). + * + * @return Wrap Mode Enabled flag. + */ + bool GetWrapMode() const; + + /** + * @copydoc Toolkit::ScrollView::SetWrapMode + */ + void SetWrapMode(bool enable); + + /** + * @copydoc Toolkit::ScrollView::GetRefreshInterval + */ + int GetRefreshInterval() const; + + /** + * @copydoc Toolkit::ScrollView::SetRefreshInterval + */ + void SetRefreshInterval(int milliseconds); + + /** + * @copydoc Toolkit::ScrollView::GetAxisAutoLock + */ + bool GetAxisAutoLock() const; + + /** + * @copydoc Toolkit::ScrollView::SetAxisAutoLock + */ + void SetAxisAutoLock(bool enable); + + /** + * @copydoc Toolkit::ScrollView::GetAxisAutoLockGradient + */ + float GetAxisAutoLockGradient() const; + + /** + * @copydoc Toolkit::ScrollView::SetAxisAutoLockGradient + */ + void SetAxisAutoLockGradient(float gradient); + + /** + * @copydoc Toolkit::ScrollView::GetFrictionCoefficient + */ + float GetFrictionCoefficient() const; + + /** + * @copydoc Toolkit::ScrollView::SetFrictionCoefficient + */ + void SetFrictionCoefficient(float friction); + + /** + * @copydoc Toolkit::ScrollView::GetFlickSpeedCoefficient + */ + float GetFlickSpeedCoefficient() const; + + /** + * @copydoc Toolkit::ScrollView::SetFlickSpeedCoefficient + */ + void SetFlickSpeedCoefficient(float speed); + + /** + * @copydoc Toolkit::ScrollView::GetMaxFlickSpeed + */ + float GetMaxFlickSpeed() const; + + /** + * @copydoc Toolkit::ScrollView::SetMaxFlickSpeed + */ + void SetMaxFlickSpeed(float speed); + + /** + * @copydoc Toolkit::ScrollView::GetMouseWheelScrollDistanceStep + */ + Vector2 GetMouseWheelScrollDistanceStep() const; + + /** + * @copydoc Toolkit::ScrollView::SetMouseWheelScrollDistanceStep + */ + void SetMouseWheelScrollDistanceStep(Vector2 step); + + /** + * @copydoc Toolkit::ScrollView::GetCurrentPage + */ + unsigned int GetCurrentPage() const; + + /** + * @copydoc Toolkit::ScrollView::GetCurrentScrollPosition + */ + Vector3 GetCurrentScrollPosition() const; + + /** + * @copydoc Toolkit::ScrollView::GetCurrentScrollScale + */ + Vector3 GetCurrentScrollScale() const; + + /** + * @copydoc Toolkit::Scrollable::GetDomainSize + */ + Vector3 GetDomainSize() const; + + /** + * @copydoc Toolkit::ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation) + */ + void TransformTo(const Vector3& position, const Vector3& scale, float rotation, + DirectionBias horizontalBias = DirectionBiasNone, DirectionBias verticalBias = DirectionBiasNone); + + /** + * @copydoc Toolkit::ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation, float duration) + */ + void TransformTo(const Vector3& position, const Vector3& scale, float rotation, float duration, + DirectionBias horizontalBias = DirectionBiasNone, DirectionBias verticalBias = DirectionBiasNone); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(const Vector3 &position) + */ + void ScrollTo(const Vector3 &position); + + /** + * @copydoc Toolkit::Scrollable::ScrollTo(const Vector3& position, float duration) + */ + void ScrollTo(const Vector3& position, float duration); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(const Vector3 &position, float duration, DirectionBias horizontalBias, DirectionBias verticalBias) + */ + void ScrollTo(const Vector3& position, float duration, + DirectionBias horizontalBias, DirectionBias verticalBias); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(unsigned int page) + */ + void ScrollTo(unsigned int page); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias) + */ + void ScrollTo(unsigned int page, float duration, DirectionBias bias = DirectionBiasNone); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(Actor& actor) + */ + void ScrollTo(Actor &actor); + + /** + * @copydoc Toolkit::ScrollView::ScrollTo(Actor& actor, float duration) + */ + void ScrollTo(Actor &actor, float duration); + + /** + * @copydoc Toolkit::ScrollView::SetScrollingDirection() + */ + void SetScrollingDirection( Radian direction, Radian threshold ); + + /** + * @copydoc Toolkit::ScrollView::RemoveScrollingDirection() + */ + void RemoveScrollingDirection( Radian angle ); + + /** + * Finds the closest Actor to the current center of the ScrollView. + * + * @return A handle to the actor if found, or an empty handle if not. + */ + Actor FindClosestActor(); + + /** + * Finds the closest Actor to position in ScrollView + * + * @param[in] position position within ScrollView. + * @param[in] dirX Whether to search only those elements that are Left,Right, or All + * @param[in] dirY Whether to search only those elements that are Up,Down, or All + * @param[in] dirZ Whether to search only those elements that are Out,In, or All + * @return A handle to the actor if found, or an empty handle if not. + */ + Actor FindClosestActorToPosition(const Vector3& position, FindDirection dirX = All, FindDirection dirY = All, FindDirection dirZ = All); + + /** + * @copydoc Toolkit::ScrollView::ScrollToSnapPoint + */ + bool ScrollToSnapPoint(); + + /** + * @copydoc Toolkit::ScrollView::ScaleTo(const Vector3& scale) + */ + void ScaleTo(const Vector3& scale); + + /** + * @copydoc Toolkit::ScrollView::ScaleTo(const Vector3& scale, float duration) + */ + void ScaleTo(const Vector3& scale, float duration); + + /** + * Stops animation + */ + void StopAnimation(void); + + /** + * Animates to position/scale/rotation transform. + * + * @param[in] position The position to animate to + * @param[in] positionDuration The number of seconds this animation should run for in each axis. + * @param[in] scale The scale to animate to + * @param[in] scaleDuration The number of seconds this animation should run for in each axis. + * @param[in] rotation The angle to animate to + * @param[in] rotationDuration The number of seconds this animation should run for in each axis. + * @param[in] alpha The easing alpha function to use. + * @param[in] findShortcuts (optional) Whether to find the shortest route (in Wrap mode) + * @param[in] horizontalBias (optional) Whether to bias animation to left or right (or no biasing) + * @param[in] verticalBias (optional) Whether to bias animation to top or bottom (or no biasing) + * @return True if animation necessary and taking place to reach desired transform. + */ + bool AnimateTo(const Vector3& position, const Vector3& positionDuration, + const Vector3& scale, const Vector3& scaleDuration, + float rotation, float rotationDuration, + AlphaFunction alpha, bool findShortcuts = true, + DirectionBias horizontalBias = DirectionBiasNone, DirectionBias verticalBias = DirectionBiasNone, + SnapType snapType = Snap); + + /** + * @copydoc Toolkit::Scrollable::AddOverlay() + */ + void AddOverlay(Actor actor); + + /** + * @copydoc Toolkit::Scrollable::RemoveOverlay() + */ + void RemoveOverlay(Actor actor); + +public: //Signals + + /** + * @copydoc Dali::Toolkit::ScrollView::SnapStartedSignal() + */ + Toolkit::ScrollView::SnapStartedSignalV2& SnapStartedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +private: // private overriden functions from CustomActorImpl and Controls + + /** + * @copydoc Dali::CustomActorImpl::OnSizeAnimation(Animation&, const Vector3&) + */ + virtual void OnSizeAnimation(Animation& animation, const Vector3& targetSize); + + /** + * @copydoc Dali::ControlImpl::OnControlSizeSet(const Vector3&) + */ + virtual void OnControlSizeSet( const Vector3& size ); + + /** + * From CustomActorImpl; called after a child has been added to the owning actor. + * @param[in] child The child which has been added. + */ + virtual void OnChildAdd(Actor& child); + + /** + * From CustomActorImpl; called shortly before a child is removed from the owning actor. + * @param[in] child The child being removed. + */ + virtual void OnChildRemove(Actor& child); + + /** + * From CustomActorImpl; called after a touch-signal is received by the owning actor. + * + * We don't listen to these events as content within the contain may consume events. + * + * @param[in] event The touch event. + * @return True if the event should be consumed. + */ + virtual bool OnTouchEvent(const TouchEvent& event); + + /** + * From CustomActorImpl; called after a mouse-wheel-event is received by the owning actor. + * @param[in] event The mouse wheel event. + * @return True if the event should be consumed. + */ + virtual bool OnMouseWheelEvent(const MouseWheelEvent& event); + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnControlStageConnection() + */ + virtual void OnControlStageConnection(); + + /** + * @copydoc Toolkit::Control::OnControlStageConnection() + */ + virtual void OnControlStageDisconnection(); + + /** + * @copydoc Toolkit::Control::OnAccessibilityPan() + */ + virtual bool OnAccessibilityPan(PanGesture gesture); + + /** + * @copydoc Toolkit::Scrollable::SetOvershootEnabled() + */ + virtual void SetOvershootEnabled(bool enable); + +private: + + /** + * Called whenever a snap animation has completed + * @param[in] source the Animation instance that has completed. + */ + void OnSnapAnimationFinished( Animation& source ); + + /** + * Called whenever a snap animation on the x-axis has completed + * @param[in] source the Animation instance that has completed. + */ + void OnSnapXAnimationFinished( Animation& source ); + + /** + * Called whenever a snap animation on the y-axis has completed + * @param[in] source the Animation instance that has completed. + */ + void OnSnapYAnimationFinished( Animation& source ); + + /** + * This is called internally whenever the Scroll Rulers are + * modified. This will update the properties: 'scroll-position-min' + * and 'scroll-position-max' to reflect the changes. + * + * @param[in] size size of the visible scroll area (ScrollView control size) + */ + void UpdatePropertyDomain(const Vector3& size); + + /** + * Called when the gesture starts. + */ + void GestureStarted(); + + /** + * Amalgamated Gesture Continuing event + * + * @param[in] panDelta average panning delta from base position (0) + * @param[in] scaleDelta average scale delta from base scale (1) + * @param[in] rotationDelta average rotation delta from base angle (0) + */ + void GestureContinuing(Vector2 panDelta, Vector2 scaleDelta, float rotationDelta); + + /** + * Called upon pan gesture event. + * + * @param[in] gesture The gesture event. + */ + void OnPan(PanGesture pan); + + /** + * Called up pinch gesture event. + * + * @param[in] gesture The gesture event. + */ + void OnPinch(PinchGesture gesture); + + /** + * Extension of the above gestures. + * + * @param[in] gesture The gesture event. + */ + void OnGestureEx(Gesture::State state); + + /** + * Performs snapping while taking into account Velocity of gesture + * (velocity in pixels/sec) + * + * @param[in] velocity velocity in pixels/sec + */ + bool SnapWithVelocity(Vector2 velocity); + + /** + * Updates Container Transform based on Pan, Scale, and Rotation props. + * (occurs when continuing gesture i.e. dragging/pinching.) + */ + void UpdateTransform(); + + /** + * Finishes Container Transform + * (occurs upon finishing gesture i.e. releasing) + */ + void FinishTransform(); + + /** + * Sets Overshoot to origin / cancels animation + */ + void SetOvershootToOrigin(); + + /** + * Animates Overshoot to origin + */ + void AnimateOvershootToOrigin(float xDelay, float yDelay); + + /** + * Called whenever a snap overshoot animation has completed. + * @param[in] source the Animation instance that has completed. + */ + void OnSnapOvershootAnimationFinished( Animation& source ); + + /** + * Returns overshoot vector based on current position + * + * Overshoot vector is defined as how far outside of bounds + * the viewport is trying to view (prior to being clamped). + * + * an overshoot of (100,50), means user is in bottom right corner, + * trying to pan +100 to the right, and +50 below. This can be used + * to determine an effect, such as stretching. + * + * @param[in] position The position for which you wish to obtain overshoot vector + */ + Vector3 GetOvershoot(Vector3& position) const; + + /** + * Clamps position within the domain set up by X/Y Rulers + * + * @param[in,out] position The position you wish to clamp + */ + void ClampPosition(Vector3& position) const; + + /** + * Clamps position within the domain set up by X/Y Rulers + * + * @param[in,out] position The position you wish to clamp + * @param[out] clamped The results of the clamping. + */ + void ClampPosition(Vector3& position, ClampState3 &clamped) const; + + /** + * Wraps position within the domain set up by X/Y Rulers + * + * @note Only wraps if mWrapMode is enabled, and respective domains + * are enabled. + * + * @param[in,out] position The position you wish to wrap + */ + void WrapPosition(Vector3& position) const; + + /** + * Clamps scale within the domain set up by Scale-X/Scale-Y Rulers + * + * @param[in,out] scale The scale you wish to clamp + */ + void ClampScale(Vector3& scale) const; + + /** + * Clamps scale within the domain set up by Scale-X/Scale-Y Rulers + * + * @param[in,out] scale The scale you wish to clamp + * @param[out] clamped The results of the clamping. + */ + void ClampScale(Vector3& scale, ClampState3 &clamped) const; + + /** + * Updates the main internal scroll constraints with new ruler and domain + * values + */ + void UpdateMainInternalConstraint(); + + /** + * Enables/disables the overshoot constraints + * + * @param[in] enabled whether to enable or disable the overshoot constraints + */ + void SetOvershootConstraintsEnabled(bool enabled); + + /** + * Sets internal constraints for this ScrollView. + * Many of these internal constraints are based on properties within + * ScrollView. + */ + void SetInternalConstraints(); + +protected: + + /** + * Construct a new ScrollView. + */ + ScrollView(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollView(); + +private: + + /** + * Searches this ScrollView, and attempts to Unbind + * systematically this Actor from the ScrollView attached. + * + * @param[in] child The actor to be unbound. + */ + virtual void FindAndUnbindActor(Actor child); + + /** + * Gets position property. + * + * @return The current position + */ + Vector3 GetPropertyPrePosition() const; + + /** + * Gets position property. + * + * @return The current position + */ + Vector3 GetPropertyPosition() const; + + /** + * Gets scale property. + * + * @return The current scale + */ + Vector3 GetPropertyScale() const; + + /** + * Handles a Stopped animation. Its position/scale/rotation properties need to be + * saved, and the animation flag switched off. + */ + void HandleStoppedAnimation(); + + /** + * Handles a Stopped animation (whether the animation completed, or was + * manually stopped). Its position/scale/rotation properties need to be + * saved, and the animation flag switched off. + */ + void HandleSnapAnimationFinished(); + + /** + * Helper to start the refresh timer. + */ + void StartRefreshTimer(); + + /** + * Helper to cancel the refresh timer. + */ + void CancelRefreshTimer(); + + /** + * Refresh the ScrollView (used when animating to update application developer of changes) + * @return True if the refresh timer should be kept running. + */ + bool OnRefreshTick(); + +private: + + // Undefined + ScrollView(const ScrollView&); + + // Undefined + ScrollView& operator=(const ScrollView& rhs); + +private: + + bool mInitialized; + bool mScrolling; ///< Flag indicating whether the scroll view is being scrolled (by user or animation) + bool mScrollInterrupted; ///< Flag set for when a down event interrupts a scroll + unsigned long mTouchDownTime; ///< The touch down time + Vector2 mTouchDownPosition; ///< The touch down position + + bool mSensitive; ///< Scroll Sensitivity Flag. + + int mGestureStackDepth; ///< How many gestures are currently occuring. + Vector2 mGestureReferencePosition; ///< Point where scaling should occur from. + Vector2 mPinchGestureLastPosition; + Vector2 mPinchGestureLastScale; + + Vector3 mPanDelta; ///< Amount currently panned. + Vector3 mScaleDelta; ///< Amount currently scaled. + float mRotationDelta; ///< Amount currently rotated. + + // Scroll delegate pre and post position/scale/rotation properties... + Vector3 mScrollPrePosition; ///< Scroll delegate pre-position + Vector3 mScrollPostPosition; ///< Scroll delegate post-position (affected by current touch) + Vector3 mScrollPreScale; ///< Scroll delegate pre-scale + Vector3 mScrollPostScale; ///< Scroll delegate post-scale (affected by current touch) + float mScrollPreRotation; ///< Scroll delegate pre-rotation + float mScrollPostRotation; ///< Scroll delegate post-rotation (affected by current touch) + Vector3 mDomainOffset; ///< Domain offset (this keeps track of the domain boundaries that scroll positions traverses) + + // Rulers for each axes... + RulerPtr mRulerX; + RulerPtr mRulerY; + RulerPtr mRulerScaleX; + RulerPtr mRulerScaleY; + RulerPtr mRulerRotation; + bool mTouchDownReceived; + bool mActorAutoSnapEnabled; ///< Whether to automatically snap to closest actor. + bool mAutoResizeContainerEnabled; ///< Whether to automatically resize container (affects RulerDomain's on X/Y axes) + bool mWrapMode; ///< Whether to wrap contents based on container size. + bool mAxisAutoLock; ///< Whether to automatically lock axis when panning. + unsigned int mMinTouchesForPanning; ///< Minimum number of touches for panning to be used. + unsigned int mMaxTouchesForPanning; ///< Maximum number of touches for panning to be used. + + Animation mSnapAnimation; + Animation mSnapXAnimation; ///< Animates from current x-axis position to the snapped (or scrolled) x-axis position. + Animation mSnapYAnimation; ///< Animates from current y-axis position to the snapped (or scrolled) y-axis position. + Animation mSnapOvershootAnimation; ///< Animates scroll-overshoot from current position to 0,0 based on specified easing equation. + + + Vector2 mLastVelocity; ///< Record the last velocity from PanGesture (Finish event doesn't have correct velocity) + LockAxis mLockAxis; + + Timer mOvershootRefreshTimer; + Timer mRefreshTimer; ///< Refresh timer is used to provide the Application developer with updates as animations run. + int mRefreshIntervalMilliseconds; ///< Refresh timer interval. + + bool mAlterChild; ///< Internal flag to control behavior of OnChildAdd/OnChildRemove when Adding internal Actors. + Actor mInternalActor; ///< Internal actor (we keep internal actors in here e.g. scrollbars, so we can ignore it in searches) + + ScrollViewEffectContainer mEffects; ///< Container keeping track of all the applied effects. + + float mOvershootDelay; ///< Time to wait for input before reducing overshoot back to 0 + Vector2 mMaxOvershoot; ///< Number of scrollable pixels that will take overshoot from 0.0f to 1.0f + bool mDefaultMaxOvershoot; ///< Whether to use default max overshoot or application defined one + float mSnapOvershootDuration; ///< Duration for overshoot snapping back to Vector3::ZERO + AlphaFunction mSnapOvershootAlphaFunction; ///< AlphaFunction to be used for this overshoot. + + float mSnapDuration; ///< Time for the snap animation to take (in seconds). + AlphaFunction mSnapAlphaFunction; ///< AlphaFunction to be used for the Snap Animation. + + float mFlickDuration; ///< Time for the flick animation to take (in seconds). + AlphaFunction mFlickAlphaFunction; ///< AlphaFunction to be used for the Flick Animation. + + float mAxisAutoLockGradient; ///< Axis Auto-lock gradient threshold. Above this gradient and it will lock scrolling to closest axis. + float mFrictionCoefficient; ///< Friction coefficient. Amount of friction to apply to free panning flick animation. in stage.lengths/sec + float mFlickSpeedCoefficient; ///< Flick velocity coefficient. Input touch velocity is multiplied by this. + float mMaxFlickSpeed; ///< Maximum flick speed. Maximum speed of flick in stage.lengths/sec. + + Vector2 mMouseWheelScrollDistanceStep; ///< The step of scroll distance in actor coordinates in X and Y axes for each mouse wheel event received. + + //ScrollInternalConstraintsPtr mScrollInternalConstraints; + ActiveConstraint mScrollMainInternalPrePositionConstraint; + ActiveConstraint mScrollMainInternalPositionConstraint; + ActiveConstraint mScrollMainInternalXConstraint; + ActiveConstraint mScrollMainInternalYConstraint; + ActiveConstraint mScrollMainInternalOvershootXConstraint; + ActiveConstraint mScrollMainInternalOvershootYConstraint; + ActiveConstraint mScrollMainInternalDeltaConstraint; + ActiveConstraint mScrollMainInternalFinalConstraint; + ActiveConstraint mScrollMainInternalRelativeConstraint; + + ScrollOvershootIndicatorPtr mOvershootIndicator; + + Toolkit::ScrollView::SnapStartedSignalV2 mSnapStartedSignalV2; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ScrollView& GetImpl(Toolkit::ScrollView& scrollView) +{ + DALI_ASSERT_ALWAYS(scrollView); + + Dali::RefObject& handle = scrollView.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::ScrollView& GetImpl(const Toolkit::ScrollView& scrollView) +{ + DALI_ASSERT_ALWAYS(scrollView); + + const Dali::RefObject& handle = scrollView.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.cpp new file mode 100644 index 0000000..a67cf71 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.cpp @@ -0,0 +1,246 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +const float PAGE_SIZE_MULTIPLIER( 1.15f ); + +using namespace ScrollViewHelperFunctions; + +/** + * ScrollPageCarouselEffectInfo + * + * Color constraint: adjusts the alpha of the page based on their parent page's position relative + * to the middle of the screen. + * When at middle of screen Alpha is 100% opacity. + * When outside the viewable area, the opacity is 0%. + * + * Position constraint: adjusts the position of the page based on their parent page's position + * relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated as per expected in a 3D carousel. + */ +class ScrollPageCarouselEffectInfo : public Dali::RefObject +{ +public: + + ScrollPageCarouselEffectInfo( const Vector2& positionToPageSizeRatio ) + : mPositionToPageSizeRatio( positionToPageSizeRatio ) + { + } + + /** + * @param[in] current The current color of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new color of this Actor. + */ + Vector4 ColorConstraint(const Vector4& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + + if( scrollWrap.GetBoolean() ) + { + WrapPositionWithinDomain( position, pageSize, scrollPositionMin.GetVector3(), scrollPositionMax.GetVector3() ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // note preserve color channels incase there is a shader/further constraint + // that wishes to do something with that information. + return Vector4(current.r, current.g, current.b, 0.0f); + } + + Vector4 color( current ); + Vector2 distance( position / pageSize * PAGE_SIZE_MULTIPLIER ); + color.a = Clamp( 1.0f - distance.Length(), 0.0f, 1.0f ); + + return color; + } + + /** + * @param[in] current The current position + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current + scrollPosition; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + + if( scrollWrap.GetBoolean() ) + { + WrapPositionWithinDomain( position, pageSize, scrollPositionMin.GetVector3(), scrollPositionMax.GetVector3() ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this) + // they will be invisible so doesn't have to be precise, just away from stage. + return current + scrollPosition; + } + + Vector3 angle( position / pageSize * PAGE_SIZE_MULTIPLIER ); + + position.x = pageSize.x * sinf( angle.x ); + position.y = pageSize.y * sinf( angle.y ); + + Vector2 zMovement( pageSize ); + zMovement *= mPositionToPageSizeRatio; + position.z = - ( ( zMovement.x - ( zMovement.x * cos( angle.x ) ) ) + ( zMovement.y - ( zMovement.y * cos( angle.y ) ) ) ); + + return position; + } + + const Vector2 mPositionToPageSizeRatio; ///< The page will move its position according to this ratio. +}; + +typedef IntrusivePtr ScrollPageCarouselEffectInfoPtr; + +/** + * Helper: Applies the 3D scroll cube constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] info The effect info for the constraints + */ +void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView, + Actor child, + ScrollPageCarouselEffectInfoPtr info) +{ + // Apply constraints to this actor // + Constraint constraint; + constraint = Constraint::New( Actor::COLOR, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollPageCarouselEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollPageCarouselEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +ScrollViewPageCarouselEffect::ScrollViewPageCarouselEffect() +{ + +} + +ScrollViewPageCarouselEffect::~ScrollViewPageCarouselEffect() +{ +} + +void ScrollViewPageCarouselEffect::ApplyToPage( Actor page, const Vector2& positionToPageSizeRatio ) +{ + ScrollPageCarouselEffectInfoPtr info(new ScrollPageCarouselEffectInfo( positionToPageSizeRatio ) ); + + ApplyScrollCubeConstraints( GetScrollView(), page, info ); +} + +void ScrollViewPageCarouselEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewPageCarouselEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.h new file mode 100644 index 0000000..ffb92e2 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollGroup; +class ScrollView; + +namespace Internal +{ + +/** + * @copydoc Toolkit::ScrollViewPageCarouselEffect + */ +class ScrollViewPageCarouselEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewPageCarouselEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToPage( Actor child, const Vector2& positionToPageSizeRatio ); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach( Toolkit::ScrollView& scrollView ); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach( Toolkit::ScrollView& scrollView ); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewPageCarouselEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewPageCarouselEffect& GetImpl(Dali::Toolkit::ScrollViewPageCarouselEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewPageCarouselEffect& GetImpl(const Dali::Toolkit::ScrollViewPageCarouselEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.cpp new file mode 100644 index 0000000..0c762ed --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.cpp @@ -0,0 +1,327 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +using namespace ScrollViewHelperFunctions; + +/** + * ScrollPageCubeEffectInfo + * + * Rotate constraint: adjusts the angle of the page based on its position relative to the middle of + * the screen. + * When at middle of screen Angles on X and Y Axes is 0. + * When one screen away from the middle Angle is 90 degrees (pi/2) + * + * Color constraint: adjusts the alpha of the page based on their parent page's position relative + * to the middle of the screen. + * When at middle of screen Alpha is 100% opacity. + * When outside the viewable area, the opacity is 0%. + * + * Position constraint: adjusts the position of the page based on their parent page's position + * relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated as per expected in a 3D inner cube. + */ +class ScrollPageCubeEffectInfo : public Dali::RefObject +{ +public: + + ScrollPageCubeEffectInfo( const Vector2& angleSwing ) + : mAngleSwing(angleSwing) + { + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new orientation of this Actor. + */ + Quaternion RotationConstraint(const Quaternion& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + + if( scrollWrap.GetBoolean() ) + { + WrapPositionWithinDomain( position, pageSize, scrollPositionMin.GetVector3(), scrollPositionMax.GetVector3() ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + return current; + } + + // Our target is a 90 degree (PI/2) rotation per page, so calculate the angle we should be rotate + // our page by calculating the amount we've moved as a fraction of the total size of the page. + Vector2 angle( position / pageSize * Dali::Math::PI_2 ); + + Quaternion rotation = Quaternion( -angle.x * mAngleSwing.x, Vector3::YAXIS ) * + Quaternion( angle.y * mAngleSwing.y, Vector3::XAXIS ) * + current; + + return rotation; + } + + /** + * @param[in] current The current color of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new color of this Actor. + */ + Vector4 ColorConstraint(const Vector4& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + + if( scrollWrap.GetBoolean() ) + { + WrapPositionWithinDomain( position, pageSize, scrollPositionMin.GetVector3(), scrollPositionMax.GetVector3() ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // note preserve color channels incase there is a shader/further constraint + // that wishes to do something with that information. + return Vector4(current.r, current.g, current.b, 0.0f); + } + + // Calculate the distance of this page from our view and ensure it falls within the appropriate + // visual bounds. + // If it does not, then the opacity is set to 0.0f. + position.x /= pageSize.width; + position.y /= pageSize.height; + float distanceFactor = sqrt( position.x * position.x + position.y * position.y ); + + if ( distanceFactor > 1.0f ) + { + return Vector4(current.r, current.g, current.b, 0.0f); + } + + return current; + } + + /** + * @param[in] current The current position + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollWrap Whether scroll wrap has been enabled or not (SCROLL_WRAP_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollWrap) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current + scrollPosition; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + + if( scrollWrap.GetBoolean() ) + { + WrapPositionWithinDomain( position, pageSize, scrollPositionMin.GetVector3(), scrollPositionMax.GetVector3() ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this) + // they will be invisible so doesn't have to be precise, just away from stage. + return current + scrollPosition; + } + + // Our target when scrolling is moving from the origin to the following points around a curve: + // Right To Left: (-pageWidth, 0, pageWidth) + // Left To Right: ( pageWidth, 0, pageWidth) + // Down To Up: ( 0, -pageHeight, pageWidth) + // Up To Down: ( 0, pageHeight, pageWidth) + + Vector2 angle( position / pageSize * Dali::Math::PI_2 ); + Vector2 radius( pageSize * 0.5 ); + + position.x = radius.x * sin( angle.x ); + position.y = radius.y * sin( angle.y ); + position.z = ( radius.x - ( radius.x * cos( angle.x ) ) ) + ( radius.y - ( radius.y * cos( angle.y ) ) ); + + return position; + } + + Vector2 mAngleSwing; ///< Maximum amount in X and Y axes to rotate. +}; + +typedef IntrusivePtr ScrollPageCubeEffectInfoPtr; + +/** + * Helper: Applies the 3D scroll cube constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] info The effect info for the constraints + */ +void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView, + Actor child, + ScrollPageCubeEffectInfoPtr info) +{ + // Apply constraints to this actor // + Constraint constraint; + constraint = Constraint::New( Actor::ROTATION, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollPageCubeEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::COLOR, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollPageCubeEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + boost::bind( &ScrollPageCubeEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +ScrollViewPageCubeEffect::ScrollViewPageCubeEffect() +{ + +} + +ScrollViewPageCubeEffect::~ScrollViewPageCubeEffect() +{ +} + +void ScrollViewPageCubeEffect::ApplyToPage( Actor page, const Vector2& angleSwing ) +{ + ScrollPageCubeEffectInfoPtr info(new ScrollPageCubeEffectInfo( angleSwing )); + + ApplyScrollCubeConstraints( GetScrollView(), page, info ); +} + +void ScrollViewPageCubeEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewPageCubeEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.h new file mode 100644 index 0000000..f09eed7 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.h @@ -0,0 +1,114 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollGroup; +class ScrollView; + +namespace Internal +{ + +/** + * @copydoc Toolkit::ScrollViewPageCubeEffect + */ +class ScrollViewPageCubeEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewPageCubeEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToPage( Actor child, + const Vector2& angleSwing ); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach( Toolkit::ScrollView& scrollView ); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach( Toolkit::ScrollView& scrollView ); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewPageCubeEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewPageCubeEffect& GetImpl(Dali::Toolkit::ScrollViewPageCubeEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewPageCubeEffect& GetImpl(const Dali::Toolkit::ScrollViewPageCubeEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.cpp new file mode 100644 index 0000000..cbb031c --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.cpp @@ -0,0 +1,424 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +const float PAGE_EPSILON_FACTOR( 0.25f ); + +const float PAGE_SIZE_RELATIVE_ANGLE_FACTOR( 0.75f ); +const float NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR( 1.5f ); + +const float SCROLL_PAGE_OPAQUE_BEFORE( 0.4f ); +const float SCROLL_PAGE_FULLY_TRANSPARENT_AFTER( 0.9f ); +const float NON_SCROLL_PAGE_OPAQUE_BEFORE( 0.8f ); +const float NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER( 1.0f ); + +const float RADIUS_FACTOR( 0.95f ); +const float SCROLL_PAGE_Z_POSITION_FACTOR( -2.0f ); +const float NON_SCROLL_PAGE_Z_POSITION_FACTOR( -0.75f ); + +using namespace ScrollViewHelperFunctions; + +/** + * ScrollPageSpiralEffectInfo + * + * Rotate constraint: adjusts the angle of the page based on its position relative to the middle of + * the screen. + * When at middle of screen Angles on X and Y Axes is 0. + * + * Color constraint: adjusts the alpha of the page based on their parent page's position relative + * to the middle of the screen. + * When at middle of screen Alpha is 100% opacity. + * When outside the viewable area, the opacity is 0%. + * + * Position constraint: adjusts the position of the page based on their parent page's position + * relative to the middle of the screen. + * When at middle of the screen the position is not altered. + */ +class ScrollPageSpiralEffectInfo : public Dali::RefObject +{ +public: + + ScrollPageSpiralEffectInfo( const Vector2& spiralAngle, bool scrollWrap ) + : mSpiralAngle( spiralAngle ), + mScrollWrap( scrollWrap ) + { + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME) + * @return The new orientation of this Actor. + */ + Quaternion RotationConstraint(const Quaternion& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollStartPagePositionProperty) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + const Vector3& minScrollPosition( scrollPositionMin.GetVector3() ); + const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() ); + + if( mScrollWrap ) + { + WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + return current; + } + + Vector2 angle( position / ( pageSize * PAGE_SIZE_RELATIVE_ANGLE_FACTOR ) * Vector3( mSpiralAngle ) ); + const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR ); + Vector2 distanceFromScrollPage; + distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x ); + distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y ); + + Vector2 angleMaxMin( mSpiralAngle ); + + // X rotation + if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page? + { + angle.x = -angle.x * 0.9f; + } + else + { + // If not then multiply by angle factor. + angleMaxMin.x *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR; + } + ClampInPlace( angle.x, -angleMaxMin.x, angleMaxMin.x ); + + // Y rotation + if ( fabsf( distanceFromScrollPage.y ) > epsilon.y ) // If not on the scroll page then multiply by angle factor. + { + angleMaxMin.y *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR; + angle.y = -angle.y; + } + ClampInPlace( angle.y, -angleMaxMin.y, angleMaxMin.y ); + + Quaternion rotation = Quaternion( angle.x, Vector3::YAXIS ) * + Quaternion( angle.y, Vector3::XAXIS ) * + current; + + return rotation; + } + + /** + * @param[in] current The current color of this Actor + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME) + * @return The new color of this Actor. + */ + Vector4 ColorConstraint(const Vector4& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollStartPagePositionProperty) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + const Vector3& minScrollPosition( scrollPositionMin.GetVector3() ); + const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() ); + + if( mScrollWrap ) + { + WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // note preserve color channels incase there is a shader/further constraint + // that wishes to do something with that information. + return Vector4(current.r, current.g, current.b, 0.0f); + } + + Vector4 color( current ); + Vector2 distance( position / pageSize ); + float distanceLength( distance.Length() ); + const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR ); + Vector2 distanceFromScrollPage; + distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x ); + distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y ); + + float fullyOpaqueBefore( 0.0f ); + float fullyTransparentAfter( 1.0f ); + + if ( ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) && ( fabsf( distanceFromScrollPage.y ) <= epsilon.y )) // Did scroll start on this page? + { + fullyOpaqueBefore = SCROLL_PAGE_OPAQUE_BEFORE; + fullyTransparentAfter = SCROLL_PAGE_FULLY_TRANSPARENT_AFTER; + } + else + { + fullyOpaqueBefore = NON_SCROLL_PAGE_OPAQUE_BEFORE; + fullyTransparentAfter = NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER; + } + + if ( distanceLength <= fullyOpaqueBefore ) + { + color.a = 1.0f; + } + else if ( distanceLength <= fullyTransparentAfter ) + { + float opacity( distanceLength - fullyOpaqueBefore ); + opacity /= fullyTransparentAfter - fullyOpaqueBefore; + color.a = Clamp( 1.0f - opacity, 0.0f, 1.0f ); + } + else + { + color.a = 0.0f; + } + + return color; + } + + /** + * @param[in] current The current position + * @param[in] pagePositionProperty The page's position. + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 PositionConstraint(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& pageSizeProperty, + const PropertyInput& scrollStartPagePositionProperty) + { + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3(); + + // Get position of page. + Vector3 position = pagePosition + scrollPosition; + + // short circuit: if we're looking straight on at the page. + if( IsStraightOnView( position ) ) + { + return current + scrollPosition; + } + + const Vector3& pageSize = pageSizeProperty.GetVector3(); + const Vector3& minScrollPosition( scrollPositionMin.GetVector3() ); + const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() ); + + if( mScrollWrap ) + { + WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition ); + } + + // short circuit: for pages outside of view. + if( IsOutsideView( position, pageSize ) ) + { + // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this) + // they will be invisible so doesn't have to be precise, just away from stage. + return current + scrollPosition; + } + + const Vector2 angle( position / pageSize * ( Dali::Math::PI_4 ) ); + const Vector2 radius( pageSize * RADIUS_FACTOR ); + const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR ); + Vector2 distanceFromScrollPage; + distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x ); + distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y ); + + // X position and relative Z position + if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page? + { + position.x = radius.x * sin( angle.x ) * 0.77f; + position.z = fabsf( position.x ) * SCROLL_PAGE_Z_POSITION_FACTOR; + } + else + { + position.x = radius.x * ( sinf( angle.x * Math::PI * 0.4f ) ); + + position.z = fabsf( position.x ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR; + } + + // Y position and relative Z position + if ( fabsf( distanceFromScrollPage.y ) <= epsilon.y ) // Did scroll start on this page? + { + position.y = radius.y * sin( angle.y ) * 0.77f; + position.z += fabsf( position.y ) * SCROLL_PAGE_Z_POSITION_FACTOR; + } + else + { + position.y = radius.y * ( sinf( angle.y * Math::PI * 0.4f ) ); + + position.z += fabsf( position.y ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR; + } + + return position; + } + + Vector2 mSpiralAngle; ///< The angle of the spirald page + bool mScrollWrap; ///< Whether the scroll view wraps or not. +}; + +typedef IntrusivePtr ScrollPageSpiralEffectInfoPtr; + +/** + * Helper: Applies the 3D scroll cube constraints to the child actor + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the 3D Effect. + * @param[in] info The effect info for the constraints + */ +void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView, + Actor child, + ScrollPageSpiralEffectInfoPtr info) +{ + // Apply constraints to this actor // + Constraint constraint; + constraint = Constraint::New( Actor::ROTATION, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ), + boost::bind( &ScrollPageSpiralEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::COLOR, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ), + boost::bind( &ScrollPageSpiralEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::POSITION, + LocalSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ), + boost::bind( &ScrollPageSpiralEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) ); + + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect() +{ + +} + +ScrollViewPageSpiralEffect::~ScrollViewPageSpiralEffect() +{ +} + +void ScrollViewPageSpiralEffect::ApplyToPage( Actor page, const Vector2& spiralAngle ) +{ + Toolkit::ScrollView scrollView( GetScrollView() ); + + if ( scrollView ) + { + ScrollPageSpiralEffectInfoPtr info(new ScrollPageSpiralEffectInfo( spiralAngle, GetImpl( scrollView ).GetWrapMode() )); + ApplyScrollCubeConstraints( scrollView, page, info ); + } +} + +void ScrollViewPageSpiralEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ +} + +void ScrollViewPageSpiralEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h new file mode 100644 index 0000000..a0a8fcb --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollGroup; +class ScrollView; + +namespace Internal +{ + +/** + * @copydoc Toolkit::ScrollViewPageSpiralEffect + */ +class ScrollViewPageSpiralEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewPageSpiralEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToPage( Actor child, const Vector2& spiralAngle ); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach( Toolkit::ScrollView& scrollView ); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach( Toolkit::ScrollView& scrollView ); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewPageSpiralEffect(); + +private: + + Vector3 mPageSize; ///< The logical page size for the 3D effect. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewPageSpiralEffect& GetImpl(Dali::Toolkit::ScrollViewPageSpiralEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewPageSpiralEffect& GetImpl(const Dali::Toolkit::ScrollViewPageSpiralEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_PAGE_SPIRAL_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.cpp new file mode 100644 index 0000000..92df9f2 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.cpp @@ -0,0 +1,651 @@ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +typedef Dali::Toolkit::Internal::ScrollSlideInfo ScrollSlideInfo; +typedef Dali::Toolkit::Internal::ScrollSlideInfoPtr ScrollSlideInfoPtr; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * ScrollSlideInfo structure contains + * common info that is shared amongst the constraints applied to Actors. + * The constraints + effect all share ownership of this info struct. + * The info is written to by the ScrollSlideInfoUpdate constraint. While + * the other constraints read from by the other constraints. Due to the order + * in which the constraints are applied, all constraints will get the current + * property values for these properties. + * The advantage of doing this is that: + * A) Constraints are not restricted by the 6 property limit. to function. + * B) Properties which rarely change or only change when another property changes + * (e.g. time), such as scroll position, scroll domain, size, wrap mode don't need + * to be checked for each constraint to apply. + */ +class ScrollSlideInfo : public RefObject +{ + +public: + + /** + * Constructor + */ + ScrollSlideInfo() + { + } + + /** + * Destructor + */ + virtual ~ScrollSlideInfo() + { + } + +public: + + Vector3 mScrollPosition; + Vector3 mEffectReference; + Vector3 mScrollSize; + Vector3 mScrollPositionMin; + Vector3 mScrollPositionMax; + bool mScrollWrap; + bool mVertical; + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + + +namespace // unnamed namespace +{ + +const float SLIDEEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again) +const float COMPLETION_START_DURATION = 0.25f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect) +const float COMPLETION_END_DURATION = 5.0f; ///< Maximum time for completion of effect after scroll-view initially completes (due to delay effect) +const float ANIMATION_BLEND_COEFFICIENT = 0.05f; ///< Animation blending coefficient (blends between target value e.g. 5% and current value 9%) +const float INV_ANIMATION_BLEND_COEFFICIENT = (1.0f - ANIMATION_BLEND_COEFFICIENT); +const float DEFAULT_MAX_DELAY_DURATION = 0.25f; ///< Default maximum delay duration of the effect after scroll completes is 0.25f +const float EFFECT_SNAP_GROW_DURATION = 0.33f; ///< Take 1/3rd of a second for the snap effect property to grow +const float EFFECT_SNAP_DECAY_DURATION = 0.667f; ///< Take 2/3rds of a second for the snap effect property to decay + +/** + * Gets a property index. If the property doesn't already exist, then + * it will create the property. + * @param[in] handle The handle that owns or will own the property + * @param[in] name The name for this property + * @param[in] propertyValue The initial value for this property + * @return The property index for this property is returned. + */ +Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue ) +{ + Property::Index index = handle.GetPropertyIndex( name ); + + if(index == Property::INVALID_INDEX) + { + index = handle.RegisterProperty( name, propertyValue ); + } + + return index; +} + +/** + * Re-scales input value x from x0 - x1, to linearly map + * over the values y0 - y1. Values outside of this range + * will also conform to the trend (gradient) set. + * @param[in] y0 output minimum bound + * @param[in] y1 output maximum bound + * @param[in] x input X value + * @param[in] x0 (optional) input minimum bound (default 0.0) + * @param[in] x1 (optional) input maximum bound (default 1.0) + * @return The result of the mapping is returned. + */ +float Mix(float y0, float y1, float x, float x0 = 0.0f, float x1 = 1.0f) +{ + return y0 + (y1 - y0) * (x - x0) / (x1-x0); +} + +/** + * Returns the value of x chasing target. + * returns a value of x which is closer to target. + * but limited by maxDelta. # + * For example: + * x = 10.0f + * target = 50.0f + * maxDelta = 20.0f + * result is 30.0f (x is 20.0f units closer to target) + * However, if x is already within maxDelta units + * of target, x will equal target. + * For example: + * x = 55.0f + * target = 50.0f + * maxDelta = 20.0f + * result is 50.0f (x was already within 20.0f units of target) + */ +float Chase( float x, float target, float maxDelta ) +{ + float delta = target - x; + + if(delta > 0.0f) + { + x = std::min( x + maxDelta, target ); + } + else + { + x = std::max( x - maxDelta, target ); + } + + return x; +} + +// constraints //////////////////////////////////////////////////////////////// + +/** + * ScrollSlideInfoUpdate + * + * Info constraint updates an info struct with property info, + * so that constraints can use this instead of having it passed through + * as parameters. + */ +struct ScrollSlideInfoUpdate +{ + /** + * Constraint constructor + */ + ScrollSlideInfoUpdate(ScrollSlideInfoPtr scrollInfo) + : mScrollSlideInfo(scrollInfo) + { + } + + /** + * @param[in] current The current value + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen + * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging + * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect) + * @param[in] sizeProperty The size of the ScrollView. + * @return The new position of this Actor. + */ + float operator()(const float& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& effectReferenceProperty, + const PropertyInput& scrollSizeProperty, + const PropertyInput& scrollPositionMinProperty, + const PropertyInput& scrollPositionMaxProperty, + const PropertyInput& scrollWrapProperty) + { + mScrollSlideInfo->mScrollPosition = scrollPositionProperty.GetVector3(); + mScrollSlideInfo->mEffectReference = effectReferenceProperty.GetVector3(); + mScrollSlideInfo->mScrollSize = scrollSizeProperty.GetVector3(); + mScrollSlideInfo->mScrollPositionMin = scrollPositionMinProperty.GetVector3(); + mScrollSlideInfo->mScrollPositionMax = scrollPositionMaxProperty.GetVector3(); + mScrollSlideInfo->mScrollWrap = scrollWrapProperty.GetBoolean(); + + return current; + } + +private: + + ScrollSlideInfoPtr mScrollSlideInfo; + +}; + + +/** + * ScrollSlidePositionConstraint + * + * Position constraint adjusts the position of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated about it's origin + mAnchor + */ +struct ScrollSlidePositionConstraint +{ + /** + * Constraint constructor + */ + ScrollSlidePositionConstraint(ScrollSlideInfoPtr scrollInfo, float delayMin, float delayMax) + : mScrollSlideInfo(scrollInfo), + mScrollPosition(scrollInfo->mScrollPosition), + mDelayMin(delayMin), + mDelayMax(delayMax), + mAverageSpeed(0.0f) + { + } + + /** + * @param[in] current The current position + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen + * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging + * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect) + * @param[in] sizeProperty The size of the ScrollView. + * @return The new position of this Actor. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& effectTimeProperty, + const PropertyInput& deltaPositionProperty, + const PropertyInput& snapProperty) + { + const float complete = snapProperty.GetFloat(); + bool activate = (complete > Math::MACHINE_EPSILON_1); + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& scrollPosition = mScrollSlideInfo->mScrollPosition; + + // Get position of page. + Vector2 relativePosition(pagePosition + scrollPosition); + + // short circuit: for orthognal view and when the blending has been deactivated. + if( (!activate) && + (fabsf(relativePosition.x) < Math::MACHINE_EPSILON_1) && + (fabsf(relativePosition.y) < Math::MACHINE_EPSILON_1) ) + { + Vector3 actorPosition(current + scrollPosition); + return actorPosition; + } + + const Vector3& referencePoint = mScrollSlideInfo->mEffectReference; + const Vector3& scrollSize = mScrollSlideInfo->mScrollSize; + const Vector3& deltaPosition = deltaPositionProperty.GetVector3(); + + // 1. Determine the relative position of the actor from the scrolling reference point. + // (the further away from the reference, the longer the delay should be) + const Vector3& min = mScrollSlideInfo->mScrollPositionMin; + const Vector3& max = mScrollSlideInfo->mScrollPositionMax; + + relativePosition.y = (pagePosition.y + current.y - referencePoint.y) / scrollSize.height; + + // Smoothen the relativePosition value by averaging with mRelativePosition (avoids sudden jerk when + // user touche different points) + float shortestDirection = ShortestDistanceInDomain(mRelativePosition.y, relativePosition.y, min.y, max.y); + mRelativePosition.y += activate ? shortestDirection * ANIMATION_BLEND_COEFFICIENT : shortestDirection; + + // f represents this absolute distance. Get as a relative distance and inverse exponential + // (as delay equation has an exponential effect i.e. the closer delayFactor to 1.0f, + // the longer the delay would appear exponentially) + float f = fabsf(mRelativePosition.y) * complete; + f = std::min(f, 1.0f); + f = 1.0f - powf(ANIMATION_BLEND_COEFFICIENT, f); + + // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax + f = Mix(mDelayMin, mDelayMax, f); + + // 2. Now that f (delay factor) has been determined for this Actor, + // move mScrollPosition towards the actual scroll position, at rate determined by f. + float shortest = ShortestDistanceInDomain(mScrollPosition.x, WrapInDomain(scrollPosition.x, -min.x, -max.x), min.x, max.x); + mScrollPosition.x += activate ? shortest * (1.0f-f) : shortest; + mScrollPosition.x = WrapInDomain(mScrollPosition.x, -min.x, -max.x); + mScrollPosition.y = scrollPosition.y; + + Vector3 actorPosition(current + pagePosition + mScrollPosition); + + // Get position of actor. + bool wrap = mScrollSlideInfo->mScrollWrap; + + if(wrap) + { + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + actorPosition.x = WrapInDomain(actorPosition.x + scrollSize.x, min.x, max.x) - scrollSize.width; + } + } + + const float targetRelativePositionX = (referencePoint.x + deltaPosition.x); + + float blend(Mix(1.0f, ANIMATION_BLEND_COEFFICIENT, 1.0f - (1.0f - complete) * (1.0f - complete) )); + float invBlend(1.0f - blend); + + mRelativePosition.x = activate ? mRelativePosition.x * invBlend + targetRelativePositionX * blend : targetRelativePositionX; + mRelativePosition.x = Chase( mRelativePosition.x, targetRelativePositionX, 1.0f ); + + relativePosition.x = (actorPosition.x - mRelativePosition.x) / scrollSize.width; + + float difference = fabsf(ShortestDistanceInDomain(mScrollPosition.x, scrollPosition.x, -max.x, -min.x)); + mAverageSpeed = activate ? mAverageSpeed * invBlend + difference * blend : 0.0f; + + actorPosition.x += relativePosition.x * mAverageSpeed; + + return actorPosition - pagePosition; + } + +private: + + ScrollSlideInfoPtr mScrollSlideInfo; + Vector3 mScrollPosition; ///< The current scroll position + float mDelayMin; ///< Minimum delay rate (at closest position to touch) + float mDelayMax; ///< Maximum delay rate (at furthest position from touch - 1 page away) + Vector2 mRelativePosition; + float mAverageSpeed; ///< The Average speed of the Actor (proportional to mScrollPosition - scrollPosition) + +}; + +/** + * ScrollSlideScaleConstraint + * + */ +struct ScrollSlideScaleConstraint +{ + /** + * Constraint constructor + */ + ScrollSlideScaleConstraint() + { + } + + /** + * @param[in] current The current position + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen + * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging + * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect) + * @param[in] sizeProperty The size of the ScrollView. + * @return The new position of this Actor. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& snapProperty) + { + float scale = 1.0f + snapProperty.GetFloat() * 0.008f; + return Vector3( current.x * scale, current.y * scale, current.z); + } + +}; + +/** + * Applies the slide constraints to the child actor for overshoot effect. + * + * @param[in] scrollView The ScrollView containing the pages. + * @param[in] child The child to be affected with the slide effect. + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) as the page is scrolled. + * move for each axis (X and Y) as the page is scrolled. + */ +void ApplyScrollSlideConstraints(ScrollSlideInfoPtr scrollSlideInfo, + Toolkit::ScrollView scrollView, + Actor child, + float delayMin, + float delayMax) +{ + // Apply constraints to these actors // + Constraint constraint = Constraint::New( Actor::POSITION, + ParentSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_TIME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ), + ScrollSlidePositionConstraint(scrollSlideInfo, delayMin, delayMax) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + constraint = Constraint::New( Actor::SCALE, + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE ) ), + ScrollSlideScaleConstraint() ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewSlideEffect::ScrollViewSlideEffect() +: mScrollSlideInfo(new ScrollSlideInfo()), + mPropertyTime(Property::INVALID_INDEX), + mPropertyReference(Property::INVALID_INDEX), + mPropertyActive(Property::INVALID_INDEX), + mDelayReferenceOffset(Vector3::ZERO), + mMaxDelayDuration(DEFAULT_MAX_DELAY_DURATION) +{ +} + +ScrollViewSlideEffect::~ScrollViewSlideEffect() +{ +} + +bool ScrollViewSlideEffect::GetSlideDirection() const +{ + return mScrollSlideInfo->mVertical; +} + +void ScrollViewSlideEffect::SetSlideDirection(bool vertical) +{ + mScrollSlideInfo->mVertical = vertical; +} + +const Vector3& ScrollViewSlideEffect::GetDelayReferenceOffset() const +{ + return mDelayReferenceOffset; +} + +void ScrollViewSlideEffect::SetDelayReferenceOffset(const Vector3& offset) +{ + mDelayReferenceOffset = offset; +} + +float ScrollViewSlideEffect::GetMaxDelayDuration() const +{ + return mMaxDelayDuration; +} + +void ScrollViewSlideEffect::SetMaxDelayDuration(float duration) +{ + mMaxDelayDuration = duration; +} + +void ScrollViewSlideEffect::ApplyToActor(Actor child, + float delayMin, + float delayMax) +{ + ApplyScrollSlideConstraints( mScrollSlideInfo, + GetScrollView(), + child, + delayMin, + delayMax ); +} + +void ScrollViewSlideEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ + mScrollSlideInfo->mScrollPosition = scrollView.GetProperty( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ); + mScrollSlideInfo->mScrollSize = scrollView.GetProperty( Actor::SIZE ); + mScrollSlideInfo->mScrollPositionMin = scrollView.GetProperty( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ); + mScrollSlideInfo->mScrollPositionMax = scrollView.GetProperty( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ); + mScrollSlideInfo->mScrollWrap = scrollView.GetProperty( scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ); + mScrollSlideInfo->mVertical = false; + + // Create effect-time property if not already created. + if(mPropertyTime == Property::INVALID_INDEX) + { + mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_TIME, 0.0f ); + mPropertyReference = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE, Vector3::ZERO ); + mPropertyActive = SafeRegisterProperty( scrollView, Toolkit::ScrollViewSlideEffect::EFFECT_ACTIVE, 0.0f ); + } + + // Create constraint to update ScrollSlideInfo + // Doesn't matter what this is applied to and on what property. + // Just needs to update mScrollSlideInfo values as properties change. + // The minor constraints (applied to the Actors) can use this mScrollSlideInfo. + Constraint constraint = Constraint::New( mPropertyTime, + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewSlideEffect::EFFECT_REFERENCE ) ), + Source(scrollView, Actor::SIZE), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_WRAP_PROPERTY_NAME ) ), + ScrollSlideInfoUpdate(mScrollSlideInfo) ); + constraint.SetRemoveAction( Constraint::Discard ); + mInfoUpdateConstraint = scrollView.ApplyConstraint( constraint ); + + // Connect to the scroll view signals + scrollView.ScrollStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollStart); + scrollView.SnapStartedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollSnapStarted); + scrollView.TouchedSignal().Connect(this, &ScrollViewSlideEffect::OnScrollTouched); + + AttachActor(scrollView); +} + +bool ScrollViewSlideEffect::OnScrollTouched(Actor actor, const TouchEvent& event) +{ + // Ignore events with multiple-touch points + if (event.GetPointCount() != 1) + { + return false; + } + + if (event.GetPoint(0).state == TouchPoint::Down) + { + const TouchPoint& point = event.GetPoint(0); + Vector3 touchPosition(point.local - Stage::GetCurrent().GetSize() * 0.5f); + + Vector3 scrollPosition = GetScrollView().GetCurrentScrollPosition(); + GetScrollView().SetProperty(mPropertyReference, scrollPosition + touchPosition + mDelayReferenceOffset); + } + + return false; +} + +void ScrollViewSlideEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ + scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollStart); + scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollSnapStarted); + scrollView.TouchedSignal().Disconnect(this, &ScrollViewSlideEffect::OnScrollTouched); + scrollView.RemoveConstraint( mInfoUpdateConstraint ); + + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished); + mAnimation.Clear(); + mAnimation.Reset(); + } + + if(mAnimationSnap) + { + mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished); + mAnimationSnap.Clear(); + mAnimationSnap.Reset(); + } +} + +void ScrollViewSlideEffect::AttachActor(Actor actor) +{ + +} + +void ScrollViewSlideEffect::DetachActor(Actor actor) +{ + // TODO: remove the specific constraint defined in AttachActor (and possibly + // unregister property) - neither functionality exists in Dali. +} + +void ScrollViewSlideEffect::ContinueAnimation(float endTime) +{ + // continue animating + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished); + mAnimation.Clear(); + } + + Actor scrollView = GetScrollView(); + + mAnimation = Animation::New(SLIDEEFFECT_ANIMATION_MAX_TIME); + mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear ); + mAnimation.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationFinished); + mAnimation.Play(); +} + +void ScrollViewSlideEffect::OnScrollStart( const Vector3& position ) +{ + Actor scrollView = GetScrollView(); + GetScrollView().SetProperty(mPropertyTime, 0.0f); + + ContinueAnimation(SLIDEEFFECT_ANIMATION_MAX_TIME); + + if(mAnimationSnap) + { + mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished); + mAnimationSnap.Clear(); + } + + mAnimationSnap = Animation::New( EFFECT_SNAP_GROW_DURATION ); + mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 1.0f, AlphaFunctions::Linear ); + mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished); + mAnimationSnap.Play(); +} + +void ScrollViewSlideEffect::OnScrollSnapStarted(const Toolkit::ScrollView::SnapEvent& event) +{ + if(mAnimationSnap) + { + mAnimationSnap.Clear(); + } + + Actor scrollView = GetScrollView(); + mAnimationSnap = Animation::New(EFFECT_SNAP_DECAY_DURATION ); + mAnimationSnap.AnimateTo( Property(scrollView, mPropertyActive), 0.0f, AlphaFunctions::Linear ); + mAnimationSnap.FinishedSignal().Connect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished); + mAnimationSnap.Play(); +} + +void ScrollViewSlideEffect::OnAnimationSnapFinished( Animation& animation ) +{ + mAnimationSnap.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationSnapFinished); + mAnimationSnap.Clear(); + + // stop time animation + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewSlideEffect::OnAnimationFinished); + mAnimation.Clear(); + } +} + +void ScrollViewSlideEffect::OnAnimationFinished( Animation& animation ) +{ + // still unstable, so continue animating. + // TODO: Requires an instability check to ensure time animation finishes when delay is + // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints. + // best solution for this is to switch to a single history vector of scroll position, and compare if + // position has not deviated >= 0.5 pixel for the past 1 second. + float endTime = GetScrollView().GetProperty(mPropertyTime) + SLIDEEFFECT_ANIMATION_MAX_TIME; + ContinueAnimation(endTime); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h new file mode 100644 index 0000000..840bdd5 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.h @@ -0,0 +1,223 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_SLIDE_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_SLIDE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +class ScrollSlideInfo; + +typedef IntrusivePtr ScrollSlideInfoPtr; + +/** + * @copydoc Toolkit::ScrollViewSlideEffect + */ +class ScrollViewSlideEffect : public ScrollViewEffect +{ +public: + + /** + * Constructor + */ + ScrollViewSlideEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::GetSlideDirection + */ + bool GetSlideDirection() const; + + /** + * @copydoc ScrollViewEffect::SetSlideDirection + */ + void SetSlideDirection(bool vertical); + + /** + * @copydoc ScrollViewEffect::GetDelayReferenceOffset + */ + const Vector3& GetDelayReferenceOffset() const; + + /** + * @copydoc ScrollViewEffect::SetDelayReferenceOffset + */ + void SetDelayReferenceOffset(const Vector3& offset); + + /** + * @copydoc ScrollViewEffect::GetMaxDelayDuration + */ + float GetMaxDelayDuration() const; + + /** + * @copydoc ScrollViewEffect::SetMaxDelayDuration + */ + void SetMaxDelayDuration(float duration); + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor( Actor child, + float delayMin, + float delayMax ); +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewSlideEffect(); + +private: + + /** + * Invoked when user touches the scroll-view + * We keep track of the touch as this is used to determine + * the reference point which is used to determine the delay + * factor for the Actors' movements. + * @param[in] actor The actor touched + * @param[in] event The touch Event + * @return Whether to consume the even or not. + */ + bool OnScrollTouched(Actor actor, const TouchEvent& event); + + /** + * Signal handler, called when the ScrollView starts to move + * + * @param[in] position The current scroll position + */ + void OnScrollStart( const Vector3& position ); + + /** + * Signal handler, called when the ScrollView starts to snap + * @param[in] event The snap event. + */ + void OnScrollSnapStarted(const Toolkit::ScrollView::SnapEvent& event); + + /** + * Signal handler, called some time after the ScrollView has completed + * movement. There is a delay as when the ScrollView has completed + * movement, there are Actors that have a delay, and take time to arrive + * at their final destination. + * + * @param[in] animation The animation delegate for this delay + */ + void OnAnimationSnapFinished( Animation& animation ); + + /** + * Signal handler, called when the Wobble Effect animation has completed. + * + * @param[in] animation The animation. + */ + void OnAnimationFinished( Animation& animation ); + + /** + * Attaches effect to Scroll Actor (ScrollView) + * + * Applies the same wobble effect to each Scroll Actor. + * + * @param[in] actor The attached Actor + */ + void AttachActor(Actor actor); + + /** + * Detaches effect from Scroll Actor (ScrollView) + * + * @param[in] actor The attached Actor + */ + void DetachActor(Actor actor); + + /** + * Continues Animation to time reaches endTime + * + * @param[in] endTime the target time to reach. + */ + void ContinueAnimation(float endTime); + +private: + + ScrollSlideInfoPtr mScrollSlideInfo; ///< Info structure to keep track of common properties amongst many constraints. + ActiveConstraint mInfoUpdateConstraint; ///< Constraint applied to scroll-view to update Info structure. + Animation mAnimation; ///< Animation Timer to drive the twist effect constraint. + Animation mAnimationSnap; ///< Animation Snap (this animates from from 1.0 to 0.0 when contents snap) + Property::Index mPropertyTime; ///< Time property used by twist effect constraint to calculate timePassed. + Property::Index mPropertyReference; ///< Reference point in scroll-contents, this point has no delay. + ///< The further out from this point, the further the delay. + Property::Index mPropertyActive; ///< Property indicates the progress of the scrolling from 1.0f (scrolling) to 0.0f (fully snapped) + Vector3 mDelayReferenceOffset; ///< Where to offset the delay reference point when dragging. + float mMaxDelayDuration; ///< Maximum duration of effect after scroll-view completes. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewSlideEffect& GetImpl(Dali::Toolkit::ScrollViewSlideEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewSlideEffect& GetImpl(const Dali::Toolkit::ScrollViewSlideEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_SLIDE_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp new file mode 100755 index 0000000..8d78c44 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp @@ -0,0 +1,731 @@ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace // unnamed namespace +{ + +const float TWISTEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again) +const float TWISTEFFECT_DEFAULT_DROPOFF = 0.7f; ///< Default drop off amount +const float TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_X = 720.0f; ///< Default drop off distance +const float TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_Y = 1280.0f; ///< Default drop off distance + +// Hop Easing equation. +// Starts with a -ve cosine ranging from 0 to pi. +// Then plateaus. +// Then finishes with a -ve cosine ranging from pi to 0 +// 0......(RISE).....PI (SUSTAIN) PI.....(FALL)......0 +// xxxxxxxxxxxxxxxxxxxxx +// x x +// x x +// x x +// x x +// x x +// x x +// xxxxxx xxxxxx + +const float HOP_RISE(0.25f); +const float HOP_FALL(0.5f); +const float DELAY(0.5f); + +float HopEasing(float progress) +{ + // progress from 0.0 - HOP_RISE (go from 0.0 to 1.0) + if(progress < HOP_RISE) + { + return 0.5f - cosf(progress/HOP_RISE * Math::PI) * 0.5f; + } + + progress += HOP_FALL - 1.0f; + + // progress from 0.0 - HOP_FALL (go from 1.0 to 0.0) + if(progress > 0.0f ) + { + return 0.5f + cosf(progress/HOP_FALL * Math::PI) * 0.5f; + } + + // progress at plateau. + return 1.0f; +} + +/** + * Gets a property index. If the property doesn't already exist, then + * it will create the property. + * @param[in] handle The handle that owns or will own the property + * @param[in] name The name for this property + * @param[in] propertyValue The initial value for this property + * @return The property index for this property is returned. + */ +Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue ) +{ + Property::Index index = handle.GetPropertyIndex( name ); + + if(index == Property::INVALID_INDEX) + { + index = handle.RegisterProperty( name, propertyValue ); + } + + return index; +} + +/** + * Re-scales input value x from x0 - x1, to linearly map + * over the values y0 - y1. Values outside of this range + * will also conform to the trend (gradient) set. + * @param[in] x input X value + * @param[in] x0 input minimum bound + * @param[in] x1 input maximum bound + * @param[in] y0 output minimum bound + * @param[in] y1 output maximum bound + * @return The result of the mapping is returned. + */ +float Rescale(float x, float x0, float x1, float y0, float y1) +{ + return y0 + (y1 - y0) * (x - x0) / (x1-x0); +} + +/** + * Returns the value of x chasing target. + * returns a value of x which is closer to target. + * but limited by maxDelta. # + * For example: + * x = 10.0f + * target = 50.0f + * maxDelta = 20.0f + * result is 30.0f (x is 20.0f units closer to target) + * However, if x is already within maxDelta units + * of target, x will equal target. + * For example: + * x = 55.0f + * target = 50.0f + * maxDelta = 20.0f + * result is 50.0f (x was already within 20.0f units of target) + */ +float Chase( float x, float target, float maxDelta ) +{ + float delta = target - x; + + if(delta > 0.0f) + { + x = std::min( x + maxDelta, target ); + } + else + { + x = std::max( x - maxDelta, target ); + } + + return x; +} + +// constraints //////////////////////////////////////////////////////////////// + +/** + * ScrollTwistRotationConstraint + * + * Rotate constraint adjusts the angle of the Actors + * based on actor's world-position relative to the middle of the screen. + * When at middle of screen Angles on X and Y Axes is 0. + * When one screen away from the middle Angle is 90 degrees (pi/2) + */ +struct ScrollDropoffTwistRotationConstraint +{ + /** + * Constraint constructor + * @param[in] angleSwing The amount the Actor should revolve in radians + * for a given page worth of distance. + */ + ScrollDropoffTwistRotationConstraint(const Vector2& angleSwing, const Vector2& dropOff, const Vector2& distance, AlphaFunction function) + : mAngleSwing(angleSwing), + mDropOff(dropOff), + mDropOffDistance(distance), + mDropOffFunction(function) + { + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] actorPositionProperty The actor's world-position property + * @param[in] scrollOvershootXProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_X_PROPERTY_NAME) + * @param[in] scrollOvershootYProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_Y_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @return The new orientation of this Actor. + */ + Quaternion operator()(const Quaternion& current, + const PropertyInput& actorPositionProperty, + const PropertyInput& scrollablePositionProperty, + const PropertyInput& scrollOvershootXProperty, + const PropertyInput& scrollOvershootYProperty, + const PropertyInput& pageSizeProperty, + const PropertyInput& activateProperty) + { + const Vector3& position = actorPositionProperty.GetVector3(); + const Vector3& parentPosition = scrollablePositionProperty.GetVector3(); + const Vector3& pageSize = pageSizeProperty.GetVector3(); + const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat()); + + if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0) + { + return current; + } + + const float& activate = activateProperty.GetFloat(); + + if(activate < Math::MACHINE_EPSILON_0) + { + return current; + } + + // get distance from centre of scrollable container + Vector2 distance = position.GetVectorXY() - parentPosition.GetVectorXY(); + + if( overshoot.x > 0.0f ) + { + distance.x += pageSize.x * 0.5f; + } + else + { + distance.x -= pageSize.x * 0.5f; + } + distance.x = Clamp(fabsf(distance.x), 0.0f, mDropOffDistance.x); + + if( overshoot.y > 0.0f ) + { + distance.y += pageSize.y * 0.5f; + } + else + { + distance.y -= pageSize.y * 0.5f; + } + distance.y = Clamp(fabsf(distance.y), 0.0f, mDropOffDistance.y); + + Vector2 angleMod = distance / mDropOffDistance; + if(mDropOffFunction) + { + angleMod.x = mDropOffFunction(angleMod.x); + angleMod.y = mDropOffFunction(angleMod.y); + } + angleMod = Vector2::ONE - (angleMod * mDropOff); + + Vector2 angle = angleMod * mAngleSwing * overshoot; + + Quaternion rotation = Quaternion(angle.x, Vector3::YAXIS) * + Quaternion(-angle.y, Vector3::XAXIS) * + current; + + return rotation; + } + + const Vector2 mAngleSwing; ///< Maximum amount in X and Y axes to rotate. + const Vector2 mDropOff; + const Vector2 mDropOffDistance; + AlphaFunction mDropOffFunction; +}; + +/** + * ScrollTwistRotationConstraint + * + * Rotate constraint adjusts the angle of the Actors + * based on actor's world-position relative to the middle of the screen. + * When at middle of screen Angles on X and Y Axes is 0. + * When one screen away from the middle Angle is 90 degrees (pi/2) + */ +struct ScrollTwistRotationConstraint +{ + /** + * Constraint constructor + * @param[in] angleSwing The amount the Actor should revolve in radians + * for a given page worth of distance. + */ + ScrollTwistRotationConstraint(const Vector2& angleSwing) + : mAngleSwing(angleSwing) + { + } + + /** + * @param[in] current The current orientation of this Actor + * @param[in] actorPositionProperty The actor's world-position property + * @param[in] scrollOvershootXProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_X_PROPERTY_NAME) + * @param[in] scrollOvershootYProperty The scroll-view's overshoot property (SCROLL_OVERSHOOT_Y_PROPERTY_NAME) + * @param[in] pageSizeProperty The size of the page. (scrollView SIZE) + * @return The new orientation of this Actor. + */ + Quaternion operator()(const Quaternion& current, + const PropertyInput& scrollOvershootXProperty, + const PropertyInput& scrollOvershootYProperty, + const PropertyInput& activateProperty) + { + const Vector2 overshoot(scrollOvershootXProperty.GetFloat(), scrollOvershootYProperty.GetFloat()); + + if(fabsf(overshoot.x) < Math::MACHINE_EPSILON_0 && fabsf(overshoot.y) < Math::MACHINE_EPSILON_0) + { + return current; + } + + const float& activate = activateProperty.GetFloat(); + + if(activate < Math::MACHINE_EPSILON_0) + { + return current; + } + + Quaternion rotation = Quaternion(overshoot.x * mAngleSwing.x, Vector3::YAXIS) * + Quaternion(-overshoot.y * mAngleSwing.y, Vector3::XAXIS) * + current; + + return rotation; + } + + const Vector2 mAngleSwing; +}; + +/** + * ScrollTwistPositionConstraint + * + * Position constraint adjusts the position of the Actors + * based on their parent page's position relative to the middle of the screen. + * When at middle of the screen the position is not altered. + * When one screen away from middle the position is rotated about it's origin + mAnchor + */ +struct ScrollTwistPositionConstraint +{ + /** + * Constraint constructor + */ + ScrollTwistPositionConstraint(float delayMin, float delayMax) + : mDelayMin(delayMin), + mDelayMax(delayMax), + mCurrentDelayFactor(0.0f) + { + } + + /** + * @param[in] current The current position + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @param[in] effectReferenceProperty The point in the scroll-view where the user touched the screen + * @param[in] effectTimeProperty The current timer. Starting from 0.0 when scroll animation/dragging + * commences. Ensures that constraint is applied and processed every frame (to achieve the delay effect) + * @param[in] sizeProperty The size of the ScrollView. + * @return The new position of this Actor. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& pagePositionProperty, + const PropertyInput& scrollPositionProperty, + const PropertyInput& effectReferenceProperty, + const PropertyInput& effectTimeProperty, + const PropertyInput& sizeProperty, + const PropertyInput& activateProperty) + { + const Vector3& scrollPosition = scrollPositionProperty.GetVector3(); + const float& activate = activateProperty.GetFloat(); + + if(activate < Math::MACHINE_EPSILON_0) + { + mScrollPosition = scrollPosition; + return current + mScrollPosition; + } + const Vector3& pagePosition = pagePositionProperty.GetVector3(); + const Vector3& referencePoint = effectReferenceProperty.GetVector3(); + // Determine the relative position of the actor from the scrolling reference point. + // (the further away from the refernce, the longer the delay should be) + Vector3 relativePosition = pagePosition + current - referencePoint; + float f = relativePosition.x; + + // f represents this absolute distance. Get as a relative distance and inverse exponential + // (as delay equation is has an exponential effect i.e. the closer delayFactor to 1.0f, + // the longer the delay would appear exponentially) + f = fabsf(f / sizeProperty.GetVector3().width); + f = std::min(f, 1.0f); + f = 1.0f - (1.0f - f) * (1.0f - f); + // at center delay factor is mDelayMin, at maximum (1.0) it is mDelayMax + f = Rescale(f, 0.0f, 1.0f, mDelayMin, mDelayMax); + + // Will take 0.25s for current delay factor to equal target delay factor + // This prevents users quickly dragging from different points and noticing a jerk. + mCurrentDelayFactor = Chase( mCurrentDelayFactor, f, 4.0f/60.0f ); + float delay = activate * mCurrentDelayFactor; + mScrollPosition = mScrollPosition * delay + scrollPosition * (1.0f-delay); + + return current + mScrollPosition; + } + +private: + + Vector3 mScrollPosition; ///< The current scroll position + float mDelayMin; + float mDelayMax; + float mCurrentDelayFactor; + +}; + +/** + * ScrollTwistScaleConstraint + * + * Scale constraint adjusts the scale of the Actors + * based on a supplied depth property value. + */ +struct ScrollTwistScaleConstraint +{ + /** + * Constraint constructor + */ + ScrollTwistScaleConstraint(float scaleAmount) + : mScaleAmount(scaleAmount) + { + } + + /** + * @param[in] current The current position + * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME) + * @return The new position of this Actor. + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& depthProperty) + { + float depth = depthProperty.GetFloat(); + + return current * (1.0f - depth * mScaleAmount); // contract by mScaleAmount of original size. + } + +private: + + float mScaleAmount; +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +ScrollViewTwistEffect::ScrollViewTwistEffect() +: mFlags(DefaultFlags), + mPropertyTime(Property::INVALID_INDEX), + mEnableEffect(true), + mAdditionalEffects(false), + mPropertyReference(Property::INVALID_INDEX), + mPropertyActivate(Property::INVALID_INDEX), + mMinimumDistanceForShrink(Toolkit::ScrollViewTwistEffect::DEFAULT_MINIMUM_DISTANCE_FOR_SHRINK), + mMaxSwingAngle(Math::PI_2, Math::PI_2), + mDropOff(TWISTEFFECT_DEFAULT_DROPOFF, TWISTEFFECT_DEFAULT_DROPOFF), + mDropOffDistance(TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_X, TWISTEFFECT_DEFAULT_DROPOFF_DISTANCE_Y), + mDropOffFunction(NULL) +{ +} + +ScrollViewTwistEffect::~ScrollViewTwistEffect() +{ +} + + +float ScrollViewTwistEffect::GetMinimumDistanceForShrink() const +{ + return mMinimumDistanceForShrink; +} + +void ScrollViewTwistEffect::SetMinimumDistanceForShrink(float distance) +{ + mMinimumDistanceForShrink = distance; +} + +void ScrollViewTwistEffect::EnableEffect(bool enableFlag) +{ + mEnableEffect = enableFlag; +} + +void ScrollViewTwistEffect::ApplyToActor(Actor child, + bool additionalEffects, + const Vector2& angleSwing, + float scaleAmount, + float delayMin, + float delayMax) +{ + mMaxSwingAngle = angleSwing; + mAdditionalEffects = additionalEffects; + mScaleFactor = scaleAmount; + mDelayMin = delayMin; + mDelayMax = delayMax; + if(mFlags & FlagDefaultDropOff) + { + Vector3 size = GetScrollView().GetCurrentSize(); + // size may still be 0 if the effect is applied before scroll view hits the stage + if(size.x > Math::MACHINE_EPSILON_1) + { + mDropOffDistance.x = size.x; + } + if(size.y > Math::MACHINE_EPSILON_1) + { + mDropOffDistance.y = size.y; + } + } + if(scaleAmount > Math::MACHINE_EPSILON_0) + { + mFlags |= FlagScale; + } + else + { + mFlags = mFlags & ~FlagScale; + } + if(mMaxSwingAngle.LengthSquared() > Math::MACHINE_EPSILON_0) + { + mFlags |= FlagTwist; + } + else + { + mFlags = mFlags & ~FlagTwist; + } + Apply(child); +} + +void ScrollViewTwistEffect::Apply(Actor child) +{ + // Apply constraints to these actors // + Constraint constraint; + + Toolkit::ScrollView scrollView = GetScrollView(); + + if( mFlags & FlagScale ) + { + constraint = Constraint::New( Actor::SCALE, + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_DEPTH ) ), + ScrollTwistScaleConstraint(mScaleFactor) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + } + + constraint = Constraint::New( Actor::POSITION, + ParentSource(Actor::POSITION), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_REFERENCE ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_TIME ) ), + Source(scrollView, Actor::SIZE), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_ACTIVATE) ), + ScrollTwistPositionConstraint(mDelayMin, mDelayMax) ); + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + + // use actor position to affect rotation + if(mFlags & FlagTwist) + { + if(mFlags & FlagDropOff) + { + constraint = Constraint::New( Actor::ROTATION, + LocalSource(Actor::WORLD_POSITION), + Source(scrollView, Actor::WORLD_POSITION ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ), + Source(scrollView, Actor::SIZE ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_ACTIVATE) ), + ScrollDropoffTwistRotationConstraint(mMaxSwingAngle, mDropOff, mDropOffDistance, mDropOffFunction) ); + } + else + { + constraint = Constraint::New( Actor::ROTATION, + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME ) ), + Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollViewTwistEffect::EFFECT_ACTIVATE) ), + ScrollTwistRotationConstraint(mMaxSwingAngle) ); + } + constraint.SetRemoveAction( Constraint::Discard ); + child.ApplyConstraint( constraint ); + } +} + +void ScrollViewTwistEffect::SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function) +{ + if( mDropOffDistance.LengthSquared() > Math::MACHINE_EPSILON_1 && mDropOff.LengthSquared() > Math::MACHINE_EPSILON_1 ) + { + mFlags |= FlagDropOff; + mDropOff = dropOff; + mDropOffDistance = distance; + mDropOffFunction = function; + } + else + { + mFlags = mFlags & ~FlagDropOff; + } + // can no longer use default dop off + mFlags = mFlags & ~FlagDefaultDropOff; +} + +void ScrollViewTwistEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ + // Create effect-time property if not already created. + if(mPropertyTime == Property::INVALID_INDEX) + { + mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewTwistEffect::EFFECT_TIME, 0.0f ); + mPropertyReference = SafeRegisterProperty( scrollView, Toolkit::ScrollViewTwistEffect::EFFECT_REFERENCE, Vector3::ZERO ); + mPropertyDepth = SafeRegisterProperty( scrollView, Toolkit::ScrollViewTwistEffect::EFFECT_DEPTH, 0.0f); + mPropertyActivate = SafeRegisterProperty(scrollView,Toolkit::ScrollViewTwistEffect::EFFECT_ACTIVATE,1.0f); + } + + // Connect to the scroll view signals + scrollView.ScrollStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollStart); + scrollView.SnapStartedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollSnap); + scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollUpdate); + scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewTwistEffect::OnScrollComplete); + + AttachActor(scrollView); +} + +void ScrollViewTwistEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ + scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollStart); + scrollView.SnapStartedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollSnap); + scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollUpdate); + scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewTwistEffect::OnScrollComplete); + + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished); + mAnimation.Clear(); + mAnimation.Reset(); + } +} + +void ScrollViewTwistEffect::AttachActor(Actor actor) +{ + +} + +void ScrollViewTwistEffect::DetachActor(Actor actor) +{ + // TODO: remove the specific constraint defined in AttachActor (and possibly + // unregister property) - neither functionality exists in Dali. +} + +void ScrollViewTwistEffect::ContinueAnimation(float endTime) +{ + // continue animating + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished); + mAnimation.Clear(); + } + + Actor scrollView = GetScrollView(); + + mAnimation = Animation::New(TWISTEFFECT_ANIMATION_MAX_TIME); + mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear ); + mAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnAnimationFinished); + mAnimation.Play(); +} + +void ScrollViewTwistEffect::OnScrollStart( const Vector3& position ) +{ + if(mActivateAnimation) + { + // if the animation after Scroll complete not terminate before another scroll action, stop the animation before start again + mActivateAnimation.Stop(); + mActivateAnimation.Clear(); + mActivateAnimation = NULL; + } + + GetScrollView().SetProperty(mPropertyTime, 0.0f); + if(mEnableEffect) + { + GetScrollView().SetProperty(mPropertyActivate, 1.0f); + } + else + { + GetScrollView().SetProperty(mPropertyActivate, 0.0f); + } + GetScrollView().SetProperty(mPropertyReference, position); + + ContinueAnimation(TWISTEFFECT_ANIMATION_MAX_TIME); +} + +void ScrollViewTwistEffect::OnScrollUpdate( const Vector3& position ) +{ + // nothing to do +} + +void ScrollViewTwistEffect::OnScrollComplete( const Vector3& position ) +{ + if(!mEnableEffect) + { + OnActivateAnimationFinished(mAnimation); + return; + } + Actor scrollView = GetScrollView(); + scrollView.SetProperty(mPropertyActivate, 1.0f); + mActivateAnimation = Animation::New(DELAY); + mActivateAnimation.AnimateTo( Property(scrollView, mPropertyActivate), 0.0f, AlphaFunctions::Linear); + mActivateAnimation.FinishedSignal().Connect(this, &ScrollViewTwistEffect::OnActivateAnimationFinished); + mActivateAnimation.Play(); +} + +void ScrollViewTwistEffect::OnScrollSnap( const Toolkit::ScrollView::SnapEvent& event ) +{ + // If a Flicking snap is occuring and the distance is more than mMinimumDistanceForShrink + // then animate depth effect i.e. shrink actors and then bring back in to regular size. + // NOTE: ScrollView Snap returns a value opposite of GetCurrentScrollPosition + // i.e. if you've "scrolled 100 pixels right" (so content on screen has shifted 100 pixels left) + // then GetCurrentScrollPosition returns a positive value (100.0f, 0.0f) (position of where you're + // look, not where content has been moved to). + // event.position returns a negative value (-100.0f, 0.0f) + // Would be a good idea to change SnapEvent in the API so it reflects GetCurrentScrollPosition. + // TODO: Change scroll-view API, check if anything uses SnapEvent and change them correspondingly + Vector3 targetScrollPosition(-event.position); + + Vector3 delta = targetScrollPosition - GetScrollView().GetCurrentScrollPosition(); + + if(event.type==Flick && delta.Length() > mMinimumDistanceForShrink) + { + Actor scrollView = GetScrollView(); + + Animation animation = Animation::New(event.duration); + animation.AnimateTo( Property(scrollView, mPropertyDepth), 1.0f, HopEasing); + animation.Play(); + } +} + +void ScrollViewTwistEffect::OnAnimationFinished( Animation& animation ) +{ + // still unstable, so continue animating. + // TODO: Requires an instability check to ensure time animation finishes when delay is + // less noticeable. i.e. all present scrollPositions are approx the same as mScrollPosition in constraints. + // best solution for this is to switch to a single history vector of scroll position, and compare if + // position has not deviated >= 0.5 pixel for the past 1 second. + float endTime = GetScrollView().GetProperty(mPropertyTime) + TWISTEFFECT_ANIMATION_MAX_TIME; + ContinueAnimation(endTime); +} + +void ScrollViewTwistEffect::OnActivateAnimationFinished( Animation& animation ) +{ + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewTwistEffect::OnAnimationFinished); + mAnimation.Clear(); + mAnimation.Reset(); + } +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h new file mode 100644 index 0000000..43dc312 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.h @@ -0,0 +1,230 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_TWIST_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_TWIST_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewTwistEffect + */ +class ScrollViewTwistEffect : public ScrollViewEffect +{ + +public: + enum Flag + { + FlagTwist = 0x01, + FlagScale = 0x02, + FlagDropOff = 0x04, + FlagDefaultDropOff = 0x08, + DefaultFlags = FlagTwist | FlagDropOff | FlagDefaultDropOff + }; + + /** + * Constructor + */ + ScrollViewTwistEffect(); + +public: + + /** + * @copydoc ScrollViewEffect::GetMinimumDistanceForShrink + */ + float GetMinimumDistanceForShrink() const; + + /** + * @copydoc ScrollViewEffect::SetMinimumDistanceForShrink + */ + void SetMinimumDistanceForShrink(float distance); + + /** + * enable or disable this effect + * @param[in] enable when enableFlag is true, disable when enableFlag is false. + */ + void EnableEffect(bool enableFlag); + + /** + * @copydoc ScrollViewEffect::ApplyToActor + */ + void ApplyToActor( Actor child, + bool additionalEffects, + const Vector2& angleSwing, + float scaleAmount, + float delayMin, + float delayMax ); + + /** + * @copydoc Toolkit::ScrollViewEffect::Apply + */ + void Apply(Actor child); + + /** + * @copydoc Toolkit::ScrollViewEffect::SetMaxSwingAngle + */ + void SetMaxSwingAngle(const Vector2& maxSwingAngle) { mMaxSwingAngle = maxSwingAngle; } + + /** + * @copydoc Toolkit::ScrollViewEffect::SetSwingDropOff + */ + void SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function = NULL); + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewTwistEffect(); + +private: + + /** + * Signal handler, called when the ScrollView starts to move + * + * @param[in] position The current scroll position + */ + void OnScrollStart( const Vector3& position ); + + /** + * Signal handler, called when the ScrollView is moving + * + * @param[in] position The current scroll positionApply + */ + void OnScrollUpdate( const Vector3& position ); + + /** + * Signal handler, called when the ScrollView has completed movement + * + * @param[in] position The current scroll position + */ + void OnScrollComplete( const Vector3& position ); + + void OnScrollSnap( const Toolkit::ScrollView::SnapEvent& event ); + + /** + * Signal handler, called when the Wobble Effect animation has completed. + * + * @param[in] animation The animation. + */ + void OnAnimationFinished( Animation& animation ); + + void OnActivateAnimationFinished( Animation& animation ); + /** + * Attaches effect to Scroll Actor (ScrollView) + * + * Applies the same wobble effect to each Scroll Actor. + * + * @param[in] actor The attached Actor + */ + void AttachActor(Actor actor); + + /** + * Detaches effect from Scroll Actor (ScrollView) + * + * @param[in] actor The attached Actor + */ + void DetachActor(Actor actor); + + /** + * Continues Animation to time reaches endTime + * + * @param[in] endTime the target time to reach. + */ + void ContinueAnimation(float endTime); + +private: + + ushort mFlags; + Animation mAnimation; ///< Animation Timer to drive the twist effect constraint. + Animation mActivateAnimation; + Property::Index mPropertyTime; ///< Time property used by twist effect constraint to calculate timePassed. + bool mEnableEffect; ///< flag that decide whether enable or disable the twist effect. + bool mAdditionalEffects; ///< whether we want to apply extra effects + Property::Index mPropertyReference; ///< Reference point in scroll-contents, this point has no delay. + Property::Index mPropertyActivate; ///::< The further out from this point, the further the delay. + Property::Index mPropertyDepth; ///< Depth property used by twist effect constraint to shrink Actors. + float mMinimumDistanceForShrink; ///< Scrolling animation must last for longer than this amount to do shrink effect.s + Vector2 mMaxSwingAngle; ///< Maximum swing angle to be applied to x and y axes + Vector2 mDropOff; ///< Amount to drop off the angle on each axis of drop off distance + Vector2 mDropOffDistance; ///< Distance to apply the full drop off over + AlphaFunction mDropOffFunction; ///< Function to adjust how drop off is aplied over the distance, NULL for linear + float mScaleFactor; ///< Amount to scale by during translation + float mDelayMin; + float mDelayMax; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::ScrollViewTwistEffect& GetImpl(Dali::Toolkit::ScrollViewTwistEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::ScrollViewTwistEffect& GetImpl(const Dali::Toolkit::ScrollViewTwistEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::RefObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_TWIST_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.cpp new file mode 100644 index 0000000..5e82f95 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.cpp @@ -0,0 +1,314 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +using namespace Dali; + +namespace // unnamed namespace +{ + +const float WOBBLEEFFECT_FRICTION_COEFFICIENT = 0.8f; ///< (deacc) - velocity multiplier per unit time (80%) +const float WOBBLEEFFECT_IMPULSE_DISTANCE_FACTOR = 0.1f; ///< (acc) - move by 10% of distance-delta per unit time +const float WOBBLEEFFECT_TIME_FACTOR = 30.0f; ///< (T) - 30 times faster (unit time = 1/30th sec) +const float WOBBLEEFFECT_ANIMATION_MAX_TIME = 60.0f; ///< Animation time (every time finishes, checks if it needs to go again) +const float WOBBLEEFFECT_STABLE_TIME_THRESHOLD = 0.5f; ///< Must be stable for more than half a second to stop animating. +const float STABILITY_DELTA_THRESHOLD = Math::MACHINE_EPSILON_10000; ///< When velocity delta is greater than this threshold it is considered in motion. + +/** + * Gets a property index. If the property doesn't already exist, then + * it will create the property. + * @param[in] handle The handle that owns or will own the property + * @param[in] name The name for this property + * @param[in] propertyValue The initial value for this property + * @return The property index for this property is returned. + */ +Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue ) +{ + Property::Index index = handle.GetPropertyIndex( name ); + + if(index == Property::INVALID_INDEX) + { + index = handle.RegisterProperty( name, propertyValue ); + } + + return index; +} + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * ScrollView WobbleEffect constraint + * This constraint has a chase position and velocity, that chases + * a target position (scroll-position + scroll-offset). As it has a + * velocity. It will eventually pass it's target position, and chase back + * in the opposite direction. As it has a friction coefficient, it will + * gradually slow, and reach it's target position (stabilized). + */ +struct ScrollViewWobbleEffectConstraint +{ + /** + * @param[in,out] wobbleEffect Reference to wobbleEffect instance + */ + ScrollViewWobbleEffectConstraint(ScrollViewWobbleEffect& wobbleEffect) + : mChase(Vector3::ZERO), + mVelocity(Vector3::ZERO), + mTime(0.0f), + mStabilityTimeCounter(0), + mStabilized(true), + mWobbleEffect(wobbleEffect), + mAnimationCycleId(0) + { + + } + + /** + * @param[out] current The new wobble value + * @param[in] propertyTime The current time since the wobble effect started + * @param[in] propertyPosition The scroll-position + * @param[in] propertyOffset The scroll-overshoot + */ + Vector3 operator()(const Vector3& current, + const PropertyInput& propertyTime, + const PropertyInput& propertyPosition, + const PropertyInput& propertyOffsetX, + const PropertyInput& propertyOffsetY) + { + Vector3 dir; + + if(mStabilized) + { + // check if animation cycle id has changed (if so then this spells + // the start of a new animation) + if(mAnimationCycleId != mWobbleEffect.GetAnimationCycleId()) + { + mStabilized = false; + } + } + else + { + // not stable (i.e. wobbling) + Vector3 offset(propertyOffsetX.GetFloat(), propertyOffsetY.GetFloat(), 0.0f); + const Vector3& position = propertyPosition.GetVector3() - offset; + const float time = propertyTime.GetFloat(); + const float timePassed = time - mTime; + + mTime = time; + + if(timePassed>0) + { + const Vector3 delta = position - mChase; + + // Check to see if wobble has stabilized. + if( (fabsf(delta.x) < STABILITY_DELTA_THRESHOLD) && + (fabsf(mVelocity.x) < STABILITY_DELTA_THRESHOLD) ) + { + mStabilityTimeCounter+=timePassed; + + if(mStabilityTimeCounter > WOBBLEEFFECT_STABLE_TIME_THRESHOLD) + { + mStabilityTimeCounter = 0.0f; + mStabilized = true; + mWobbleEffect.IncrementStableCount(); + mAnimationCycleId = mWobbleEffect.GetAnimationCycleId(); + } + } + else + { + mStabilityTimeCounter = 0.0f; + } + + if(!mStabilized) + { + float t = timePassed * WOBBLEEFFECT_TIME_FACTOR; + + mVelocity *= pow( WOBBLEEFFECT_FRICTION_COEFFICIENT, t ); + mVelocity += delta * 0.1f * t; + mChase += mVelocity; + } + else + { + // stabilized, so chase should be exactly on position. + mChase = position; + } + } + + dir.x = propertyPosition.GetVector3().x - mChase.x; + dir.y = propertyPosition.GetVector3().y - mChase.y; + } // end else + + return dir; + } + + Vector3 mChase; ///< Chaser position + Vector3 mVelocity; ///< Velocity of Chaser + float mTime; ///< Current time. + float mStabilityTimeCounter; ///< Time in seconds that stable for. + bool mStabilized; ///< Stabilized flag. + ScrollViewWobbleEffect& mWobbleEffect; ///< Reference to Wobble Effect + unsigned int mAnimationCycleId; ///< Animation Cycle Id +}; + +ScrollViewWobbleEffect::ScrollViewWobbleEffect() +: mPropertyTime(Property::INVALID_INDEX) +{ + +} + +ScrollViewWobbleEffect::~ScrollViewWobbleEffect() +{ +} + +void ScrollViewWobbleEffect::IncrementStableCount() +{ + mStableCurrent++; +} + +unsigned int ScrollViewWobbleEffect::GetAnimationCycleId() const +{ + return mAnimationCycleId; +} + +void ScrollViewWobbleEffect::OnAttach(Toolkit::ScrollView& scrollView) +{ + mStableCurrent = 0; + mAnimationCycleId = 0; + + // Create effect-time property if not already created. + if(mPropertyTime == Property::INVALID_INDEX) + { + mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewWobbleEffect::EFFECT_TIME, 0.0f ); + } + + // Connect to the scroll view signals + scrollView.ScrollStartedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollStart); + scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollUpdate); + scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollComplete); + + AttachActor(scrollView); +} + +void ScrollViewWobbleEffect::OnDetach(Toolkit::ScrollView& scrollView) +{ + scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollStart); + scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollUpdate); + scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollComplete); + + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished); + mAnimation.Clear(); + mAnimation.Reset(); + } +} + +void ScrollViewWobbleEffect::AttachActor(Actor actor) +{ + Property::Index propertyPosition = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME); + Property::Index propertyOvershootX = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME); + Property::Index propertyOvershootY = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME); + + // Create effect-overshoot property if not already created. + Property::Index propertyEffectOvershoot = actor.GetPropertyIndex(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT); + if(propertyEffectOvershoot == Property::INVALID_INDEX) + { + propertyEffectOvershoot = actor.RegisterProperty(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT, Vector3::ZERO); + } + + Actor scrollView = GetScrollView(); + + Constraint constraint = Constraint::New( propertyEffectOvershoot, + Source(scrollView, mPropertyTime), + Source(actor, propertyPosition), + Source(actor, propertyOvershootX), + Source(actor, propertyOvershootY), + ScrollViewWobbleEffectConstraint(*this) ); + actor.ApplyConstraint(constraint); +} + +void ScrollViewWobbleEffect::DetachActor(Actor actor) +{ + // TODO: remove the specific constraint defined in AttachActor (and possibly + // unregister property) - neither functionality exists in Dali. +} + +void ScrollViewWobbleEffect::ContinueAnimation(float endTime) +{ + + // continue animating + if(mAnimation) + { + mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished); + mAnimation.Clear(); + } + + Actor scrollView = GetScrollView(); + + mAnimation = Animation::New(WOBBLEEFFECT_ANIMATION_MAX_TIME); + mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear ); + mAnimation.FinishedSignal().Connect(this, &ScrollViewWobbleEffect::OnAnimationFinished); + mAnimation.Play(); + +} + +void ScrollViewWobbleEffect::OnScrollStart( const Vector3& position ) +{ + // When animation starts, all constraints all unstable, + // and we change the animation cycle id. + mStableCurrent = 0; + mAnimationCycleId++; + + GetScrollView().SetProperty(mPropertyTime, 0.0f); + + ContinueAnimation(WOBBLEEFFECT_ANIMATION_MAX_TIME); +} + +void ScrollViewWobbleEffect::OnScrollUpdate( const Vector3& position ) +{ + // nothing to do +} + +void ScrollViewWobbleEffect::OnScrollComplete( const Vector3& position ) +{ + // nothing to do +} + +void ScrollViewWobbleEffect::OnAnimationFinished( Animation& animation ) +{ + if(mStableCurrent!=1) + { + // still unstable, so continue animating. + float endTime = GetScrollView().GetProperty(mPropertyTime) + WOBBLEEFFECT_ANIMATION_MAX_TIME; + ContinueAnimation(endTime); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.h b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.h new file mode 100644 index 0000000..e187bc7 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.h @@ -0,0 +1,160 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_WOBBLE_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_WOBBLE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +class Animation; + +namespace Toolkit +{ + +class ScrollView; + +namespace Internal +{ + +class ScrollViewEffect; + +/** + * @copydoc Toolkit::ScrollViewWobbleEffect + */ +class ScrollViewWobbleEffect : public ScrollViewEffect +{ + +public: + + /** + * Constructor + */ + ScrollViewWobbleEffect(); + + /** + * Increases the Stable Count (when this reaches mStableTotal) + * Then all constraints are stable, and the animation can stop. + */ + void IncrementStableCount(); + + /** + * Returns animation cycle id. Every time a new animation starts + * this cycle id is increased. + * + * @return The Animation Cycle id is returned. + */ + unsigned int GetAnimationCycleId() const; + +public: + + /** + * @copydoc ScrollViewEffect::OnAttach + */ + virtual void OnAttach(Toolkit::ScrollView& scrollView); + + /** + * @copydoc ScrollViewEffect::OnDetach + */ + virtual void OnDetach(Toolkit::ScrollView& scrollView); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ScrollViewWobbleEffect(); + +private: + + /** + * Signal handler, called when the ScrollView starts to move + * + * @param[in] position The current scroll position + */ + void OnScrollStart( const Vector3& position ); + + /** + * Signal handler, called when the ScrollView is moving + * + * @param[in] position The current scroll position + */ + void OnScrollUpdate( const Vector3& position ); + + /** + * Signal handler, called when the ScrollView has completed movement + * + * @param[in] position The current scroll position + */ + void OnScrollComplete( const Vector3& position ); + + /** + * Signal handler, called when the Wobble Effect animation has completed. + * + * @param[in] animation The animation. + */ + void OnAnimationFinished( Animation& animation ); + + /** + * Attaches effect to Scroll Actor (ScrollView) + * + * Applies the same wobble effect to each Scroll Actor. + * + * @param[in] actor The attached Actor + */ + void AttachActor(Actor actor); + + /** + * Detaches effect from Scroll Actor (ScrollView) + * + * @param[in] actor The attached Actor + */ + void DetachActor(Actor actor); + + /** + * Continues Animation to time reaches endTime + * + * @param[in] endTime the target time to reach. + */ + void ContinueAnimation(float endTime); + +private: + + Animation mAnimation; ///< Animation Timer to drive the wobble effect constraint. + Property::Index mPropertyTime; ///< Time property used by wobble effect constraint to calculate timePassed. + + int mStableCurrent; ///< Stability current - how many wobble constraints are not wobbling (or have neglible wobble). + unsigned int mAnimationCycleId; ///< The start of each new animation cycle is a unique number. + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLL_VIEW_WOBBLE_EFFECT_H__ diff --git a/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp b/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp new file mode 100644 index 0000000..1421d3e --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp @@ -0,0 +1,198 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +using namespace Dali; + +namespace +{ + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +BaseHandle Create() +{ + // empty handle as we cannot create Scrollable (but type registered for scroll signal) + return BaseHandle(); +} + +TypeRegistration mType( typeid(Toolkit::Scrollable), typeid(Toolkit::Control), Create ); + +SignalConnectorType s1(mType, Toolkit::Scrollable::SIGNAL_SCROLL_STARTED, &Scrollable::DoConnectSignal); +SignalConnectorType s2(mType, Toolkit::Scrollable::SIGNAL_SCROLL_COMPLETED, &Scrollable::DoConnectSignal); +SignalConnectorType s3(mType, Toolkit::Scrollable::SIGNAL_SCROLL_UPDATED, &Scrollable::DoConnectSignal); +SignalConnectorType s4(mType, Toolkit::Scrollable::SIGNAL_SCROLL_CLAMPED, &Scrollable::DoConnectSignal); + +} + +const std::string Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL( "scrollable-can-scroll-vertical" ); +const std::string Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL( "scrollable-can-scroll-horizontal" ); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Scrollable +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Scrollable::Scrollable() +: ControlImpl(true/*requires touch*/), + mPropertyRelativePosition(Property::INVALID_INDEX), + mPropertyPositionMin(Property::INVALID_INDEX), + mPropertyPositionMax(Property::INVALID_INDEX), + mPropertyScrollDirection(Property::INVALID_INDEX), + mOvershootEnabled(false) +{ +} + +Scrollable::~Scrollable() +{ + // Clear scroll components, forces their destruction before Scrollable is destroyed. + mComponents.clear(); +} + +void Scrollable::RegisterCommonProperties() +{ + Actor self = Self(); + + // Register properties. + mPropertyRelativePosition = self.RegisterProperty(Toolkit::Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME, Vector3::ZERO); + mPropertyPositionMin = self.RegisterProperty(Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME, Vector3::ZERO); + mPropertyPositionMax = self.RegisterProperty(Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME, Vector3::ZERO); + mPropertyScrollDirection = self.RegisterProperty(Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME, Vector3::ZERO); + mPropertyCanScrollVertical = self.RegisterProperty(SCROLLABLE_CAN_SCROLL_VERTICAL, true); + mPropertyCanScrollHorizontal = self.RegisterProperty(SCROLLABLE_CAN_SCROLL_HORIZONTAL, true); +} + +bool Scrollable::IsScrollComponentEnabled(Toolkit::Scrollable::ScrollComponentType type) const +{ + if(type == Toolkit::Scrollable::OvershootIndicator) + { + return mOvershootEnabled; + } + return (mComponents.find(type) != mComponents.end()); +} + +void Scrollable::EnableScrollComponent(Toolkit::Scrollable::ScrollComponentType type) +{ + if(type == Toolkit::Scrollable::OvershootIndicator) + { + SetOvershootEnabled(true); + mOvershootEnabled = true; + return; + } + if( mComponents.find(type) == mComponents.end() ) + { + // Create ScrollComponent + Toolkit::Scrollable scrollable = Toolkit::Scrollable::DownCast(Self()); + Toolkit::ScrollComponent scrollComponent = ScrollComponent::New(scrollable, type); + Toolkit::Internal::ScrollComponent& component = static_cast(scrollComponent.GetImplementation()); + ScrollComponentPtr componentPtr(&component); + + mComponents[type] = componentPtr; + } +} + +void Scrollable::DisableScrollComponent(Toolkit::Scrollable::ScrollComponentType type) +{ + if(type == Toolkit::Scrollable::OvershootIndicator) + { + SetOvershootEnabled(false); + mOvershootEnabled = false; + return; + } + ComponentIter pair = mComponents.find( type ); + + if( mComponents.end() != pair ) + { + ScrollComponentPtr component = pair->second; + + // Disconnect the scroll component first. + component->OnDisconnect(); + + // Destroy ScrollComponent. + mComponents.erase( type ); + } +} + +Toolkit::Scrollable::ScrollStartedSignalV2& Scrollable::ScrollStartedSignal() +{ + return mScrollStartedSignalV2; +} + +Toolkit::Scrollable::ScrollUpdatedSignalV2& Scrollable::ScrollUpdatedSignal() +{ + return mScrollUpdatedSignalV2; +} + +Toolkit::Scrollable::ScrollCompletedSignalV2& Scrollable::ScrollCompletedSignal() +{ + return mScrollCompletedSignalV2; +} + +Toolkit::Scrollable::ScrollClampedSignalV2& Scrollable::ScrollClampedSignal() +{ + return mScrollClampedSignalV2; +} + +bool Scrollable::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::Scrollable scrollable = Toolkit::Scrollable::DownCast( handle ); + + if( Toolkit::Scrollable::SIGNAL_SCROLL_STARTED == signalName ) + { + scrollable.ScrollStartedSignal().Connect( tracker, functor ); + } + else if( Toolkit::Scrollable::SIGNAL_SCROLL_UPDATED == signalName ) + { + scrollable.ScrollUpdatedSignal().Connect( tracker, functor ); + } + else if( Toolkit::Scrollable::SIGNAL_SCROLL_COMPLETED == signalName ) + { + scrollable.ScrollCompletedSignal().Connect( tracker, functor ); + } + else if( Toolkit::Scrollable::SIGNAL_SCROLL_CLAMPED == signalName ) + { + scrollable.ScrollClampedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/scrollable/scrollable-impl.h b/dali-toolkit/internal/controls/scrollable/scrollable-impl.h new file mode 100644 index 0000000..b784c02 --- /dev/null +++ b/dali-toolkit/internal/controls/scrollable/scrollable-impl.h @@ -0,0 +1,235 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SCROLLABLE_H__ +#define __DALI_TOOLKIT_INTERNAL_SCROLLABLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class Scrollable; +typedef IntrusivePtr ScrollablePtr; + +/** + * @copydoc Toolkit::Scrollable + */ +class Scrollable : public ControlImpl +{ +public: + static const std::string SCROLLABLE_CAN_SCROLL_VERTICAL; + static const std::string SCROLLABLE_CAN_SCROLL_HORIZONTAL; + + /** + * Create a new Scrollable. + * @return A public handle to the newly allocated Scrollable. + */ +// static Dali::Toolkit::Scrollable New(); + +public: + + /** + * @copydoc Dali::Toolkit::Scrollable::IsScrollComponentEnabled(Scrollable::ScrollComponentType type) + */ + bool IsScrollComponentEnabled(Toolkit::Scrollable::ScrollComponentType type) const; + + /** + * @copydoc Dali::Toolkit::Scrollable::EnableScrollComponent(Scrollable::ScrollComponentType type) + */ + void EnableScrollComponent(Toolkit::Scrollable::ScrollComponentType type); + + /** + * @copydoc Dali::Toolkit::Scrollable::DisableScrollComponent(Scrollable::ScrollComponentType type) + */ + void DisableScrollComponent(Toolkit::Scrollable::ScrollComponentType type); + + /** + * Gets the size of the domain (minimum/maximum extents for each axis to scroll to) + * @return the domain size + */ + virtual Vector3 GetDomainSize() const = 0; + + /** + * Adds actor as an Overlay to Scrollable + * This method is called by Add-on UI components + * such as scroll bars, page indicators. + * @param[in] actor Actor to add as an overlay. + */ + virtual void AddOverlay(Actor actor) = 0; + + /** + * Removes overlay actor from Scrollable + * This method is called by Add-on UI components + * such as scroll bars, page indicators. + * @param[in] actor Actor overlay to remove. + */ + virtual void RemoveOverlay(Actor actor) = 0; + + /** + * Retrieves current scroll position. + * @returns The current scroll position. + */ + virtual Vector3 GetCurrentScrollPosition() const = 0; + + /** + * Scrolls Scrollable to position specified (contents will scroll to this position) + * Position 0,0 is the origin. Increasing X scrolls contents left, while + * increasing Y scrolls contents up. + * @param[in] position The position to scroll to. + * @param[in] duration The duration of the animation in seconds + */ + virtual void ScrollTo(const Vector3 &position, float duration) = 0; + +private: + + /** + * Temporary function to override EnableScrollComponent functionality for overshoot + * Only ScrollView needs to override this as HQ has not requested disable functionality in ItemView + * @param[in] enable true to enable, false to disable overshoot indicator + */ + virtual void SetOvershootEnabled(bool enable) {} + +public: //Signals + + /** + * @copydoc Dali::Toolkit::Scrollable::ScrollStartedSignal() + */ + Toolkit::Scrollable::ScrollStartedSignalV2& ScrollStartedSignal(); + + /** + * @copydoc Dali::Toolkit::Scrollable::ScrollUpdatedSignal() + */ + Toolkit::Scrollable::ScrollUpdatedSignalV2& ScrollUpdatedSignal(); + + /** + * @copydoc Dali::Toolkit::Scrollable::ScrollCompletedSignal() + */ + Toolkit::Scrollable::ScrollCompletedSignalV2& ScrollCompletedSignal(); + + /** + * @copydoc Dali::Toolkit::Scrollable::ScrollClampedSignal() + */ + Toolkit::Scrollable::ScrollClampedSignalV2& ScrollClampedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: + + /** + * Construct a new Scrollable. + */ + Scrollable(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Scrollable(); + + /** + * Register common properties + */ + void RegisterCommonProperties(); + +private: + + /** + * Gets position property. + * + * @return The current position + */ + Vector3 GetPropertyPosition() const; + +private: + + // Undefined + Scrollable(const Scrollable&); + + // Undefined + Scrollable& operator=(const Scrollable& rhs); + +protected: + + Property::Index mPropertyRelativePosition;///< Scroll Relative Position ("scroll-relative-position") [range from 0.0f - 1.0f in each axes] + Property::Index mPropertyPositionMin; ///< Scroll Domain Minimum ("position-min") + Property::Index mPropertyPositionMax; ///< Scroll Domain Maximum ("position-max") + Property::Index mPropertyScrollDirection; ///< Scroll direction ("scroll-direction") + Property::Index mPropertyCanScrollVertical; ///< Whether the current scroll domain is large enough to scroll vertically + Property::Index mPropertyCanScrollHorizontal; ///< Whether the current scroll domain is large enough to scroll horizontally + + std::map mComponent; ///< ScrollComponent (such as a scrollbar/page indicator/status) + + Toolkit::Scrollable::ScrollStartedSignalV2 mScrollStartedSignalV2; + Toolkit::Scrollable::ScrollUpdatedSignalV2 mScrollUpdatedSignalV2; + Toolkit::Scrollable::ScrollCompletedSignalV2 mScrollCompletedSignalV2; + Toolkit::Scrollable::ScrollClampedSignalV2 mScrollClampedSignalV2; + +private: + + typedef std::map ComponentContainer; + typedef ComponentContainer::iterator ComponentIter; + + ComponentContainer mComponents; ///< ScrollComponent (such as a scrollbar/page indicator/status) + bool mOvershootEnabled; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Scrollable& GetImpl(Toolkit::Scrollable& scrollable) +{ + DALI_ASSERT_ALWAYS(scrollable); + + Dali::RefObject& handle = scrollable.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::Scrollable& GetImpl(const Toolkit::Scrollable& scrollable) +{ + DALI_ASSERT_ALWAYS(scrollable); + + const Dali::RefObject& handle = scrollable.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SCROLLABLE_H__ diff --git a/dali-toolkit/internal/controls/selectors/rotating-selector-impl.cpp b/dali-toolkit/internal/controls/selectors/rotating-selector-impl.cpp new file mode 100644 index 0000000..eac92bd --- /dev/null +++ b/dali-toolkit/internal/controls/selectors/rotating-selector-impl.cpp @@ -0,0 +1,273 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +#include "dali-toolkit/public-api/controls/selectors/rotating-selector.h" + +using namespace std; + +namespace +{ +const float TOUCH_OPACITY_THRESHOLD = 0.1f; +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +namespace +{ +//Type registration +BaseHandle Create() +{ + // empty handle, as RotatingSelector takes children during construction + return Toolkit::RotatingSelector(); +} +TypeRegistration mType( typeid(Toolkit::RotatingSelector), typeid(Toolkit::Control), Create ); + +const Quaternion ROTATION_ANGLE(0.0f, Vector3(1.0f, 0.0f, 0.0f)); +} + +Dali::Toolkit::RotatingSelector RotatingSelector::New(Actor& unSelectedActor, Actor& selectedActor) +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< RotatingSelector > customCheckActor = new RotatingSelector; + + // Pass ownership to CustomActor + Dali::Toolkit::RotatingSelector handle( *customCheckActor ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + customCheckActor->Initialize(); + + customCheckActor->SetSelectedActor(selectedActor); + customCheckActor->SetUnSelectedActor(unSelectedActor); + + return handle; +} + +void RotatingSelector::OnInitialize() +{ + mContainer = Actor::New(); + mContainer.SetName("Selector Container"); + mUnSelectedActor = Actor::New(); + mSelectedActor = Actor::New(); + + mRotateAnimation = Animation::New(0.5f); + mRotateAnimation.FinishedSignal().Connect(this, &RotatingSelector::AnimationCompleted); + + mUnSelectedActor.SetParentOrigin(ParentOrigin::CENTER); + mUnSelectedActor.SetAnchorPoint(AnchorPoint::CENTER); + + mSelectedActor.SetParentOrigin(ParentOrigin::CENTER); + mSelectedActor.SetAnchorPoint(AnchorPoint::CENTER); + + mContainer.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION ); + + Constraint constraint = Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ); + mSelectedActor.ApplyConstraint(constraint); + mUnSelectedActor.ApplyConstraint(constraint); + mContainer.ApplyConstraint(constraint); + + mContainer.Add(mUnSelectedActor); + mSelectedActor.SetRotation(Radian(Math::PI), Vector3::XAXIS ); + + mUnSelectedActor.SetName("RotatingSelector : UnSelectedActor"); + mSelectedActor.SetName("RotatingSelector : SelectedActor"); + + Self().Add(mContainer); + Self().SetLeaveRequired(true); + + mRotateAnimation.RotateBy(mContainer, Radian(Math::PI), Vector3(1.0f, 0.0f, 0.0f)); +} + +RotatingSelector::RotatingSelector() +: ControlImpl(true/*requires touch*/), + mSelected(false), + mSelectable(true), + mIsAnimating(false) +{ +} + +RotatingSelector::~RotatingSelector() +{ + mRotateAnimation.Reset(); +} + +void RotatingSelector::SetSelected( bool toggle ) +{ + if(toggle != mSelected) + { + if( mSelectable ) + { + ToggleAndAnimateSelection(); + } + } +} + +void RotatingSelector::SetSelectedActor( Actor& selectedActor ) +{ + unsigned int numChildren = mSelectedActor.GetChildCount(); + for( unsigned int i=0; i TOUCH_OPACITY_THRESHOLD) + { + mPressed = true; + } + break; + case TouchPoint::Leave: + mPressed = false; + break; + case TouchPoint::Up: + { + if(mSelectable && mPressed) + { + ToggleAndAnimateSelection(); + } + mPressed = false; + break; + } + default: + break; + } + } + + return false; // dont consume +} + +void RotatingSelector::ToggleAndAnimateSelection() +{ + if(!mIsAnimating) + { + mSelected = !mSelected; + if(mSelected) + { + //The checked image (i.e mSelectedActor should be in front) + mSelectedActor.SetPosition(0.0f, 0.0f, -1.0f); + mContainer.Add(mSelectedActor); + } + else + { + //The un checked image (i.e mUnSelectedActor should be in front) + mUnSelectedActor.SetPosition(0.0f, 0.0f, 1.0f); + mContainer.Add(mUnSelectedActor); + } + + mIsAnimating = true; + mRotateAnimation.Play(); + } +} + +void RotatingSelector::AnimationCompleted( Animation& animation ) +{ + if(mSelected) + { + //The checked image (i.e mSelectedActor should be in front) + mSelectedActor.SetPosition(0.0f, 0.0f, 0.0f); + mContainer.Remove(mUnSelectedActor); + + } + else + { + //The un checked image (i.e mUnSelectedActor should be in front) + mContainer.Remove(mSelectedActor); + mUnSelectedActor.SetPosition(0.0f, 0.0f, 0.0f); + } + + mIsAnimating = false; + + //Emit signal. + Dali::Toolkit::RotatingSelector handle( GetOwner() ); + mCheckedSignalV2.Emit( handle, mSelected ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/selectors/rotating-selector-impl.h b/dali-toolkit/internal/controls/selectors/rotating-selector-impl.h new file mode 100644 index 0000000..95e4b56 --- /dev/null +++ b/dali-toolkit/internal/controls/selectors/rotating-selector-impl.h @@ -0,0 +1,186 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CUSTOM_CHECK_ACTOR_H__ +#define __DALI_TOOLKIT_INTERNAL_CUSTOM_CHECK_ACTOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class RotatingSelector; + + +/** + * RotatingSelector is a custom control for text aligning and multiline support + */ +class RotatingSelector : public ControlImpl +{ +public: + + /** + * Create a new RotatingSelector. + * @return A smart-pointer to the newly allocated RotatingSelector. + */ + static Dali::Toolkit::RotatingSelector New(Actor& unSelectedActor, Actor& selectedActor); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::SetSelected() + */ + void SetSelected( bool toggle ); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::IsSelected() + */ + bool IsSelected() const {return mSelected;} + + /** + * @copydoc Dali::Toolkit::RotatingSelector::SetSelectedActor() + */ + void SetSelectedActor( Actor& selectedActor ); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::GetSelectedActor() + */ + Actor GetSelectedActor(); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::SetUnSelectedActor() + */ + void SetUnSelectedActor( Actor& unSelectedActor ); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::GetUnSelectedActor() + */ + Actor GetUnSelectedActor(); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::SetSelectable() + */ + void SetSelectable( bool selectable ); + + /** + * @copydoc Dali::Toolkit::RotatingSelector::IsSelectable() + */ + bool IsSelectable()const {return mSelectable;} + + /** + * @copydoc Dali::Toolkit::RotatingSelector::SelectedSignal() + */ + Toolkit::RotatingSelector::SelectedSignalV2& SelectedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnTouchEvent(const TouchEvent& event) + */ + virtual bool OnTouchEvent( const TouchEvent& event ); + +protected: + + /** + * Construct a new RotatingSelector. + */ + RotatingSelector(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~RotatingSelector(); + +private: + + // Undefined + RotatingSelector(const RotatingSelector&); + + // Undefined + RotatingSelector& operator=(const RotatingSelector& rhs); + + void ToggleAndAnimateSelection(); + void AnimationCompleted( Animation& animation ); + + +private: + + Actor mContainer; + bool mSelected; + bool mSelectable; + + Actor mUnSelectedActor; + Actor mSelectedActor; + bool mPressed; + + Animation mRotateAnimation; + + bool mIsAnimating; + + Toolkit::RotatingSelector::SelectedSignalV2 mCheckedSignalV2; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::RotatingSelector& GetImpl(Toolkit::RotatingSelector& RotatingSelector) +{ + DALI_ASSERT_ALWAYS(RotatingSelector); + + Dali::RefObject& handle = RotatingSelector.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::RotatingSelector& GetImpl(const Toolkit::RotatingSelector& RotatingSelector) +{ + DALI_ASSERT_ALWAYS(RotatingSelector); + + const Dali::RefObject& handle = RotatingSelector.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ diff --git a/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.cpp b/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.cpp new file mode 100644 index 0000000..fbc2e1c --- /dev/null +++ b/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.cpp @@ -0,0 +1,390 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "shadow-view-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include + +// TODO: +// pixel format / size - set from JSON +// aspect ratio property needs to be able to be constrained also for cameras. (now do-able) +// default near clip value +// mChildrenRoot Add()/Remove() overloads - better solution + + +///////////////////////////////////////////////////////// +// IMPLEMENTATION NOTES + +// As the ShadowView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:- +// OnSetSize() does not get called when ShadowView object size is modified using a Constraint. +// OnSizeAnimation() only gets called once per AnimateTo/By() and if an Animation has N such calls then only the final one will end up being used. Therefore we can't use +// OnSizeAnimation() to alter render target sizes. +// To get around the above problems, we use fixed sized render targets, from the last SetSize() call (which calls OnSetSize()), then we adjust the internal cameras / actors +// to take account of the changed ShadowView object size, projecting to the unchanged render target sizes. This is done relative to the fixed render target / actor sizes +// by using constraints relative to the ShadowView actor size. + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +using namespace Dali; + +BaseHandle Create() +{ + return Toolkit::ShadowView::New(); +} + +TypeRegistration mType( typeid(Toolkit::ShadowView), typeid(Toolkit::Control), Create ); + + +const float BLUR_STRENGTH_DEFAULT = 1.0f; + +const Vector3 DEFAULT_LIGHT_POSITION(300.0f, 250.0f, 600.0f); +const float DEFAULT_FIELD_OF_VIEW_RADIANS = Math::PI / 4.0f; // 45 degrees + +const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.8f); + +const std::string SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME( "uLightCameraProjectionMatrix" ); +const std::string SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME( "uLightCameraViewMatrix" ); +const std::string SHADER_SHADOW_COLOR_PROPERTY_NAME( "uShadowColor" ); + +const std::string BLUR_STRENGTH_PROPERTY_NAME( "BlurStrengthProperty" ); +const std::string SHADOW_COLOR_PROPERTY_NAME( "ShadowColorProperty" ); + +const char* const RENDER_SHADOW_VERTEX_SOURCE = + " uniform mediump mat4 uLightCameraProjectionMatrix;\n" + " uniform mediump mat4 uLightCameraViewMatrix;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = uProjection * uModelView * vec4(aPosition,1.0);\n" + " vec4 textureCoords = uLightCameraProjectionMatrix * uLightCameraViewMatrix * uModelMatrix * vec4(aPosition,1.0);\n" + " vTexCoord = 0.5 + 0.5 * (textureCoords.xy/textureCoords.w);\n" + "}\n"; + +const char* const RENDER_SHADOW_FRAGMENT_SOURCE = + "uniform lowp vec4 uShadowColor;\n" + "void main()\n" + "{\n" + " lowp float alpha;\n" + " alpha = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y)).a;\n" + " gl_FragColor = vec4(uShadowColor.rgb, uShadowColor.a * alpha);\n" + "}\n"; + +// TODO: Add this to dali-core constraints.h +/** + * EqualToConstraintMatrix + * + * f(current, property) = property + */ +struct EqualToConstraintMatrix +{ + EqualToConstraintMatrix(){} + + Dali::Matrix operator()(const Dali::Matrix& current, const PropertyInput& property) {return property.GetMatrix();} +}; + +} // namespace + +ShadowView::ShadowView( float downsampleWidthScale, float downsampleHeightScale ) +: ControlImpl( false ), // doesn't require touch events + mChildrenRoot(Actor::New()), + mCachedShadowColor(DEFAULT_SHADOW_COLOR), + mCachedBackgroundColor(DEFAULT_SHADOW_COLOR.r, DEFAULT_SHADOW_COLOR.g, DEFAULT_SHADOW_COLOR.b, 0.0f), + mBlurStrengthPropertyIndex(Property::INVALID_INDEX), + mShadowColorPropertyIndex(Property::INVALID_INDEX), + mDownsampleWidthScale(downsampleWidthScale), + mDownsampleHeightScale(downsampleHeightScale) +{ +} + +ShadowView::~ShadowView() +{ +} + +Toolkit::ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale) +{ + ShadowView* impl = new ShadowView(downsampleWidthScale, downsampleHeightScale); + + Dali::Toolkit::ShadowView handle = Dali::Toolkit::ShadowView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +///////////////////////////////////////////////////////////// +// for creating a subtree for all user added child actors. +// TODO: overloading Actor::Add()/Remove() not nice since breaks polymorphism. Need another method to pass ownership of added child actors to our internal actor root. +void ShadowView::Add(Actor child) +{ + mChildrenRoot.Add(child); +} + +void ShadowView::Remove(Actor child) +{ + mChildrenRoot.Remove(child); +} + +void ShadowView::SetShadowPlane(Actor shadowPlane) +{ + mShadowPlaneBg = shadowPlane; + + mShadowPlane = ImageActor::New(); + mShadowPlane.SetParentOrigin(ParentOrigin::CENTER); + mShadowPlane.SetAnchorPoint(AnchorPoint::CENTER); + + mShadowPlane.SetImage(mOutputImage); + mShadowPlane.SetShaderEffect(mShadowRenderShader); + + // Rather than parent the shadow plane drawable and have constraints to move it to the same + // position, instead parent the shadow plane drawable on the shadow plane passed in. + mShadowPlaneBg.Add(mShadowPlane); + mShadowPlane.SetParentOrigin(ParentOrigin::CENTER); + mShadowPlane.SetZ(1.0f); + + ConstrainCamera(); + + mShadowPlane.ApplyConstraint( Constraint::New( Actor::SIZE, Source( mShadowPlaneBg, Actor::SIZE ), EqualToConstraint() ) ); + + mBlurRootActor.ApplyConstraint( Constraint::New( Actor::SIZE, Source( mShadowPlane, Actor::SIZE ), EqualToConstraint() ) ); +} + +void ShadowView::SetPointLight(Actor pointLight) +{ + mPointLight = pointLight; + + ConstrainCamera(); +} + +void ShadowView::SetPointLightFieldOfView(float fieldOfView) +{ + mCameraActor.SetFieldOfView(fieldOfView); +} + +void ShadowView::SetShadowColor(Vector4 color) +{ + mCachedShadowColor = color; + mCachedBackgroundColor.r = color.r; + mCachedBackgroundColor.g = color.g; + mCachedBackgroundColor.b = color.b; + + Self().SetProperty( mShadowColorPropertyIndex, mCachedShadowColor ); + if(mRenderSceneTask) + { + mRenderSceneTask.SetClearColor( mCachedBackgroundColor ); + } +} + +void ShadowView::Activate() +{ + DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Activate()\n" ); + + // make sure resources are allocated and start the render tasks processing + CreateRenderTasks(); +} + +void ShadowView::Deactivate() +{ + DALI_ASSERT_ALWAYS( Self().OnStage() && "ShadowView should be on stage before calling Deactivate()\n" ) + + // stop render tasks processing + // Note: render target resources are automatically freed since we set the Image::Unused flag + RemoveRenderTasks(); +} + +/////////////////////////////////////////////////////////// +// +// Private methods +// + +void ShadowView::OnInitialize() +{ + // root actor to parent all user added actors. Used as source actor for shadow render task. + mChildrenRoot.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mChildrenRoot.ApplyConstraint(Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() )); + + Vector2 stageSize = Stage::GetCurrent().GetSize(); + mCameraActor = CameraActor::New(stageSize); + mCameraActor.SetParentOrigin( ParentOrigin::CENTER ); + + // Target is constrained to point at the shadow plane origin + mCameraActor.SetNearClippingPlane( 1.0f ); + mCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Camera orientation constrained to point at shadow plane world position + mCameraActor.SetInvertYAxis(false); + mCameraActor.SetPosition(DEFAULT_LIGHT_POSITION); + + mShadowRenderShader = ShaderEffect::New( RENDER_SHADOW_VERTEX_SOURCE, RENDER_SHADOW_FRAGMENT_SOURCE, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING )); + + // Create render targets needed for rendering from light's point of view + mSceneFromLightRenderTarget = FrameBufferImage::New( stageSize.width, stageSize.height, Pixel::RGBA8888 ); + + mOutputImage = FrameBufferImage::New( stageSize.width * 0.5f, stageSize.height * 0.5f, Pixel::RGBA8888 ); + + ////////////////////////////////////////////////////// + // Connect to actor tree + + Self().Add( mChildrenRoot ); + Stage::GetCurrent().Add( mCameraActor ); + + mBlurFilter.SetRefreshOnDemand(false); + mBlurFilter.SetInputImage(mSceneFromLightRenderTarget); + mBlurFilter.SetOutputImage(mOutputImage); + mBlurFilter.SetSize(stageSize * 0.5f); + mBlurFilter.SetPixelFormat(Pixel::RGBA8888); + + mBlurRootActor = Actor::New(); + + // Turn off inheritance to ensure filter renders properly + mBlurRootActor.SetPositionInheritanceMode(USE_PARENT_POSITION); + mBlurRootActor.SetInheritRotation(false); + mBlurRootActor.SetInheritScale(false); + mBlurRootActor.SetColorMode(USE_OWN_COLOR); + + Self().Add(mBlurRootActor); + + mBlurFilter.SetRootActor(mBlurRootActor); + mBlurFilter.SetBackgroundColor(Vector4::ZERO); + + SetShaderConstants(); +} + +void ShadowView::OnSizeSet(const Vector3& targetSize) +{ +} + +void ShadowView::OnStageConnection() +{ + // TODO: can't call this here, since SetImage() calls fail to connect images to stage, since parent chain not fully on stage yet + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Activate(); +} + +void ShadowView::OnStageDisconnection() +{ + // TODO: can't call this here, since SetImage() calls fails similarly to above + // Need to fix the stage connection so this callback can be used arbitrarily. At that point we can simplify the API by removing the need for Activate() / Deactivate() + //Deactivate(); +} + +void ShadowView::ConstrainCamera() +{ + if( mPointLight && mShadowPlane ) + { + // Constrain camera to look directly at center of shadow plane. (mPointLight position + // is under control of application, can't use transform inheritance) + + Constraint cameraOrientationConstraint = + Constraint::New ( Actor::ROTATION, + Source( mShadowPlane, Actor::WORLD_POSITION ), + Source( mPointLight, Actor::WORLD_POSITION ), + Source( mShadowPlane, Actor::WORLD_ROTATION ), + &LookAt ); + + mCameraActor.ApplyConstraint( cameraOrientationConstraint ); + + Constraint pointLightPositionConstraint = Constraint::New( Actor::POSITION, Source( mPointLight, Actor::WORLD_POSITION ), EqualToConstraint() ); + + mCameraActor.ApplyConstraint( pointLightPositionConstraint ); + } +} + +void ShadowView::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + // We want the first task to render the scene from the light + mRenderSceneTask = taskList.CreateTask(); + + mRenderSceneTask.SetCameraActor( mCameraActor ); + mRenderSceneTask.SetSourceActor( mChildrenRoot ); + mRenderSceneTask.SetTargetFrameBuffer( mSceneFromLightRenderTarget ); + mRenderSceneTask.SetInputEnabled( false ); + mRenderSceneTask.SetClearEnabled( true ); + + // background color for render task should be the shadow color, but with alpha 0 + // we don't want to blend the edges of the content with a BLACK at alpha 0, but + // the same shadow color at alpha 0. + mRenderSceneTask.SetClearColor( mCachedBackgroundColor ); + + mBlurFilter.Enable(); +} + +void ShadowView::RemoveRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + taskList.RemoveTask(mRenderSceneTask); + mRenderSceneTask.Reset(); + + mBlurFilter.Disable(); +} + +void ShadowView::SetShaderConstants() +{ + CustomActor self = Self(); + + mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME, Matrix::IDENTITY ); + mShadowRenderShader.SetUniform( SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME, Matrix::IDENTITY ); + mShadowRenderShader.SetUniform( SHADER_SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor ); + + Property::Index lightCameraProjectionMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_PROJECTION_MATRIX_PROPERTY_NAME); + Property::Index lightCameraViewMatrixPropertyIndex = mShadowRenderShader.GetPropertyIndex(SHADER_LIGHT_CAMERA_VIEW_MATRIX_PROPERTY_NAME); + + Constraint projectionMatrixConstraint = Constraint::New( lightCameraProjectionMatrixPropertyIndex, Source( mCameraActor, CameraActor::PROJECTION_MATRIX ), EqualToConstraintMatrix()); + Constraint viewMatrixConstraint = Constraint::New( lightCameraViewMatrixPropertyIndex, Source( mCameraActor, CameraActor::VIEW_MATRIX ), EqualToConstraintMatrix()); + + mShadowRenderShader.ApplyConstraint(projectionMatrixConstraint); + mShadowRenderShader.ApplyConstraint(viewMatrixConstraint); + + // Register a property that the user can use to control the blur in the internal object + mBlurStrengthPropertyIndex = self.RegisterProperty(BLUR_STRENGTH_PROPERTY_NAME, BLUR_STRENGTH_DEFAULT); + mBlurFilter.GetHandleForAnimateBlurStrength().ApplyConstraint( Constraint::New( mBlurFilter.GetBlurStrengthPropertyIndex() , + Source( self, mBlurStrengthPropertyIndex), + EqualToConstraint()) ); + + // Register a property that the user can use to control the color of the shadow. + Property::Index index = mShadowRenderShader.GetPropertyIndex(SHADER_SHADOW_COLOR_PROPERTY_NAME); + mShadowColorPropertyIndex = self.RegisterProperty(SHADOW_COLOR_PROPERTY_NAME, mCachedShadowColor); + + mShadowRenderShader.ApplyConstraint(Constraint::New( index, Source( self, mShadowColorPropertyIndex ), EqualToConstraint()) ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h b/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h new file mode 100644 index 0000000..aa10428 --- /dev/null +++ b/dali-toolkit/internal/controls/shadow-view/shadow-view-impl.h @@ -0,0 +1,199 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SHADOW_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_SHADOW_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class ShadowView; + +namespace Internal +{ + +/** + * ShadowView implementation class + */ +class ShadowView : public ControlImpl +{ +public: + + /** + * @copydoc Dali::Toolkit::ShadowView::ShadowView + */ + ShadowView(); + + /** + * @copydoc Dali::Toolkit::ShadowView::ShadowView + */ + ShadowView(float downsampleWidthScale, float downsampleHeightScale); + + /** + * @copydoc Dali::Toolkit::ShadowView::~ShadowView + */ + virtual ~ShadowView(); + + /** + * @copydoc Dali::Toolkit::ShadowView::New(float downsampleWidthScale, float downsampleHeightScale) + */ + static Dali::Toolkit::ShadowView New(float downsampleWidthScale, float downsampleHeightScale); + + /** + * @copydoc Dali::Toolkit::ShadowView::Add(Actor child) + */ + void Add(Actor child); + + /** + * @copydoc Dali::Toolkit::ShadowView::Remove(Actor child) + */ + void Remove(Actor child); + + /** + * @copydoc Dali::Toolkit::ShadowView::SetShadowPlane(Actor shadowPlane) + */ + void SetShadowPlane(Actor shadowPlane); + + /** + * @copydoc Dali::Toolkit::ShadowView::SetPointLight(Actor pointLight) + */ + void SetPointLight(Actor pointLight); + + /** + * @copydoc Dali::Toolkit::ShadowView::SetPointLightFieldOfView(float fieldOfView) + */ + void SetPointLightFieldOfView(float fieldOfView); + + /** + * @copydoc Dali::Toolkit::ShadowView::SetShadowColor(Vector4 color) + */ + void SetShadowColor(Vector4 color); + + /** + * @copydoc Dali::Toolkit::ShadowView::Activate() + */ + void Activate(); + + /** + * @copydoc Dali::Toolkit::ShadowView::Deactivate() + */ + void Deactivate(); + + /** + * @copydoc Dali::Toolkit::ShadowView::GetBlurStrengthPropertyIndex() + */ + Property::Index GetBlurStrengthPropertyIndex() const {return mBlurStrengthPropertyIndex;} + + /** + * @copydoc Dali::Toolkit::ShadowView::GetShadowColorPropertyIndex() + */ + Property::Index GetShadowColorPropertyIndex() const {return mShadowColorPropertyIndex;} + + void SetShaderConstants(); + +private: + + virtual void OnInitialize(); + virtual void OnSizeSet(const Vector3& targetSize); + virtual void OnStageConnection(); + virtual void OnStageDisconnection(); + + /** + * Constrain the camera actor to the position of the point light, pointing + * at the center of the shadow plane. + */ + void ConstrainCamera(); + + void CreateRenderTasks(); + void RemoveRenderTasks(); + void CreateBlurFilter(); + +private: + ImageActor mShadowPlane; // Shadow renders into this actor + Actor mShadowPlaneBg; // mShadowPlane renders directly in front of this actor + Actor mPointLight; // Shadow is cast from this point light + + ///////////////////////////////////////////////////////////// + FrameBufferImage mSceneFromLightRenderTarget; // for rendering normal scene seen from light to texture instead of the screen + + FrameBufferImage mOutputImage; + + Actor mChildrenRoot; // Subtree for all user added child actors that should be rendered normally + Actor mBlurRootActor; // Root actor for blur filter processing + ImageActor mShadowPlaneDrawable; // Positioned on top of mShadowPlane for drawing shadow image + RenderTask mRenderSceneTask; + + CameraActor mCameraActor; // Constrained to same position as mPointLight and pointing at mShadowPlane + + ShaderEffect mShadowRenderShader; + BlurTwoPassFilter mBlurFilter; + + Vector4 mCachedShadowColor; ///< Cached Shadow color. + Vector4 mCachedBackgroundColor; ///< Cached Shadow background color (same as shadow color but with alpha at 0.0) + + ///////////////////////////////////////////////////////////// + // Properties that can be animated + Property::Index mBlurStrengthPropertyIndex; + Property::Index mShadowColorPropertyIndex; + float mDownsampleWidthScale; + float mDownsampleHeightScale; + +private: + + // Undefined copy constructor. + ShadowView( const ShadowView& ); + + // Undefined assignment operator. + ShadowView& operator=( const ShadowView& ); +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods +inline Toolkit::Internal::ShadowView& GetImpl( Toolkit::ShadowView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +inline const Toolkit::Internal::ShadowView& GetImpl( const Toolkit::ShadowView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + const Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SHADOW_VIEW_H__ diff --git a/dali-toolkit/internal/controls/slider/slider-impl.cpp b/dali-toolkit/internal/controls/slider/slider-impl.cpp new file mode 100755 index 0000000..6462e0c --- /dev/null +++ b/dali-toolkit/internal/controls/slider/slider-impl.cpp @@ -0,0 +1,1081 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +#include + +using namespace Dali; +using namespace std; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +const float BACKING_Z = -0.1f; +const float PROGRESS_Z = 0.1f; +const float HANDLE_Z = 1.0f; +const float VALUE_TEXT_INCREMENT = 0.01f; +const float HANDLE_VALUE_DISPLAY_TEXT_Z = HANDLE_Z + VALUE_TEXT_INCREMENT; +const float VALUE_DISPLAY_TEXT_Z = VALUE_TEXT_INCREMENT + VALUE_TEXT_INCREMENT; // Put above HANDLE_VALUE_DISPLAY_TEXT_Z (parented to handle) + +const float MARK_SNAP_TOLERANCE = 0.05f; // 5% of slider width + +const int VALUE_VIEW_SHOW_DURATION = 1000; // millisec +const int VALUE_VIEW_SHOW_DURATION_LONG = 2000; // millisec + +const float VALUE_VERTICAL_OFFSET = 48.0f; + +const float DEFAULT_WIDTH = 0.0f; +const float DEFAULT_HEIGHT = 27.0f; +const float DEFAULT_HIT_HEIGHT = 72.0f; +const float DEFAULT_HANDLE_HEIGHT = DEFAULT_HIT_HEIGHT; + +const char* SKINNED_BACKING_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin.png"; +const char* SKINNED_HANDLE_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin-handle.png";; +const char* SKINNED_PROGRESS_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin-progress.png"; +const char* SKINNED_POPUP_IMAGE_NAME = DALI_IMAGE_DIR "slider-popup.png"; +const char* SKINNED_POPUP_ARROW_IMAGE_NAME = DALI_IMAGE_DIR "slider-popup-arrow.png"; + +const Vector4 SKINNED_BACKING_SCALE9_BORDER( 12.0f, 0.0f, 12.0f, 0.0f ); +const Vector4 SKINNED_PROGRESS_SCALE9_BORDER( 14.0f, 0.0f, 0.0f, 0.0f ); +const Vector4 SKINNED_POPUP_SCALE9_BORDER( 10.0f, 10.0f, 10.0f, 10.0f ); + +const Vector2 DEFAULT_HIT_REGION( DEFAULT_WIDTH, DEFAULT_HIT_HEIGHT ); +const Vector2 DEFAULT_BACKING_REGION( DEFAULT_WIDTH, DEFAULT_HEIGHT ); +const Vector2 DEFAULT_HANDLE_REGION( DEFAULT_HANDLE_HEIGHT, DEFAULT_HANDLE_HEIGHT ); + +const Vector4 DEFAULT_DISABLE_COLOR( 0.5f, 0.5f, 0.5f, 1.0f ); +const Vector4 DEFAULT_POPUP_TEXT_COLOR( 0.5f, 0.5f, 0.5f, 1.0f ); + +const float VALUE_POPUP_MARGIN = 10.0f; +const float VALUE_POPUP_HEIGHT = 81.0f; +const float VALUE_POPUP_MIN_WIDTH = 54.0f; +const Vector2 VALUE_POPUP_ARROW_SIZE( 18.0f, 18.0f ); + +const float DEFAULT_LOWER_BOUND = 0.0f; +const float DEFAULT_UPPER_BOUND = 1.0f; +const float DEFAULT_VALUE = 0.0f; +const int DEFAULT_VALUE_PRECISION = 0; +const bool DEFAULT_SHOW_POPUP = false; +const bool DEFAULT_SHOW_VALUE = true; +const bool DEFAULT_ENABLED = true; + + +BaseHandle Create() +{ + return Dali::Toolkit::Slider::New(); +} + +TypeRegistration typeRegistration( typeid(Dali::Toolkit::Slider), typeid(Dali::Toolkit::Control), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::Slider::SIGNAL_VALUE_CHANGED, &Toolkit::Internal::Slider::DoConnectSignal ); +SignalConnectorType signalConnector2( typeRegistration, Toolkit::Slider::SIGNAL_MARK, &Toolkit::Internal::Slider::DoConnectSignal ); + +} // namespace + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Slider +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Dali::Toolkit::Slider Slider::New() +{ + // Create the implementation + SliderPtr slider( new Slider() ); + + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::Slider handle( *slider ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + slider->Initialize(); + + return handle; +} + +Slider::Slider() +: ControlImpl( true ), + mState( NORMAL ) +{ +} + +Slider::~Slider() +{ +} + +void Slider::OnInitialize() +{ + // Setup + CreateChildren(); + + // Properties + Actor self = Self(); + + // Register properties in a block so the properties are ready for the update functions + mPropertyHitRegion = self.RegisterProperty( Dali::Toolkit::Slider::HIT_REGION_PROPERTY_NAME, DEFAULT_HIT_REGION, Property::READ_WRITE ); + mPropertyBackingRegion = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_REGION_PROPERTY_NAME, DEFAULT_BACKING_REGION, Property::READ_WRITE ); + mPropertyHandleRegion = self.RegisterProperty( Dali::Toolkit::Slider::HANDLE_REGION_PROPERTY_NAME, DEFAULT_HANDLE_REGION, Property::READ_WRITE ); + + mPropertyBackingImageName = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_IMAGE_NAME_PROPERTY_NAME, SKINNED_BACKING_IMAGE_NAME, Property::READ_WRITE ); + mPropertyHandleImageName = self.RegisterProperty( Dali::Toolkit::Slider::HANDLE_IMAGE_NAME_PROPERTY_NAME, SKINNED_HANDLE_IMAGE_NAME, Property::READ_WRITE ); + + mPropertyProgressImageName = self.RegisterProperty( Dali::Toolkit::Slider::PROGRESS_IMAGE_NAME_PROPERTY_NAME, SKINNED_PROGRESS_IMAGE_NAME, Property::READ_WRITE ); + mPropertyPopupImageName = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_IMAGE_NAME_PROPERTY_NAME, SKINNED_POPUP_IMAGE_NAME, Property::READ_WRITE ); + mPropertyPopupArrowImageName = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_ARROW_IMAGE_NAME_PROPERTY_NAME, SKINNED_POPUP_ARROW_IMAGE_NAME, Property::READ_WRITE ); + + mPropertyBackingScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_SCALE9_BORDER_PROPERTY_NAME, SKINNED_BACKING_SCALE9_BORDER, Property::READ_WRITE ); + mPropertyProgressScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::PROGRESS_SCALE9_BORDER_PROPERTY_NAME, SKINNED_PROGRESS_SCALE9_BORDER, Property::READ_WRITE ); + mPropertyPopupScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_SCALE9_BORDER_PROPERTY_NAME, SKINNED_POPUP_SCALE9_BORDER, Property::READ_WRITE ); + + mPropertyDisableColor = self.RegisterProperty( Dali::Toolkit::Slider::DISABLE_COLOR_PROPERTY_NAME, DEFAULT_DISABLE_COLOR, Property::READ_WRITE ); + mPropertyPopupTextColor = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_TEXT_COLOR_PROPERTY_NAME, DEFAULT_POPUP_TEXT_COLOR, Property::READ_WRITE ); + + mPropertyValuePrecision = self.RegisterProperty( Dali::Toolkit::Slider::VALUE_PRECISION_PROPERTY_NAME, DEFAULT_VALUE_PRECISION, Property::READ_WRITE ); + mPropertyShowPopup = self.RegisterProperty( Dali::Toolkit::Slider::SHOW_POPUP_PROPERTY_NAME, DEFAULT_SHOW_POPUP, Property::READ_WRITE ); + mPropertyShowValue = self.RegisterProperty( Dali::Toolkit::Slider::SHOW_VALUE_PROPERTY_NAME, DEFAULT_SHOW_VALUE, Property::READ_WRITE ); + + mPropertyEnabled = self.RegisterProperty( Dali::Toolkit::Slider::ENABLED_PROPERTY_NAME, DEFAULT_ENABLED, Property::READ_WRITE ); + + mPropertyMarks = self.RegisterProperty( Dali::Toolkit::Slider::MARKS_PROPERTY_NAME, mMarks, Property::READ_WRITE ); + mPropertySnapToMarks = self.RegisterProperty( Dali::Toolkit::Slider::SNAP_TO_MARKS_PROPERTY_NAME, false, Property::READ_WRITE ); + mPropertyMarkTolerance = self.RegisterProperty( Dali::Toolkit::Slider::MARK_TOLERANCE_PROPERTY_NAME, MARK_SNAP_TOLERANCE, Property::READ_WRITE ); + + mPropertyLowerBound = self.RegisterProperty( Dali::Toolkit::Slider::LOWER_BOUND_PROPERTY_NAME, DEFAULT_LOWER_BOUND, Property::READ_WRITE ); + mPropertyUpperBound = self.RegisterProperty( Dali::Toolkit::Slider::UPPER_BOUND_PROPERTY_NAME, DEFAULT_UPPER_BOUND, Property::READ_WRITE ); + mPropertyValue = self.RegisterProperty( Dali::Toolkit::Slider::VALUE_PROPERTY_NAME, DEFAULT_VALUE, Property::READ_WRITE ); + + ResizeHitRegion( DEFAULT_HIT_REGION ); + SetBackingRegion( DEFAULT_BACKING_REGION ); + UpdateHandleRegion( DEFAULT_HANDLE_REGION ); + CreateBackingImage( SKINNED_BACKING_IMAGE_NAME ); + CreateHandleImage( SKINNED_HANDLE_IMAGE_NAME ); + CreateProgressImage( SKINNED_PROGRESS_IMAGE_NAME ); + CreatePopupImage( SKINNED_POPUP_IMAGE_NAME ); + CreatePopupArrowImage( SKINNED_POPUP_ARROW_IMAGE_NAME ); + SetBackingScale9( SKINNED_BACKING_SCALE9_BORDER ); + SetProgressScale9( SKINNED_PROGRESS_SCALE9_BORDER ); + SetPopupScale9( SKINNED_POPUP_SCALE9_BORDER ); + UpdatePopupTextColor( DEFAULT_POPUP_TEXT_COLOR ); + ShowPopup( DEFAULT_SHOW_POPUP ); + ShowValue( DEFAULT_SHOW_VALUE ); + SetEnabled( DEFAULT_ENABLED ); + UpdateLowerBound( DEFAULT_LOWER_BOUND ); + UpdateUpperBound( DEFAULT_UPPER_BOUND ); + UpdateSkin(); + DisplayValue( DEFAULT_VALUE, false ); // Run this last to display the correct value + + // Size the Slider actor to a default + self.SetSize( DEFAULT_HIT_REGION.x, DEFAULT_HIT_REGION.y ); +} + +void Slider::OnControlSizeSet( const Vector3& size ) +{ + // Factor in handle overshoot into size of backing + SetHitRegion( Vector2( size.x, GetHitRegion().y ) ); + SetBackingRegion( Vector2( size.x - GetHandleRegion().x, GetBackingRegion().y ) ); +} + +bool Slider::OnTouchEvent(Actor actor, const TouchEvent& event) +{ + if( mState != DISABLED ) + { + TouchPoint::State touchState = event.GetPoint(0).state; + + if( touchState == TouchPoint::Down ) + { + mState = PRESSED; + + float percentage = MapPercentage( event.GetPoint(0).local ); + float value = MapBounds( ( GetSnapToMarks() ) ? SnapToMark( percentage ) : MarkFilter( percentage ), GetLowerBound(), GetUpperBound() ); + SetValue( value ); + DisplayPopup( value ); + } + } + + return true; +} + +void Slider::OnPan( Actor actor, PanGesture gesture ) +{ + // gesture.position is in local actor coordinates + if( mState != DISABLED ) + { + switch( gesture.state ) + { + case Gesture::Continuing: + { + if( mState == PRESSED ) + { + float value = MapBounds( MarkFilter ( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() ); + SetValue( value ); + DisplayPopup( value ); + } + break; + } + case Gesture::Finished: + { + if( mState == PRESSED && GetSnapToMarks() ) + { + float value = MapBounds( SnapToMark( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() ); + SetValue( value ); + DisplayPopup( value ); + } + + mState = NORMAL; + break; + } + default: + { + break; + } + } + } +} + +float Slider::HitSpaceToDomain( float x ) +{ + float halfRegionWidth = GetHitRegion().x * 0.5f; + float halfDomainWidth = ( mDomain.to.x - mDomain.from.x ) * 0.5f; + float endDiff = halfRegionWidth - halfDomainWidth; + + return x - endDiff; +} + +float Slider::MapPercentage( const Vector2& point ) +{ + return Clamp( ( HitSpaceToDomain( point.x ) - mDomain.from.x ) / ( mDomain.to.x - mDomain.from.x ), 0.0f, 1.0f ); +} + +float Slider::MapValuePercentage( float value ) +{ + return ( value - GetLowerBound() ) / ( GetUpperBound() - GetLowerBound() ); +} + +float Slider::MapBounds( float percent, float lowerBound, float upperBound ) +{ + return lowerBound + percent * ( upperBound - lowerBound ); +} + +Slider::Domain Slider::CalcDomain( const Vector2& currentSize ) +{ + return Domain( Vector2( 0.0f, 0.0f ), currentSize ); +} + +void Slider::DisplayValue( float value, bool raiseSignals ) +{ + float clampledValue = Clamp( value, GetLowerBound(), GetUpperBound() ); + + float percent = MapValuePercentage( clampledValue ); + + float x = mDomain.from.x + percent * ( mDomain.to.x - mDomain.from.x ); + + mHandle.SetPosition( x, 0.0f, HANDLE_Z ); + + // Progress bar + if( mProgress ) + { + if( clampledValue > 0.0f ) + { + mProgress.SetVisible( true ); // Deliberately set this in case multiple SetValues are fired at once + mProgress.SetSize( x, GetBackingRegion().y ); + } + else + { + mProgress.SetVisible( false ); + } + } + + // Signals + if( raiseSignals ) + { + Toolkit::Slider self = Toolkit::Slider::DownCast( Self() ); + mValueChangedSignal.Emit( self, clampledValue ); + + int markIndex; + if( MarkReached( percent, markIndex ) ) + { + mMarkSignal.Emit( self, markIndex ); + } + } + + if( mHandleValueTextView ) + { + std::stringstream ss; + ss.precision( GetValuePrecision() ); + ss << fixed << clampledValue; + mHandleValueTextView.SetText( ss.str() ); + } +} + +void Slider::SetMarks( const MarkList& marks ) +{ + float value; + for( MarkList::const_iterator it = marks.begin(), itEnd = marks.end(); it != itEnd; ++it ) + { + const Property::Value& propertyValue = *it; + propertyValue.Get( value ); + + mMarks.push_back( value ); + } +} + +const Slider::MarkList& Slider::GetMarks() const +{ + return mMarks; +} + +bool Slider::GetSnapToMarks() const +{ + return Self().GetProperty( mPropertySnapToMarks ); +} + +Actor Slider::CreateHitRegion() +{ + Actor hitRegion = Actor::New(); + hitRegion.SetParentOrigin( ParentOrigin::CENTER ); + hitRegion.SetAnchorPoint( AnchorPoint::CENTER ); + hitRegion.TouchedSignal().Connect( this, &Slider::OnTouchEvent ); + + return hitRegion; +} + +ImageActor Slider::CreateBacking() +{ + ImageActor backing = ImageActor::New(); + backing.SetParentOrigin( ParentOrigin::CENTER ); + backing.SetAnchorPoint( AnchorPoint::CENTER ); + backing.SetZ( BACKING_Z ); + + return backing; +} + +void Slider::CreateBackingImage( const std::string& imageName ) +{ + if( mBacking && imageName != String::EMPTY ) + { + Image image = Image::New( imageName ); + mBacking.SetImage( image ); + } +} + +void Slider::SetBackingScale9( const Vector4& border ) +{ + if( mBacking ) + { + mBacking.SetStyle( ImageActor::STYLE_NINE_PATCH ); + mBacking.SetNinePatchBorder( border ); + } +} + +void Slider::SetBackingRegionSize( const Vector2& region ) +{ + if( mBacking ) + { + mBacking.SetSize( region ); + } +} + +ImageActor Slider::CreateProgress() +{ + ImageActor progress = ImageActor::New(); + progress.SetParentOrigin( ParentOrigin::CENTER_LEFT ); + progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + progress.SetZ( PROGRESS_Z ); + + return progress; +} + +void Slider::CreateProgressImage( const std::string& imageName ) +{ + if( mProgress && imageName != String::EMPTY ) + { + Image image = Image::New( imageName ); + mProgress.SetImage( image ); + } +} + +void Slider::CreatePopupImage( const std::string& imageName ) +{ + if( mPopup && imageName != String::EMPTY ) + { + Image image = Image::New( imageName ); + mPopup.SetImage( image ); + } +} + +void Slider::CreatePopupArrowImage( const std::string& imageName ) +{ + if( mPopupArrow && imageName != String::EMPTY ) + { + Image image = Image::New( imageName ); + mPopupArrow.SetImage( image ); + } +} + +void Slider::SetProgressScale9( const Vector4& border ) +{ + if( mProgress ) + { + mProgress.SetStyle( ImageActor::STYLE_NINE_PATCH ); + mProgress.SetNinePatchBorder( border ); + } +} + +void Slider::SetPopupScale9( const Vector4& border ) +{ + if( mPopup ) + { + mPopup.SetStyle( ImageActor::STYLE_NINE_PATCH ); + mPopup.SetNinePatchBorder( border ); + } +} + +void Slider::ResizeProgressRegion( const Vector2& region ) +{ + if( mProgress ) + { + mProgress.SetSize( region ); + } +} + +ImageActor Slider::CreateHandle() +{ + ImageActor handle = ImageActor::New(); + handle.SetParentOrigin( ParentOrigin::CENTER_LEFT ); + handle.SetAnchorPoint( AnchorPoint::CENTER ); + handle.SetZ( HANDLE_Z ); + + return handle; +} + +ImageActor Slider::CreatePopupArrow() +{ + ImageActor arrow = ImageActor::New(); + arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + arrow.SetZ( HANDLE_Z ); + + return arrow; +} + +Toolkit::TextView Slider::CreatePopupText() +{ + Toolkit::TextView textView = Toolkit::TextView::New(); + textView.SetParentOrigin( ParentOrigin::CENTER ); + textView.SetAnchorPoint( AnchorPoint::CENTER ); + textView.SetSizePolicy( Control::Flexible, Control::Flexible ); + textView.SetZ( VALUE_DISPLAY_TEXT_Z ); + return textView; +} + +ImageActor Slider::CreatePopup() +{ + ImageActor popup = ImageActor::New(); + popup.SetParentOrigin( ParentOrigin::TOP_CENTER ); + popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + + mValueTextView = CreatePopupText(); + popup.Add( mValueTextView ); + + return popup; +} + +void Slider::CreateHandleImage( const std::string& imageName ) +{ + if( mHandle && imageName != String::EMPTY ) + { + Image image = Image::New( imageName ); + mHandle.SetImage( image ); + } +} + +void Slider::ResizeHandleRegion( const Vector2& region ) +{ + if( mHandle ) + { + mHandle.SetSize( region ); + } +} + +void Slider::CreateHandleValueDisplay() +{ + if( mHandle && !mHandleValueTextView ) + { + mHandleValueTextView = Toolkit::TextView::New(); + mHandleValueTextView.SetParentOrigin( ParentOrigin::CENTER ); + mHandleValueTextView.SetAnchorPoint( AnchorPoint::CENTER ); + mHandleValueTextView.SetSize( GetHandleRegion() ); + mHandleValueTextView.SetZ( HANDLE_VALUE_DISPLAY_TEXT_Z ); + mHandle.Add( mHandleValueTextView ); + } +} + +void Slider::DestroyHandleValueDisplay() +{ + mHandleValueTextView.Unparent(); + mHandleValueTextView.Reset(); +} + +void Slider::UpdatePopupTextColor( const Vector4& color ) +{ + if( mValueTextView ) + { + mValueTextView.SetColor( color ); + } +} + +Actor Slider::CreateValueDisplay() +{ + Actor popup = Actor::New(); + popup.SetParentOrigin( ParentOrigin::TOP_CENTER ); + popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + + mPopupArrow = CreatePopupArrow(); + popup.Add( mPopupArrow ); + + mPopup = CreatePopup(); + mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT ); + mPopupArrow.Add( mPopup ); + + return popup; +} + +Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal() +{ + return mValueChangedSignal; +} + +Toolkit::Slider::MarkSignalType& Slider::MarkSignal() +{ + return mMarkSignal; +} + +void Slider::UpdateSkin() +{ + switch( mState ) + { + case NORMAL: + { + mBacking.SetColor( Color::WHITE ); + mHandle.SetColor( Color::WHITE ); + mProgress.SetColor( Color::WHITE ); + break; + } + case DISABLED: + { + Vector4 disableColor = GetDisableColor(); + mBacking.SetColor( disableColor ); + mHandle.SetColor( disableColor ); + mProgress.SetColor( disableColor ); + break; + } + case PRESSED: + { + break; + } + case FOCUSED: + { + break; + } + } +} + +void Slider::CreateChildren() +{ + Actor self = Self(); + + // Hit region + mHitArea = CreateHitRegion(); + mPanDetector = PanGestureDetector::New(); + mPanDetector.Attach( mHitArea ); + mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan ); + self.Add( mHitArea ); + + // Background + mBacking = CreateBacking(); + self.Add( mBacking ); + + // Progress bar + mProgress = CreateProgress(); + mBacking.Add( mProgress ); + + // Handle + mHandle = CreateHandle(); + mBacking.Add( mHandle ); +} + +void Slider::ResizeHitRegion( const Vector2& size ) +{ + if( mHitArea ) + { + mHitArea.SetSize( size ); + } +} + +void Slider::AddPopup() +{ + if( !mValueDisplay ) + { + mValueDisplay = CreateValueDisplay(); + mValueDisplay.SetVisible( false ); + mHandle.Add( mValueDisplay ); + + Actor self = Self(); + CreatePopupImage( self.GetProperty( mPropertyPopupImageName ) ); + SetPopupScale9( GetPopupScale9Border() ); + CreatePopupArrowImage( self.GetProperty( mPropertyPopupArrowImageName ) ); + + mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION ); + mValueTimer.TickSignal().Connect( this, &Slider::HideValueView ); + } +} + +void Slider::RemovePopup() +{ + if( mValueDisplay ) + { + mPopup.Unparent(); + mPopup.Reset(); + + mPopupArrow.Unparent(); + mPopupArrow.Reset(); + + mValueDisplay.Unparent(); + mValueDisplay.Reset(); + + mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView ); + mValueTimer.Reset(); + } +} + + +float Slider::MarkFilter( float value ) +{ + const float MARK_TOLERANCE = GetMarkTolerance(); + + float mark; + for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it ) + { + const Property::Value& propertyValue = *it; + propertyValue.Get( mark ); + mark = MapValuePercentage( mark ); + + // If close to a mark, return the mark + if( fabsf( mark - value ) < MARK_TOLERANCE ) + { + return mark; + } + } + + return value; +} + +float Slider::SnapToMark( float value ) +{ + float closestMark = value; + float closestDist = std::numeric_limits::max(); + + float mark; + for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it ) + { + const Property::Value& propertyValue = *it; + propertyValue.Get( mark ); + mark = MapValuePercentage( mark ); + + float dist = fabsf( mark - value ); + if( dist < closestDist ) + { + closestDist = dist; + closestMark = mark; + } + } + + return closestMark; +} + +bool Slider::MarkReached( float value, int& outIndex ) +{ + const float MARK_TOLERANCE = GetMarkTolerance(); + + // Binary search + int head = 0, + tail = mMarks.size() - 1; + int current; + float mark; + + while( head <= tail ) + { + current = head + ( tail - head ) / 2; + + const Property::Value& propertyValue = mMarks[ current ]; + propertyValue.Get( mark ); + mark = MapValuePercentage( mark ); + + if( fabsf( mark - value ) < MARK_TOLERANCE ) + { + outIndex = current; + return true; + } + else + { + if( value < mark ) + { + tail = current - 1; + } + else + { + head = current + 1; + } + } + } + + return false; +} + +bool Slider::HideValueView() +{ + if( mValueDisplay ) + { + mValueDisplay.SetVisible( false ); + } + + return false; +} + +void Slider::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + if( index == mPropertyLowerBound ) + { + UpdateLowerBound( propertyValue.Get() ); + } + else if( index == mPropertyUpperBound ) + { + UpdateUpperBound( propertyValue.Get() ); + } + else if( index == mPropertyValue ) + { + DisplayValue( propertyValue.Get(), true ); + } + else if( index == mPropertyHitRegion ) + { + ResizeHitRegion( propertyValue.Get() ); + } + else if( index == mPropertyBackingRegion ) + { + ResizeBackingRegion( propertyValue.Get() ); + } + else if( index == mPropertyHandleRegion ) + { + UpdateHandleRegion( propertyValue.Get() ); + } + else if( index == mPropertyBackingImageName ) + { + CreateBackingImage( propertyValue.Get() ); + } + else if( index == mPropertyHandleImageName ) + { + CreateHandleImage( propertyValue.Get() ); + } + else if( index == mPropertyProgressImageName ) + { + CreateProgressImage( propertyValue.Get() ); + } + else if( index == mPropertyPopupImageName ) + { + CreatePopupImage( propertyValue.Get() ); + } + else if( index == mPropertyPopupArrowImageName ) + { + CreatePopupArrowImage( propertyValue.Get() ); + } + else if( index == mPropertyBackingScale9Border ) + { + SetBackingScale9( propertyValue.Get() ); + } + else if( index == mPropertyProgressScale9Border ) + { + SetProgressScale9( propertyValue.Get() ); + } + else if( index == mPropertyPopupScale9Border ) + { + SetPopupScale9( propertyValue.Get() ); + } + else if( index == mPropertyDisableColor ) + { + UpdateSkin(); + } + else if( index == mPropertyPopupTextColor ) + { + UpdatePopupTextColor( propertyValue.Get() ); + } + else if( index == mPropertyValuePrecision ) + { + DisplayValue( GetValue(), false ); + } + else if( index == mPropertyShowPopup ) + { + ShowPopup( propertyValue.Get() ); + } + else if( index == mPropertyShowValue ) + { + ShowValue( propertyValue.Get() ); + } + else if( index == mPropertyEnabled ) + { + SetEnabled( propertyValue.Get() ); + } + else if( index == mPropertyMarks ) + { + SetMarks( propertyValue.Get() ); + } + else if( index == mPropertySnapToMarks ) + { + // Nothing + } + else if( index == mPropertyMarkTolerance ) + { + // Nothing + } +} + +void Slider::UpdateLowerBound( float bound ) +{ + DisplayValue( GetValue(), false ); +} + +float Slider::GetLowerBound() const +{ + return Self().GetProperty( mPropertyLowerBound ); +} + +void Slider::UpdateUpperBound( float bound ) +{ + DisplayValue( GetValue(), false ); +} + +float Slider::GetUpperBound() const +{ + return Self().GetProperty( mPropertyUpperBound ); +} + +void Slider::SetValue( float value ) +{ + Self().SetProperty( mPropertyValue, value ); +} + +float Slider::GetValue() const +{ + return Self().GetProperty( mPropertyValue ); +} + +void Slider::SetHitRegion( const Vector2& region ) +{ + Self().SetProperty( mPropertyHitRegion, region ); +} + +Vector2 Slider::GetHitRegion() const +{ + return Self().GetProperty( mPropertyHitRegion ); +} + +void Slider::SetBackingRegion( const Vector2& region ) +{ + Self().SetProperty( mPropertyBackingRegion, region ); +} + +void Slider::ResizeBackingRegion( const Vector2& region ) +{ + SetBackingRegionSize( region ); + ResizeProgressRegion( Vector2( 0.0f, region.y ) ); + + mDomain = CalcDomain( region ); + + DisplayValue( GetValue(), false ); // Set the progress bar to correct width +} + +Vector2 Slider::GetBackingRegion() const +{ + return Self().GetProperty( mPropertyBackingRegion ); +} + +void Slider::UpdateHandleRegion( const Vector2& region ) +{ + ResizeHandleRegion( region ); + + Vector2 hitRegion = GetHitRegion(); + hitRegion.x += region.x; + SetHitRegion( hitRegion ); +} + +Vector2 Slider::GetHandleRegion() const +{ + return Self().GetProperty( mPropertyHandleRegion ); +} + +Vector4 Slider::GetBackingScale9Border() const +{ + return Self().GetProperty( mPropertyBackingScale9Border ); +} + +Vector4 Slider::GetPopupScale9Border() const +{ + return Self().GetProperty( mPropertyPopupScale9Border ); +} + +Vector4 Slider::GetDisableColor() const +{ + return Self().GetProperty( mPropertyDisableColor ); +} + +Vector4 Slider::GetPopupTextColor() const +{ + return Self().GetProperty( mPropertyPopupTextColor ); +} + +int Slider::GetValuePrecision() const +{ + return Self().GetProperty( mPropertyValuePrecision ); +} + +void Slider::ShowPopup( bool showPopup ) +{ + // Value display + if( showPopup ) + { + AddPopup(); + } + else + { + RemovePopup(); + } +} + +bool Slider::GetShowPopup() const +{ + return Self().GetProperty( mPropertyShowPopup ); +} + +void Slider::ShowValue( bool showValue ) +{ + if( showValue ) + { + CreateHandleValueDisplay(); + } + else + { + DestroyHandleValueDisplay(); + } +} + +bool Slider::GetShowValue() const +{ + return Self().GetProperty( mPropertyShowValue ); +} + +void Slider::SetEnabled( bool enabled ) +{ + if( enabled ) + { + mState = NORMAL; + } + else + { + mState = DISABLED; + } + + UpdateSkin(); +} + +bool Slider::IsEnabled() const +{ + return mState != DISABLED; +} + +float Slider::GetMarkTolerance() const +{ + return Self().GetProperty( mPropertyMarkTolerance ); +} + +// static class method to support script connecting signals + +bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected = true; + Toolkit::Slider slider = Toolkit::Slider::DownCast( handle ); + + if( signalName == Dali::Toolkit::Slider::SIGNAL_VALUE_CHANGED ) + { + slider.ValueChangedSignal().Connect( tracker, functor ); + } + else if( signalName == Dali::Toolkit::Slider::SIGNAL_MARK ) + { + slider.MarkSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +void Slider::DisplayPopup( float value ) +{ + // Value display + if( mValueTextView ) + { + std::stringstream ss; + ss.precision( GetValuePrecision() ); + ss << fixed << value; + mValueTextView.SetText( ss.str() ); + TextStyle style; + style.SetTextColor( GetPopupTextColor() ); + mValueTextView.SetStyleToCurrentText( style, TextStyle::COLOR); + + if( mValueDisplay ) + { + Font font = Font::New(); + float popupWidth = font.MeasureText( ss.str() ).x + VALUE_POPUP_MARGIN * 2.0f; + if( popupWidth < VALUE_POPUP_MIN_WIDTH ) + { + popupWidth = VALUE_POPUP_MIN_WIDTH; + } + + mPopup.SetSize( popupWidth, VALUE_POPUP_HEIGHT ); + mValueDisplay.SetVisible( true ); + + mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION ); + } + } +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/slider/slider-impl.h b/dali-toolkit/internal/controls/slider/slider-impl.h new file mode 100755 index 0000000..3f4b8c8 --- /dev/null +++ b/dali-toolkit/internal/controls/slider/slider-impl.h @@ -0,0 +1,728 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SLIDER_H__ +#define __DALI_TOOLKIT_INTERNAL_SLIDER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Button; + +namespace Internal +{ + +class Slider; + +typedef Dali::IntrusivePtr< Slider > SliderPtr; + +/** + * @copydoc Toolkit::Slider + */ +class Slider : public ControlImpl +{ +public: + + typedef Property::Array MarkList; + + /** + * Create a new Slider. + * + * @return A public handle to the newly allocated Slider. + */ + static Dali::Toolkit::Slider New(); + +public: + + // Properties + + /** + * Set marks from a list + * + * @param[in] marks The list of marks to set + */ + void SetMarks( const MarkList& marks ); + + /** + * Get the list of marks + * + * @return The marks list + */ + const MarkList& GetMarks() const; + + /** + * Set if should snap to marks or not + * + * @param[in] snap Flag to snap to marks or not + */ + void SetSnapToMarks( bool snap ); + + /** + * Return if snap to marks is set or not + * + * @return If snap to marks is set + */ + bool GetSnapToMarks() const; + + /** + * Set the value of the slider + * + * @param[in] value The value to set. Will be clamped to [lowerBound .. upperBound] + */ + void SetValue( float value ); + + /** + * Get the value of the slider + * + * @return The current value of the slider + */ + float GetValue() const; + + /** + * Set hit region + * + * @param[in] region The hit region + */ + void SetHitRegion( const Vector2& region ); + + /** + * Get hit region + * + * @return The hit region + */ + Vector2 GetHitRegion() const; + + /** + * Set backing region + * + * @param[in] region The backing region + */ + void SetBackingRegion( const Vector2& region ); + + /** + * Get backing region + * + * @return The backing region + */ + Vector2 GetBackingRegion() const; + + /** + * Get backing scale9 border + * + * @return The backing scale9 border + */ + Vector4 GetBackingScale9Border() const; + + /** + * Get popup scale9 border + * + * @return The popup scale9 border + */ + Vector4 GetPopupScale9Border() const; + + /** + * Get disable color + * + * @return The disable color + */ + Vector4 GetDisableColor() const; + + /** + * Get popup text color + * + * @return The popup text color + */ + Vector4 GetPopupTextColor() const; + + /** + * Get value precision + * + * @return The value precision + */ + int GetValuePrecision() const; + + /** + * Show the popup + * + * @param[in] showPopup The show popup flag + */ + void ShowPopup( bool showPopup ); + + /** + * Get show value in popup + * + * @return The show value flag + */ + bool GetShowPopup() const; + + /** + * Set show value on handle + * + * @param[in] showValue The show value flag + */ + void ShowValue( bool showValue ); + + /** + * Get show value on handle + * + * @return The show value flag + */ + bool GetShowValue() const; + + /** + * Set enabled + * + * param[in] enabled Set the enabled flag + */ + void SetEnabled( bool enabled ); + + /** + * Return if enabled or not + * + * @return If enabled + */ + bool IsEnabled() const; + + /** + * Return the mark tolerance + * + * @return The tolerance set for snapping to marks + */ + float GetMarkTolerance() const; + +public: + //Signals + + /** + * @copydoc Toolkit::Slider::ValueChangedSignal() + */ + Toolkit::Slider::ValueChangedSignalType& ValueChangedSignal(); + + /** + * @copydoc Toolkit::Slider::MarkSignal() + */ + Toolkit::Slider::MarkSignalType& MarkSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, + FunctorDelegate* functor ); + +protected: + + /** + * Construct a new Slider. + */ + Slider(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~Slider(); + + /** + * @copydoc Toolkit::ControlImpl::OnControlSizeSet( const Vector3& size ) + */ + virtual void OnControlSizeSet( const Vector3& size ); + +private: + + /** + * Domain is a from/to pair + */ + struct Domain + { + Vector2 from; + Vector2 to; + + Domain() + { + } + Domain( Vector2 fromVal, Vector2 toVal ) + : from( fromVal ), to( toVal ) + { + } + }; + + /** + * Slider states + */ + enum SliderState + { + NORMAL, + DISABLED, + PRESSED, + FOCUSED + }; + +private: + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * Hit region touch event + * + * @param[in] actor The actor the event is raised for + * @param[in] event The touch event info + * @return If the event is handled or not + */ + bool OnTouchEvent( Actor actor, const TouchEvent& event ); + + /** + * Pan gesture event + * + * @param[in] actor The actor the event is raised for + * @param[in] gesture The pan event info + */ + void OnPan( Actor actor, PanGesture gestur ); + + /** + * Map a position onto a domain and return the result as a percentage + * + * @param[in] point The point to map onto the domain + * @return The result as a percentage [0..1] + */ + float MapPercentage( const Vector2& point ); + + /** + * Map a value in the range to a percentage + * + * @param[in] point The value in range [lowerBound..upperBound] + * @return The result as a percentage [0..1] + */ + float MapValuePercentage( float value ); + + /** + * Convert a point in local hit space into domain space + * + * @param[in] x The x position to convert + * @return The x position in domain space + */ + float HitSpaceToDomain( float x ); + + /** + * Map a percentage onto the slider's bounds + * + * @param[in] percent The current value of slider in percent + * @param[in] lowerBound The lower bound to map onto + * @param[in] upperBound The upper bound to map onto + * @return The value of percent mapped from lowerBound to upperBound + */ + float MapBounds( float percent, float lowerBound, float upperBound ); + + /** + * Get the range of the valid values the slider handle can move between + * + * @param[in] currentSize The current size of the slider + * @return The range as a domain pair + */ + Domain CalcDomain( const Vector2& currentSize ); + + /** + * Create the hit region + * + * @return The hit region actor + */ + Actor CreateHitRegion(); + + /** + * Create the backing for the slider + * + * @return The backing actor + */ + ImageActor CreateBacking(); + + /** + * Create the progress backing for the slider + * + * @return The backing actor + */ + ImageActor CreateProgress(); + + /** + * Create the handle for the slider + * + * @return The created image handle + */ + ImageActor CreateHandle(); + + /** + * Create the popup arrow + * + * @return The created image handle + */ + ImageActor CreatePopupArrow(); + + /** + * Create the popup + * + * @return The created image handle + */ + ImageActor CreatePopup(); + + /** + * Create the textview for the popup + * + * @return The textview created for the popup + */ + Toolkit::TextView CreatePopupText(); + + /** + * Create the value display for the slider + * + * @return The created root actor of the display + */ + Actor CreateValueDisplay(); + + /** + * Set the skin based on the current state + */ + void UpdateSkin(); + + /** + * Create all the children + */ + void CreateChildren(); + + /** + * Resize the hit area + * + * @param[in] size The new size of the hit area + */ + void ResizeHitRegion( const Vector2& size ); + + /** + * Create value popup + */ + void AddPopup(); + + /** + * Remove value popup + */ + void RemovePopup(); + + /** + * Display the popup for a set duration with the given value + * + * @param[in] value The value to display in the popup + */ + void DisplayPopup( float value ); + + /** + * If there are marks present, filter the incoming percent based on snapping to any nearby marks + * + * @param[in] value The incoming value on the slider to filter + * @return The filtered percentage snapped to any nearby marks + */ + float MarkFilter( float value ); + + /** + * If there are marks present, snap the incoming percent to the nearest mark + * + * @param[in] value The incoming value on the slider to snap + * @return The filtered percentage snapped to the nearest mark + */ + float SnapToMark( float value ); + + /** + * Search for if a mark has been reached + * + * @param[in] value The value to search against + * @param[out] outIndex The index of the mark if found + * @return If a mark has been found to match percent + */ + bool MarkReached( float value, int& outIndex ); + + /** + * Handler for when the value view needs to be hidden + * + * @return If handled or not + */ + bool HideValueView(); + + /** + * Set value choosing whether to fire signals or not + * + * @paramp[in] value The value to set + * @param[in] raiseSignals Configure signals to be raised or not. + */ + void DisplayValue( float value, bool raiseSignals ); + + /** + * Create the image for the backing + * + * @param[in] imageName The name of the image to load and set + */ + void CreateBackingImage( const std::string& imageName ); + + /** + * Set the backing image to be a scale-9 image + * + * @param[in] border The scale-9 border to use + */ + void SetBackingScale9( const Vector4& border ); + + /** + * Resize the backing region + * + * @param[in] region The size of the region to set + */ + void ResizeBackingRegion( const Vector2& region ); + + /** + * Size the backing region + * + * @param[in] region The size of the region to set + */ + void SetBackingRegionSize( const Vector2& region ); + + /** + * Create the image for the progress bar + * + * @param[in] imageName The name of the image to load and set + */ + void CreateProgressImage( const std::string& imageName ); + + /** + * Create the image for the popup + * + * @param[in] imageName The name of the image to load and set + */ + void CreatePopupImage( const std::string& imageName ); + + /** + * Create the image for the popup arrow + * + * @param[in] imageName The name of the image to load and set + */ + void CreatePopupArrowImage( const std::string& imageName ); + + /** + * Set the progress image to be a scale-9 image + * + * @param[in] border The scale-9 border to use + */ + void SetProgressScale9( const Vector4& border ); + + /** + * Set the popup image to be a scale-9 image + * + * @param[in] border The scale-9 border to use + */ + void SetPopupScale9( const Vector4& border ); + + /** + * Set the size of the progress bar region + * + * @param[in] region The size of the region to set + */ + void ResizeProgressRegion( const Vector2& region ); + + /** + * Create the image for the handle + * + * @param[in] imageName The name of the image to load and set + */ + void CreateHandleImage( const std::string& imageName ); + + /** + * Set the size of the handle region + * + * @param[in] region The size of the region to set + */ + void ResizeHandleRegion( const Vector2& region ); + + /** + * Create and display the value on the handle + */ + void CreateHandleValueDisplay(); + + /** + * Remove and destroy the handle value display + */ + void DestroyHandleValueDisplay(); + + /** + * Update the color of the popup text + * + * @param[in] color The new color + */ + void UpdatePopupTextColor( const Vector4& color ); + + /** + * Set handle region + * + * @param[in] region The handle region + */ + void UpdateHandleRegion( const Vector2& region ); + + /** + * Get handle region + * + * @return The handle region + */ + Vector2 GetHandleRegion() const; + + /** + * Set the lower bound of the slider's value + * + * @param[in] bound The value to set for the lower bound + */ + void UpdateLowerBound( float bound ); + + /** + * Get the lower bound of the slider's value + * + * @return The lower bound value + */ + float GetLowerBound() const; + + /** + * Set the upper bound of the slider's value + * + * @param[in] bound The value to set for the upper bound + */ + void UpdateUpperBound( float bound ); + + /** + * Get the upper bound of the slider's value + * + * @return The upper bound value + */ + float GetUpperBound() const; + +private: + // From ControlImpl + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + virtual void OnPropertySet( Property::Index index, Property::Value propertyValue ); + +private: + + // Undefined + Slider( const Slider& ); + + // Undefined + Slider& operator=( const Slider& rhs ); + +private: + + Domain mDomain; ///< Current domain of the handle + + Actor mHitArea; ///< The input handler + ImageActor mBacking; ///< Backing image + ImageActor mHandle; ///< Slider handle + ImageActor mProgress; ///< Progress backing + Actor mValueDisplay; ///< Display of the value + ImageActor mPopup; ///< Popup backing + ImageActor mPopupArrow; ///< Popup arrow backing + + Toolkit::TextView mValueTextView; //< The text value in popup + Toolkit::TextView mHandleValueTextView; ///< The text value on handle + Vector2 mHandleLastTouchPoint; ///< The last touch point for the handle + Timer mValueTimer; ///< Timer used to hide value view + + Toolkit::Slider::ValueChangedSignalType mValueChangedSignal; ///< Signal emitted when the value is changed + Toolkit::Slider::MarkSignalType mMarkSignal; ///< Signal emitted when a mark is reached + + SliderState mState; ///< The state of the slider + + PanGestureDetector mPanDetector; ///< Hit region pan detector + + MarkList mMarks; ///< List of discreet marks + + // Properties + Property::Index mPropertyLowerBound; + Property::Index mPropertyUpperBound; + Property::Index mPropertyValue; + Property::Index mPropertyHitRegion; + Property::Index mPropertyBackingRegion; + Property::Index mPropertyHandleRegion; + + Property::Index mPropertyBackingImageName; + Property::Index mPropertyHandleImageName; + Property::Index mPropertyProgressImageName; + Property::Index mPropertyPopupImageName; + Property::Index mPropertyPopupArrowImageName; + + Property::Index mPropertyBackingScale9Border; + Property::Index mPropertyProgressScale9Border; + Property::Index mPropertyPopupScale9Border; + + Property::Index mPropertyDisableColor; + Property::Index mPropertyPopupTextColor; + + Property::Index mPropertyValuePrecision; + + Property::Index mPropertyShowPopup; + Property::Index mPropertyShowValue; + + Property::Index mPropertyEnabled; + + Property::Index mPropertyMarks; + Property::Index mPropertySnapToMarks; + Property::Index mPropertyMarkTolerance; + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::Slider& GetImpl( Toolkit::Slider& pub ) +{ + DALI_ASSERT_ALWAYS( pub ); + + Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast< Toolkit::Internal::Slider& >( handle ); +} + +inline const Toolkit::Internal::Slider& GetImpl( const Toolkit::Slider& pub ) +{ + DALI_ASSERT_ALWAYS( pub ); + + const Dali::RefObject& handle = pub.GetImplementation(); + + return static_cast< const Toolkit::Internal::Slider& >( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SLIDER_H__ diff --git a/dali-toolkit/internal/controls/style-change-processor.cpp b/dali-toolkit/internal/controls/style-change-processor.cpp new file mode 100644 index 0000000..4d0996c --- /dev/null +++ b/dali-toolkit/internal/controls/style-change-processor.cpp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "style-change-processor.h" + +#include + +#include "dali-toolkit/public-api/controls/control.h" +#include "dali-toolkit/public-api/controls/control-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +boost::thread_specific_ptr gThreadLocalStyleChangeProcessor; +} // unnamed namespace + +StyleChangeProcessor::~StyleChangeProcessor() +{ +} + +void StyleChangeProcessor::Register( ControlImpl* control ) +{ + // Only create a style change processor if we have not created one in the local thread storage. + if (!gThreadLocalStyleChangeProcessor.get()) + { + gThreadLocalStyleChangeProcessor.reset(new StyleChangeProcessor); + } + + gThreadLocalStyleChangeProcessor->Reference(); + + std::vector& controls( gThreadLocalStyleChangeProcessor->mControls ); + + // Store the Control raw pointer to allow traverse all off stage controls. + DALI_ASSERT_ALWAYS( ( std::find( controls.begin(), controls.end(), control ) == controls.end() ) && "StyleChangeProcessor::Register. The control has been registered twice." ); + + controls.push_back( control ); +} + +void StyleChangeProcessor::Unregister( ControlImpl* control ) +{ + if (gThreadLocalStyleChangeProcessor.get()) + { + std::vector& controls( gThreadLocalStyleChangeProcessor->mControls ); + + // Removes the control from the vector as is not needed to notify it about style changes. + std::vector::iterator it = std::find( controls.begin(), controls.end(), control ); + std::vector::iterator endIt = controls.end(); + DALI_ASSERT_ALWAYS( ( it != endIt ) && "StyleChangeProcessor::UnRegister. The control has not been registered in the StyleChangeProcessor." ); + + *it = *(endIt - 1); + controls.erase( endIt - 1 ); + + gThreadLocalStyleChangeProcessor->Unreference(); + } +} + +void StyleChangeProcessor::Reference() +{ + ++mCount; +} + +void StyleChangeProcessor::Unreference() +{ + if (--mCount == 0) + { + // If our count is 0, then we should reset the local storage which will call our destructor as well. + gThreadLocalStyleChangeProcessor.reset(); + } +} + +StyleChangeProcessor::StyleChangeProcessor() +: mCount(0) +{ + if ( Adaptor::IsAvailable() ) + { + StyleMonitor::Get().StyleChangeSignal().Connect(this, &StyleChangeProcessor::StyleChanged); + } +} + +void StyleChangeProcessor::StyleChanged(Dali::StyleMonitor styleMonitor, StyleChange styleChange) +{ + // Traverse all registered controls. + std::vector& controls( gThreadLocalStyleChangeProcessor->mControls ); + + for( std::vector::iterator it = controls.begin(), endIt = controls.end(); it != endIt; ++it ) + { + // Create a valid handle. + IntrusivePtr implementation( *it ); + + if (implementation) + { + implementation->OnStyleChange( styleChange ); + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/style-change-processor.h b/dali-toolkit/internal/controls/style-change-processor.h new file mode 100644 index 0000000..eca3a03 --- /dev/null +++ b/dali-toolkit/internal/controls/style-change-processor.h @@ -0,0 +1,125 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_STYLE_CHANGE_PROCESSOR_H_ +#define __DALI_TOOLKIT_INTERNAL_STYLE_CHANGE_PROCESSOR_H_ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class ControlImpl; + +namespace Internal +{ + +/** + * This observes and processes when any style changes occur. When they do occur, it traverses through + * all registered controls and calls the StyleChanged method. + * + * This is created when a control first registers with it. Subsequent registrations increase the + * reference count. When the last control unregisters, i.e. the reference count is 0, the instance + * is also destroyed. + */ +class StyleChangeProcessor : public ConnectionTracker +{ +public: + + /** + * Non virtual destructor. + * We should not derive from StyleChangeProcessor. + * Destructor is called when the last control unregisters. + */ + ~StyleChangeProcessor(); + + /** + * Registers a control with the StyleChangeProcessor. + * @param[in] control The raw Control pointer. + */ + static void Register( ControlImpl* control ); + + /** + * Unregisters a control from the StyleChangeProcessor. + * @param[in] control The raw Control pointer. + */ + static void Unregister( ControlImpl* control ); + +public: + + /** + * Increment the processor's reference count. + */ + void Reference(); + + /** + * Decrement the processor's reference count. + */ + void Unreference(); + + /** + * Retrieve the processor's reference count. + * @return The reference count + */ + unsigned int ReferenceCount() const; + +private: + + /** + * Constructor. + * We should only create an instance upon first registration. + */ + StyleChangeProcessor(); + + // Undefined + StyleChangeProcessor(const StyleChangeProcessor&); + StyleChangeProcessor& operator=(const StyleChangeProcessor&); + +private: + + /** + * Callback for the StyleMonitor when the style changes on the platform. + * @param[in] styleMonitor The Style Monitor. + * @param[in] styleChange The style change information. + */ + void StyleChanged(Dali::StyleMonitor styleMonitor, Dali::StyleChange styleChange); + + /** + * Propagates the style change to all Controls in the actor hierarchy. + * This is done with a bottom-up approach, i.e. the leaf Control's StyleChange method gets + * called first followed by its parent and so on. + * @param[in] actor The actor whose children to process and send style change notification to. + * @param[in] change The style change. + */ + static void PropagateStyleChange(Actor actor, Dali::StyleChange change); + +private: + + unsigned int mCount; ///< The reference count + std::vector mControls; ///< Stores all registered controls. +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_STYLE_CHANGE_PROCESSOR_H_ diff --git a/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.cpp b/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.cpp new file mode 100644 index 0000000..fa98392 --- /dev/null +++ b/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.cpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// CLASS HEADER +#include "super-blur-view-impl.h" + +namespace //unnamed namespace +{ + +using namespace Dali; + +//Type registration +TypeRegistration mType( typeid(Toolkit::SuperBlurView), typeid(Toolkit::Control), NULL ); + +//Todo: make these properties instead of constants +const unsigned int GAUSSIAN_BLUR_DEFAULT_NUM_SAMPLES = 11; +const unsigned int GAUSSIAN_BLUR_NUM_SAMPLES_INCREMENTATION = 10; +const float GAUSSIAN_BLUR_BELL_CURVE_WIDTH = 4.5f; +const float GAUSSIAN_BLUR_BELL_CURVE_WIDTH_INCREMENTATION = 5.f; +const Pixel::Format GAUSSIAN_BLUR_RENDER_TARGET_PIXEL_FORMAT = Pixel::RGB888; +const float GAUSSIAN_BLUR_DOWNSAMPLE_WIDTH_SCALE = 0.5f; +const float GAUSSIAN_BLUR_DOWNSAMPLE_HEIGHT_SCALE = 0.5f; + +/** + * The constraint is used to blend the group of blurred images continuously with a unified blur strength property value which ranges from zero to one. + */ +struct ActorOpacityConstraint +{ + ActorOpacityConstraint(int totalImageNum, int currentImageIdx) + { + float rangeLength = 1.f / static_cast( totalImageNum ); + float index = static_cast( currentImageIdx ); + mRange = Vector2( index*rangeLength, (index+1.f)*rangeLength ); + } + + float operator()( float current, const PropertyInput& blurProperty ) + { + float blurStrength = blurProperty.GetFloat(); + if(blurStrength <= mRange.x) + { + return 1.f; + } + else if(blurStrength > mRange.y) + { + return 0.f; + } + else + { + return (mRange.y - blurStrength)/(mRange.y-mRange.x); + } + } + + Vector2 mRange; +}; + +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +SuperBlurView::SuperBlurView( unsigned int blurLevels ) +: ControlImpl( false ), + mBlurLevels( blurLevels ), + mBlurStrengthPropertyIndex(Property::INVALID_INDEX), + mResourcesCleared( true ), + mTargetSize( Vector2::ZERO ) +{ + DALI_ASSERT_ALWAYS( mBlurLevels > 0 && " Minimal blur level is one, otherwise no blur is needed" ); + mGaussianBlurView.resize( blurLevels ); + mBlurredImage.resize( blurLevels ); + mImageActors.resize( blurLevels + 1 ); +} + +SuperBlurView::~SuperBlurView() +{ +} + +Toolkit::SuperBlurView SuperBlurView::New( unsigned int blurLevels ) +{ + //Create the implementation + IntrusivePtr superBlurView( new SuperBlurView( blurLevels ) ); + + //Pass ownership to CustomActor via derived handle + Toolkit::SuperBlurView handle( *superBlurView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + superBlurView->Initialize(); + + return handle; +} + +void SuperBlurView::OnInitialize() +{ + mBlurStrengthPropertyIndex = Self().RegisterProperty( "BLUR_STRENGTH",0.f ); + + DALI_ASSERT_ALWAYS( mImageActors.size() == mBlurLevels+1 && "must synchronize the ImageActor group if blur levels got changed " ); + for(unsigned int i=0; i<=mBlurLevels;i++) + { + mImageActors[i] = ImageActor::New( ); + mImageActors[i].SetParentOrigin( ParentOrigin::CENTER ); + mImageActors[i].SetZ(-static_cast(i)*0.01f); + mImageActors[i].SetColorMode( USE_OWN_MULTIPLY_PARENT_ALPHA ); + Self().Add( mImageActors[i] ); + } + + for(unsigned int i=0; i < mBlurLevels; i++) + { + mImageActors[i].ApplyConstraint( Constraint::New( Actor::COLOR_ALPHA, ParentSource( mBlurStrengthPropertyIndex ), ActorOpacityConstraint(mBlurLevels, i) ) ); + } + + Self().SetSize(Stage::GetCurrent().GetSize()); +} + +void SuperBlurView::SetImage(Image inputImage) +{ + DALI_ASSERT_ALWAYS( mImageActors.size() == mBlurLevels+1 && "must synchronize the ImageActor group if blur levels got changed " ); + DALI_ASSERT_ALWAYS( mBlurredImage.size() == mBlurLevels && "must synchronize the blurred image group if blur levels got changed " ); + + ClearBlurResource(); + + mImageActors[0].SetImage( inputImage ); + + for(unsigned int i=1; i<=mBlurLevels;i++) + { + mImageActors[i].SetImage( mBlurredImage[i-1] ); + } + + BlurImage( 0, inputImage); + for(unsigned int i=1; i0 && level<=mBlurLevels ); + return mBlurredImage[level-1]; +} + +void SuperBlurView::BlurImage( unsigned int idx, Image image ) +{ + DALI_ASSERT_ALWAYS( mGaussianBlurView.size()>idx ); + mGaussianBlurView[idx] = Toolkit::GaussianBlurView::New( GAUSSIAN_BLUR_DEFAULT_NUM_SAMPLES+GAUSSIAN_BLUR_NUM_SAMPLES_INCREMENTATION*idx, + GAUSSIAN_BLUR_BELL_CURVE_WIDTH + GAUSSIAN_BLUR_BELL_CURVE_WIDTH_INCREMENTATION*static_cast(idx), + GAUSSIAN_BLUR_RENDER_TARGET_PIXEL_FORMAT, + GAUSSIAN_BLUR_DOWNSAMPLE_WIDTH_SCALE, GAUSSIAN_BLUR_DOWNSAMPLE_HEIGHT_SCALE, true ); + mGaussianBlurView[idx].SetParentOrigin(ParentOrigin::CENTER); + mGaussianBlurView[idx].SetSize(mTargetSize); + mGaussianBlurView[idx].SetUserImageAndOutputRenderTarget( image, mBlurredImage[idx] ); + if( idx == mBlurLevels-1 ) + { + mGaussianBlurView[idx].FinishedSignal().Connect( this, &SuperBlurView::OnBlurViewFinished ); + } + Stage::GetCurrent().Add( mGaussianBlurView[idx] ); + mGaussianBlurView[idx].ActivateOnce(); +} + +void SuperBlurView::OnBlurViewFinished( Toolkit::GaussianBlurView blurView ) +{ + ClearBlurResource(); + Toolkit::SuperBlurView handle( GetOwner() ); + mBlurFinishedSignal.Emit( handle ); +} + +void SuperBlurView::ClearBlurResource() +{ + if( !mResourcesCleared ) + { + DALI_ASSERT_ALWAYS( mGaussianBlurView.size() == mBlurLevels && "must synchronize the GaussianBlurView group if blur levels got changed " ); + for(unsigned int i=0; i(i+1); + mBlurredImage[i] = FrameBufferImage::New( mTargetSize.width/std::pow(2.f,exponent) , mTargetSize.height/std::pow(2.f,exponent), + GAUSSIAN_BLUR_RENDER_TARGET_PIXEL_FORMAT, Dali::Image::Never ); + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.h b/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.h new file mode 100644 index 0000000..846451a --- /dev/null +++ b/dali-toolkit/internal/controls/super-blur-view/super-blur-view-impl.h @@ -0,0 +1,164 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SUPER_BLUR_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_SUPER_BLUR_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class SuperBlurView; + +namespace Internal +{ + +/** + * SuperBlurView implementation class + */ +class SuperBlurView : public ControlImpl +{ +public: + + /** + * @copydoc Dali::Toolkit::SuperBlurView::New + */ + static Toolkit::SuperBlurView New( unsigned int blurLevels ); + + /** + * @copydoc Dali::Toolkit::SuperBlurView::SetImage + */ + void SetImage(Image inputImage); + + /** + * @copydoc Dali::Toolkit::SuperBlurView::GetBlurStrengthPropertyIndex + */ + Property::Index GetBlurStrengthPropertyIndex() const; + + /** + * @copydoc Dali::Toolkit::SuperBlurView::SetBlurStrength + */ + void SetBlurStrength( float blurStrength ); + + /** + * @copydoc Dali::Toolkit::SuperBlurView::GetCurrentBlurStrength + */ + float GetCurrentBlurStrength() const; + + /** + * @copydoc Dali::Toolkit::SuperBlurView::BlurFinishedSignal + */ + Dali::Toolkit::SuperBlurView::SuperBlurViewSignal& BlurFinishedSignal(); + + /** + * @copydoc Dali::Toolkit::SuperBlurView::GetBlurredImage + */ + Image GetBlurredImage( unsigned int level ); + +protected: + + /** + * Constructor. It initializes the SuperBlurView members + */ + SuperBlurView( unsigned int blurLevels ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~SuperBlurView(); + +private: // from ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnControlSizeSet + */ + virtual void OnControlSizeSet(const Vector3& targetSize); + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut() + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + +private: + + /** + * Carry out the idx-th pass of blurring + * @param[in] idx The blur pass index + * @param[in] image The input image for the current blurring, it is either the original image or the blurred image from the previous pass + */ + void BlurImage( unsigned int idx, Image image ); + + /** + * Signal handler to tell when the last blur view completes + * @param[in] blurView The blur view that just completed + */ + void OnBlurViewFinished( Toolkit::GaussianBlurView blurView ); + + /** + * Clear the resources used to create the blurred image + */ + void ClearBlurResource(); + +private: + + unsigned int mBlurLevels; + + Property::Index mBlurStrengthPropertyIndex; + + std::vector mGaussianBlurView; + std::vector mBlurredImage; + std::vector mImageActors; + bool mResourcesCleared; + + Vector2 mTargetSize; + Toolkit::SuperBlurView::SuperBlurViewSignal mBlurFinishedSignal; ///< Signal emitted when blur has completed. +}; + +} + +// Helpers for public-api forwarding methods +inline Toolkit::Internal::SuperBlurView& GetImpl( Toolkit::SuperBlurView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +inline const Toolkit::Internal::SuperBlurView& GetImpl( const Toolkit::SuperBlurView& obj ) +{ + DALI_ASSERT_ALWAYS(obj); + const Dali::RefObject& handle = obj.GetImplementation(); + return static_cast(handle); +} + +} + +} + +#endif /* __DALI_TOOLKIT_INTERNALSUPER_BLUR_VIEW_H__ */ diff --git a/dali-toolkit/internal/controls/table-view/array-2d.h b/dali-toolkit/internal/controls/table-view/array-2d.h new file mode 100644 index 0000000..076ee24 --- /dev/null +++ b/dali-toolkit/internal/controls/table-view/array-2d.h @@ -0,0 +1,274 @@ +#pragma once +#ifndef __DALI_ARRAY2D_H__ +#define __DALI_ARRAY2D_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +/** + * Helper wrapper for two dimensional array using std::vector + * Usage: + * + * Array2d< int > intArray( 3, 3 ); + * intArray[ 0 ][ 0 ] = 10; + * intArray.Resize( 4, 4 ); + * + */ +template< typename T > +class Array2d +{ +public: + + /** + * Default constructor. Creates a 0x0 array + */ + Array2d() + : mArray( 0, std::vector< T >( 0 ) ) + { } + + /** + * Constructs an array with given dimensions + * @param [in] rows for array + * @param [in] columns for array + */ + Array2d( unsigned int rows, unsigned int columns ) + : mArray( rows, std::vector< T >( columns ) ) + { } + + /** + * Destructor + */ + ~Array2d() + { } // Nothing to do, vector cleans up itself + + /** + * Copy constructor + * @param array to copy from + */ + Array2d( const Array2d& array ) + { + if( this != &array ) + { + mArray = array.mArray; + } + } + + /** + * Assignment operator + * @param array to copy from + * @return reference to self for chaining + */ + Array2d& operator=( const Array2d& array ) + { + if( this != &array ) + { + mArray = array.mArray; + } + return *this; + } + + /** + * @return the number of rows in the array + */ + unsigned int GetRows() + { + return mArray.size(); + } + + /** + * @return the number of columns in the array + */ + unsigned int GetColumns() + { + if( mArray.size() > 0 ) + { + // all columns are equal length + return mArray[ 0 ].size(); + } + return 0; + } + + /** + * @param [in] index of the row + * @return reference to the row vector for given index + */ + std::vector< T >& operator[]( unsigned int index ) + { + return mArray[ index ]; + } + + /** + * @param [in] index of the row + * @return const reference to the row vector for given index + */ + const std::vector< T >& operator[]( unsigned int index ) const + { + return mArray[ index ]; + } + + /** + * Insert a new row to given index + * @param [in] rowIndex of the new row + */ + void InsertRow( unsigned int rowIndex ) + { + // insert default initialized row of elements + mArray.insert( mArray.begin() + rowIndex, std::vector< T >( GetColumns() ) ); + } + + /** + * Delete a row from given index + * Removed elements are deleted + * @param [in] rowIndex of the row to delete + */ + void DeleteRow( unsigned int rowIndex ) + { + // erase the row + mArray.erase( mArray.begin() + rowIndex ); + } + + /** + * Delete a row from given index + * @param [in] rowIndex of the row to delete + * @param [out] removed elements + */ + void DeleteRow( unsigned int rowIndex, std::vector< T >& removed ) + { + // copy the row elements + removed.insert( removed.end(), mArray[ rowIndex ].begin(), mArray[ rowIndex ].end() ); + // erase the row + mArray.erase( mArray.begin() + rowIndex ); + } + + /** + * Insert a new column to given index + * @param [in] columnIndex of the new column + */ + void InsertColumn( unsigned int columnIndex ) + { + // go through all rows + const unsigned int rows = GetRows(); + for( unsigned int i = 0; i < rows; ++i ) + { + // insert default initialized element + mArray[ i ].insert( mArray[ i ].begin() + columnIndex, T() ); + } + } + + /** + * Delete a column from given index. + * Removed elements are deleted + * @param [in] columnIndex of the column to delete + */ + void DeleteColumn( unsigned int columnIndex ) + { + // go through all rows + const unsigned int rows = GetRows(); + for( unsigned int i = 0; i < rows; ++i ) + { + // erase the column + mArray[ i ].erase( mArray[ i ].begin() + columnIndex ); + } + } + + /** + * Delete a column from given index + * @param [in] columnIndex of the column to delete + * @param [out] removed elements + */ + void DeleteColumn( unsigned int columnIndex, std::vector< T >& removed ) + { + // go through all rows + const unsigned int rows = GetRows(); + for( unsigned int i = 0; i < rows; ++i ) + { + // copy the column element of this row + removed.push_back( mArray[ i ][ columnIndex ] ); + // erase the column + mArray[ i ].erase( mArray[ i ].begin() + columnIndex ); + } + } + + /** + * Resizes the array to given dimensions + * If new size is smaller in either dimension, items that wont fit will be deleted + * @param [in] rows for array + * @param [in] columns for array + */ + void Resize( unsigned int rows, unsigned int columns ) + { + // resize rows first, may increase or decrease size + mArray.resize( rows ); + for( unsigned int i = 0; i < rows; ++i ) + { + // resize each column, may increase or decrease size + mArray[ i ].resize( columns ); + } + } + + /** + * Resizes the array to given dimensions + * If new size is smaller, items that wont fit will be returned to caller + * @param [in] rows for array + * @param [in] columns for array + * @param [out] removed elements in case array is smaller in either dimension + */ + void Resize( unsigned int rows, unsigned int columns, std::vector< T >& removed ) + { + // remember old counts + const unsigned int oldRows = GetRows(); + const unsigned int oldColumns = GetColumns(); + // are rows being removed ? + if( rows < oldRows ) + { + // gather the elements of removed rows + for( unsigned int i = rows; i < oldRows; ++i ) + { + // copy the row elements, the whole row is gone + removed.insert( removed.end(), mArray[ i ].begin(), mArray[ i ].end() ); + } + } + // resize the rows, may increase or decrease size + mArray.resize( rows ); + // process columns, need to do all rows as also columns for new row need resizing + for( unsigned int i = 0; i < rows; ++i ) + { + // if this is an old row and columns are being removed + if( ( i < oldRows )&&( columns < oldColumns ) ) + { + // copy the columns, the end of row from columns is gone + removed.insert( removed.end(), mArray[ i ].begin() + columns, mArray[ i ].end() ); + } + // resize the column, may increase of decrease size + mArray[ i ].resize( columns ); + } + } + +private: + + std::vector< std::vector< T > > mArray; + +}; + +} // namespace Dali + +#endif // __DALI_ARRAY2D_H__ diff --git a/dali-toolkit/internal/controls/table-view/table-view-impl.cpp b/dali-toolkit/internal/controls/table-view/table-view-impl.cpp new file mode 100644 index 0000000..d702ced --- /dev/null +++ b/dali-toolkit/internal/controls/table-view/table-view-impl.cpp @@ -0,0 +1,1092 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "dali-toolkit/internal/controls/table-view/table-view-impl.h" +#include + +// EXTERNAL INCLUDES +#include +#include + +using namespace Dali; +using namespace std; + +namespace +{ +// Type registration +BaseHandle Create() +{ + return Toolkit::TableView::New(0, 0); +} +TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create ); + +const float DEFAULT_CONSTRAINT_DURATION = 0.0f; + +/** + * Constraint that sets a child property relative to parents Width or Height + */ +struct RelativeToWidthOrHeight +{ + /** + * Constraint that is relative (%) to parent width/height and applies a + * unit based padding before the relative calculation. + * @param scale of parent minus padding between 0 and 1 + * @param padding in world coordinate units + * @param fixed part in world coordinate units + */ + RelativeToWidthOrHeight( float scale, float padding, float fixed ) + : mScaleFactor( scale ), + mPadding( padding ), + mFixed( fixed ) + { + } + + inline float operator()( const float& parentWidthOrHeight ) + { + return mFixed + ( parentWidthOrHeight - mPadding ) * mScaleFactor; + } + + float operator()( const float& current, + const PropertyInput& parentWidthOrHeight ) + { + return operator()( parentWidthOrHeight.GetFloat() ); + } + + float mScaleFactor; + float mPadding; + float mFixed; +}; + +#if defined(DEBUG_ENABLED) +// debugging support, very useful when new features are added or bugs are hunted down +// currently not called from code so compiler will optimize these away, kept here for future debugging + +#define TABLEVIEW_TAG "DALI Toolkit::TableView" +#define TV_LOG(fmt, args...) LOG(LOG_INFO, TABLEVIEW_TAG, fmt, ## args) + +void PrintArray( Array2d& array ) +{ + TV_LOG( "Array2d size [%d,%d] \n", array.GetRows(), array.GetColumns() ); + // print values + for( unsigned int i = 0; i < array.GetRows(); ++i ) + { + for( unsigned int j = 0; j < array.GetColumns(); ++j ) + { + Dali::Toolkit::Internal::TableView::CellData data = array[i][j]; + char actor = ' '; + if( data.actor ) + { + actor = 'A'; + } + TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d ", i, j, actor, + data.position.rowIndex, data.position.columnIndex, + data.position.rowSpan, data.position.columnSpan ); + } + TV_LOG( "\n" ); + } +} + +// debugging support, very useful when new features are added or bugs are hunted down +// currently not called from code so compiler will optimize these away, kept here for future debugging +void PrintArray( Array2d& array ) +{ + TV_LOG( "Array2d size [%d,%d] \n", array.GetRows(), array.GetColumns() ); + // print values + for( unsigned int i = 0; i < array.GetRows(); ++i ) + { + for( unsigned int j = 0; j < array.GetColumns(); ++j ) + { + TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height ); + } + TV_LOG( "\n" ); + } +} +// debugging support, very useful when new features are added or bugs are hunted down +// currently not called from code so compiler will optimize these away, kept here for future debugging +void PrintVector( vector& array ) +{ + TV_LOG( "vector, size [%d]\n", array.size() ); + // print values + for( unsigned int i = 0; i < array.size(); ++i ) + { + TV_LOG( "vector[%d]=%.2f ", i, array[i] ); + } + TV_LOG( "\n" ); +} +#endif // defined(DEBUG_ENABLED) + +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns ) +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns ); + + // Pass ownership to CustomActor handle + Toolkit::TableView handle( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position ) +{ + // check that the child is valid + DALI_ASSERT_ALWAYS( child ); + + // if child is already parented, we adopt it + if( child.GetParent() ) + { + child.GetParent().Remove( child ); + } + // check if we need to expand our data array + if( position.rowIndex >= mCellData.GetRows() ) + { + // only adding new rows + ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() ); + } + if( position.columnIndex >= mCellData.GetColumns() ) + { + // only adding new columns + ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 ); + } + // check if there already is something in this cell + if( mCellData[ position.rowIndex ][ position.columnIndex ].actor ) + { + return false; // cannot share a cell, it would complicate all logic and not bring much benefit + } + RelayoutingLock lock( *this ); + // adopt the child + Self().Add( child ); + + // put the actor to the main cell + CellData data; + data.actor = child; + data.position = position; + mCellData[ position.rowIndex ][ position.columnIndex ] = data; + // if child spans multiple rows of columns + bool spanned = false; + if( position.rowSpan > 1 ) + { + // span might go outside table + if( position.rowIndex + position.rowSpan > mCellData.GetRows() ) + { + // increase table size for the full span, only increasing rows + ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() ); + } + spanned = true; + } + if( position.columnSpan > 1 ) + { + // span might go outside table + if( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) + { + // increase table size for the full span, only increasing columns + ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan ); + } + spanned = true; + } + // if it spanned multiple rows, put the cellinfo in all of those + if( spanned ) + { + for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row ) + { + // store same information to all cells, this way we can identify + // if a cell is the prime location of an actor or a spanned one + for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column ) + { + // store same information to all cells, this way we can identify + // if a cell is the prime location of an actor or a spanned one + mCellData[ row ][ column ] = data; + } + } + } + // relayout the whole table + RelayoutRequest(); + return true; // addition successful +} + +Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position ) +{ + // check if we have this row and column in the table + if( ( position.columnIndex >= mCellData.GetColumns() )|| + ( position.rowIndex >= mCellData.GetRows() ) ) + { + // return an empty handle + return Actor(); + } + // return the child handle + return mCellData[ position.rowIndex ][ position.columnIndex ].actor; +} + +Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position ) +{ + // get the child handle + Actor child = GetChildAt( position ); + // if no real actor there, nothing else to be done + if( child ) + { + RelayoutingLock lock( *this ); + // Remove the child, this will trigger a call to OnControlChildRemove + Self().Remove( child ); + + // relayout the table only if instances were found + if( RemoveAllInstances( child ) ) + { + RelayoutRequest(); + } + } + // return the child back to caller + return child; +} + +bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position ) +{ + // only find valid child actors + if( child ) + { + // walk through the layout data + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + if( mCellData[ row ][ column ].actor == child ) + { + position = mCellData[ row ][ column ].position; + return true; + } + } + } + } + return false; +} + +void TableView::InsertRow( unsigned int rowIndex ) +{ + RelayoutingLock lock( *this ); + mCellData.InsertRow( rowIndex ); + // need to update the cellinfos for the items that moved + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position; + // if cell is spanning and above and spans to inserted row + if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&& + ( position.rowIndex + position.rowSpan > rowIndex ) ) + { + // increase span by one + position.rowSpan++; + // copy cell to occupy the new column + mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ]; + } + // if below of inserted row, increase row index + else if( row > rowIndex ) + { + // increase index by one + position.rowIndex++; + } + } + } + mRelativeSizes.InsertRow( rowIndex ); + // inserting a row requires adjusting the height vectors + mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 ); + mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 ); + RelayoutRequest(); +} + +void TableView::DeleteRow( unsigned int rowIndex ) +{ + vector< Actor > ignored; + DeleteRow( rowIndex, ignored ); +} + +void TableView::DeleteRow( unsigned int rowIndex, vector& removed ) +{ + RelayoutingLock lock( *this ); + vector< CellData > lost; + mCellData.DeleteRow( rowIndex, lost ); + // need to update the cellinfos for the items that moved + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position; + // if cell is spanning and above and spans to deleted row + if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&& + ( position.rowIndex + position.rowSpan > rowIndex ) ) + { + // decrease span by one + if( position.rowSpan > 1 ) + { + position.rowSpan--; + } + } + // if below of or at the inserted row, decrease row index + else if( row >= rowIndex ) + { + // decrease index by one + if( position.rowIndex > 1 ) + { + position.rowIndex--; + } + } + } + } + // 1 row removed, 0 columns + RemoveAndGetLostActors( lost, removed, 1u, 0u ); + // resize the data structures + mRelativeSizes.DeleteRow( rowIndex ); + // deleting a row requires adjusting the height vectors + mFixedHeights.erase( mFixedHeights.begin() + rowIndex ); + mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex ); + RelayoutRequest(); +} + +void TableView::InsertColumn( unsigned int columnIndex ) +{ + RelayoutingLock lock( *this ); + mCellData.InsertColumn( columnIndex ); + // need to update the cellinfos for the items that moved + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position; + // if cell is spanning and left side and spans to inserted column + if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&& + ( position.columnIndex + position.columnSpan > columnIndex ) ) + { + // increase span by one + position.columnSpan++; + // copy cell to occupy the new column + mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ]; + } + // if on the right side of inserted column, increase column index + else if( column > columnIndex ) + { + // increase index by one + position.columnIndex++; + } + } + } + // relative sizes gets recalculated on Relayout + mRelativeSizes.InsertColumn( columnIndex ); + // inserting a column requires adjusting the width vectors + mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 ); + mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 ); + RelayoutRequest(); +} + +void TableView::DeleteColumn( unsigned int columnIndex ) +{ + vector< Actor > ignored; + DeleteColumn( columnIndex, ignored ); +} + +void TableView::DeleteColumn( unsigned int columnIndex, vector& removed ) +{ + RelayoutingLock lock( *this ); + vector< CellData > lost; + mCellData.DeleteColumn( columnIndex, lost ); + // need to update the cellinfos for the items that moved + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position; + // if cell is spanning and left side and spans to inserted column + if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&& + ( position.columnIndex + position.columnSpan > columnIndex ) ) + { + // decrease span by one + if( position.columnSpan > 1 ) + { + position.columnSpan--; + } + } + // if on the right side of or at the inserted column, decrease column index + else if( column >= columnIndex ) + { + // decrease index by one + if( position.columnIndex > 0 ) + { + position.columnIndex--; + } + } + } + } + // 0 rows, 1 column removed + RemoveAndGetLostActors( lost, removed, 0u, 1u ); + // resize the data structures + mRelativeSizes.DeleteColumn( columnIndex ); + // deleting a column requires adjusting the width vectors + mFixedWidths.erase( mFixedWidths.begin() + columnIndex ); + mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex ); + // relayout + RelayoutRequest(); +} + +void TableView::Resize( unsigned int rows, unsigned int columns ) +{ + vector< Actor > ignored; + Resize( rows, columns, ignored ); +} + +void TableView::Resize( unsigned int rows, unsigned int columns, vector& removed ) +{ + RelayoutingLock lock( *this ); + unsigned int oldRows = GetRows(); + unsigned int oldColumns = GetColumns(); + // resize data array + vector< CellData > lost; + ResizeContainers( rows, columns, lost ); + // calculate if we lost rows or columns + unsigned int rowsRemoved = 0; + unsigned int newRows = GetRows(); + if( oldRows < newRows ) + { + rowsRemoved = newRows - oldRows; + } + unsigned int columnsRemoved = 0; + unsigned int newColumns = GetColumns(); + if( oldColumns < newColumns ) + { + rowsRemoved = newColumns - oldColumns; + } + RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved ); + // finally relayout once all actors are removed + RelayoutRequest(); +} + +void TableView::SetCellPadding( Size padding ) +{ + // if padding really changed + if( padding != mPadding ) + { + mPadding = padding; + // do a relayout + RelayoutRequest(); + } +} + +Size TableView::GetCellPadding() +{ + return mPadding; +} + +void TableView::SetFixedHeight( unsigned int rowIndex, float height ) +{ + DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() ); + // add the fixed height to the array of fixed heights + mFixedHeights[ rowIndex ] = height; + // relayout all cells, no lock needed as nothing added or removed + RelayoutRequest(); +} + +float TableView::GetFixedHeight( unsigned int rowIndex ) const +{ + DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() ); + + return mFixedHeights[ rowIndex ]; +} + +void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage ) +{ + DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() ); + // add the relative height to the array of relative heights + mRelativeHeights[ rowIndex ] = heightPercentage; + // relayout all cells, no lock needed as nothing added or removed + RelayoutRequest(); +} + +float TableView::GetRelativeHeight( unsigned int rowIndex ) const +{ + DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() ); + + return mRelativeHeights[ rowIndex ]; +} + +void TableView::SetFixedWidth( unsigned int columnIndex, float width ) +{ + DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() ); + // add the fixed width to the array of fixed column widths + mFixedWidths[ columnIndex ] = width; + // relayout all cells, no lock needed as nothing added or removed + RelayoutRequest(); +} + +float TableView::GetFixedWidth( unsigned int columnIndex ) const +{ + DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() ); + + return mFixedWidths[ columnIndex ]; +} + +void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage ) +{ + DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() ); + // add the relative widths to the array of relative widths + mRelativeWidths[ columnIndex ] = widthPercentage; + // relayout all cells, no lock needed as nothing added or removed + RelayoutRequest(); +} + +float TableView::GetRelativeWidth( unsigned int columnIndex ) const +{ + DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() ); + + return mRelativeWidths[ columnIndex ]; +} + +void TableView::SetLayoutAnimationDuration( float duration ) +{ + mConstraintDuration = duration; +} + +float TableView::GetLayoutAnimationDuration() +{ + return mConstraintDuration; +} + +void TableView::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + float fixedHeightsTotal = 0.0f; + float fixedWidthsTotal = 0.0f; + + // 1. update the relative sizes and calculate total fixed height and width + UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal ); + + // 2. go through the layout data and create constraints + float cumulatedFixedHeight = 0.0f; + float cumulatedRelativeHeight = 0.0f; + + // iterate the table + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + // float versions of the count + 1 to keep precision + const float maxRowPlusOne( rowCount + 1 ); + const float maxColumnPlusOne( columnCount + 1 ); + for( unsigned int row = 0; row < rowCount; ++row ) + { + // reset widths at the start of each row + float cumulatedFixedWidth = 0.0f; + float cumulatedRelativeWidth = 0.0f; + for( unsigned int column = 0; column < columnCount; ++column ) + { + // check if this cell has an actor + Actor actor = mCellData[ row ][ column ].actor; + const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position; + // if there is an actor and this is the main cell of the actor + // an actor can be in multiple cells if its row or columnspan is more than 1 + // we however must only lay out each actor only once + if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) ) + { + // anchor actor correctly + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + // remove old constraints + actor.RemoveConstraints(); + + // 1. set position + // get the row and column indices + float rowPos( position.rowIndex ); + float colPos( position.columnIndex ); + // constrain the actor position to be relative to the width and height of table + // minus the padding of course (padding is all around cells) + Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight ); + // fixed height rows and fixed width cells are considered as padding so + // they are removed from the total size for relative + // for position only consider cumulated fixed rows and columns from top and left + Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal, + maxRowPlusOne * mPadding.height + fixedHeightsTotal ); + Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth, + ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight ); + + Constraint widthConstraint = Constraint::New( Actor::POSITION_X, + ParentSource( Actor::SIZE_WIDTH ), + RelativeToWidthOrHeight( relativePosition.x, positionPadding.x, fixedPosition.x ) ); + + Constraint heightConstraint = Constraint::New( Actor::POSITION_Y, + ParentSource( Actor::SIZE_HEIGHT ), + RelativeToWidthOrHeight( relativePosition.y, positionPadding.y, fixedPosition.y ) ); + + widthConstraint.SetApplyTime( mConstraintDuration ); + heightConstraint.SetApplyTime( mConstraintDuration ); + + // bake constrained position value if constraint is removed + widthConstraint.SetRemoveAction( Constraint::Bake ); + heightConstraint.SetRemoveAction( Constraint::Bake ); + + actor.ApplyConstraint( widthConstraint ); + actor.ApplyConstraint( heightConstraint ); + + // 2. set size + // constrain the actor size to be relative to the size of table + // get the relative size for this cell + Vector2 relativeSize( mRelativeSizes[ row ][ column ] ); + Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] ); + // if we span multiple cells, need to sum them all up, both fixed and relative parts + if( position.rowSpan > 1 ) + { + for( unsigned int i = 1; i < position.rowSpan; ++i ) + { + // accumulate the height only + relativeSize.height += mRelativeSizes[ row + i ][ column ].height; + fixedSize.height += mFixedHeights[ row + i ]; + } + } + if( position.columnSpan > 1 ) + { + for( unsigned int i = 1; i < position.columnSpan; ++i ) + { + // accumulate the width only + relativeSize.width += mRelativeSizes[ row ][ column + i ].width; + fixedSize.width += mFixedWidths[ column + i ]; + } + } + // minus the padding from size (padding is all around cells) + // if item spans multiple columns or rows then less padding is added (default span is 1) + // fixed height rows and fixed width cells are considered as padding so they are removed + // from the total available size for relative cells + Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal, + maxRowPlusOne * mPadding.height + fixedHeightsTotal ); + // and added to the fixed size multiplied by the span of rows and columns + fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width; + fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height; + + RelativeToWidthOrHeight relativeWidthFunctor( relativeSize.x, sizePadding.x, fixedSize.x ); + RelativeToWidthOrHeight relativeHeightFunctor( relativeSize.y, sizePadding.y, fixedSize.y ); + + widthConstraint = Constraint::New( Actor::SIZE_WIDTH, + ParentSource( Actor::SIZE_WIDTH ), + relativeWidthFunctor ); + + heightConstraint = Constraint::New( Actor::SIZE_HEIGHT, + ParentSource( Actor::SIZE_HEIGHT ), + relativeHeightFunctor ); + + widthConstraint.SetApplyTime( mConstraintDuration ); + heightConstraint.SetApplyTime( mConstraintDuration ); + + // bake constrained size value if constraint is removed + widthConstraint.SetRemoveAction( Constraint::Bake ); + heightConstraint.SetRemoveAction( Constraint::Bake ); + + actor.ApplyConstraint( widthConstraint ); + actor.ApplyConstraint( heightConstraint ); + + // Relayout Children + Relayout ( actor, Vector2( relativeWidthFunctor( size.width ), relativeHeightFunctor( size.height ) ), container ); + } + // for position we need to keep track of current fixed width and relative width + // increase for next column + cumulatedFixedWidth += mFixedWidths[ column ]; + cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width; + } + // for position we need to keep track of current fixed height and relative height + // increase for next row + cumulatedFixedHeight += mFixedHeights[ row ]; + cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height + } +} + +unsigned int TableView::GetRows() +{ + return mCellData.GetRows(); +} + +unsigned int TableView::GetColumns() +{ + return mCellData.GetColumns(); +} + +void TableView::OnControlChildAdd( Actor& child ) +{ + if( mLayoutingChild ) + { + // we're in the middle of laying out children so no point doing anything here + return; + } + // check if we're already laying out this child somewhere on the table + // walk through the layout data + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + // child not yet laid out, find the first free slot + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + // no actor means free cell + if( !(mCellData[ row ][ column ].actor) ) + { + // put the actor in the cell + CellData data; + data.actor = child; + data.position.columnIndex = column; + data.position.rowIndex = row; + mCellData[ row ][ column ] = data; + RelayoutRequest(); + // don' continue + return; + } + } + } + // still here, no room for the poor child so increase the array. Need a new row + ResizeContainers( rowCount + 1, columnCount ); + // put the actor to the first cell of the new row + CellData data; + data.actor = child; + data.position.rowIndex = rowCount; + data.position.columnIndex = 0; + mCellData[ rowCount ][ 0 ] = data; + // finally relayout the table + RelayoutRequest(); +} + +void TableView::OnControlChildRemove( Actor& child ) +{ + // dont process if we're in the middle of bigger operation like delete row, column or resize + if( !mLayoutingChild ) + { + // relayout the table only if instances were found + if( RemoveAllInstances( child ) ) + { + RelayoutRequest(); + } + } +} + +TableView::TableView( unsigned int initialRows, unsigned int initialColumns ) +: ControlImpl( true ), // requires touch + mCellData( initialRows, initialColumns ), + mLayoutingChild( false ), + mConstraintDuration( DEFAULT_CONSTRAINT_DURATION ) +{ + SetKeyboardNavigationSupport( true ); + ResizeContainers( initialRows, initialColumns ); +} + +void TableView::OnInitialize() +{ + // Make self as keyboard focusable and focus group + Actor self = Self(); + self.SetKeyboardFocusable(true); + SetAsKeyboardFocusGroup(true); +} + +void TableView::ResizeContainers( unsigned int rows, unsigned int columns ) +{ + vector ignored; + ResizeContainers( rows, columns, ignored ); +} + +void TableView::ResizeContainers( unsigned int rows, unsigned int columns, vector& removed ) +{ + mCellData.Resize( rows, columns, removed ); + // we dont care if these go smaller, data will be regenerated or is not needed anymore + mRelativeSizes.Resize( rows, columns ); + mFixedHeights.resize( rows ); + mRelativeHeights.resize( rows ); + mFixedWidths.resize( columns ); + mRelativeWidths.resize( columns ); +} + +void TableView::RemoveAndGetLostActors( const vector& lost, vector& removed, + unsigned int rowsRemoved, unsigned int columnsRemoved ) +{ + // iterate through all lost cells + vector< CellData >::const_iterator iter = lost.begin(); + for( ; iter != lost.end(); ++iter ) + { + // if it is a valid actor + if( (*iter).actor ) + { + // is this actor still somewhere else in the table + Toolkit::TableView::CellPosition position; + if( FindChildPosition( (*iter).actor, position ) ) + { + // it must be spanning multiple cells, position contains the top left most one + // check if position is left of the removed location + if( position.columnIndex < (*iter).position.columnIndex ) + { + // if column span is greater than 1 + if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 ) + { + // decrease column span + mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved; + } + } + // check if position is left of the removed location + if( position.rowIndex < (*iter).position.rowIndex ) + { + // if row span is greater than 1 + if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 ) + { + // decrease row span + mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved; + } + } + } + else + { + // this actor is gone for good + // add actor to removed container + removed.push_back( (*iter).actor ); + // we dont want the child actor anymore + Self().Remove( (*iter).actor ); + } + } + } +} + +bool TableView::RemoveAllInstances( Actor child ) +{ + bool found = false; + // walk through the layout data + const unsigned int rowCount = mCellData.GetRows(); + const unsigned int columnCount = mCellData.GetColumns(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + for( unsigned int column = 0; column < columnCount; ++column ) + { + if( mCellData[ row ][ column ].actor == child ) + { + // clear the cell, NOTE that the cell might be spanning multiple cells + mCellData[ row ][ column ] = CellData(); + found = true; + } + } + } + return found; +} + +void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal ) +{ + // 1. check all the fixed heights and widths to know how much size they take in total + // as well as the relative heights and widths to know how much is left for the 'fill' cells + unsigned int fixedRowCount = 0; + unsigned int relativeRowCount = 0; + float relativeHeightsTotal = 0.0f; + const unsigned int rowCount = mCellData.GetRows(); + for( unsigned int row = 0; row < rowCount; ++row ) + { + if( mFixedHeights[ row ] > 0.0f ) + { + ++fixedRowCount; + fixedHeightsTotal += mFixedHeights[ row ]; + } + if( mRelativeHeights[ row ] > 0.0f ) + { + ++relativeRowCount; + relativeHeightsTotal += mRelativeHeights[ row ]; + } + } + unsigned int fixedColumnCount = 0; + unsigned int relativeColumnCount = 0; + const unsigned int columnCount = mCellData.GetColumns(); + float relativeWidthsTotal = 0.0f; + for( unsigned int column = 0; column < columnCount; ++column ) + { + if( mFixedWidths[ column ] > 0.0f ) + { + ++fixedColumnCount; + fixedWidthsTotal += mFixedWidths[ column ]; + } + if( mRelativeWidths[ column ] > 0.0f ) + { + ++relativeColumnCount; + relativeWidthsTotal += mRelativeWidths[ column ]; + } + } + + // 2. cap the relative width and height totals to 100% + if( relativeHeightsTotal > 1.0f ) + { + relativeHeightsTotal = 1.0f; + } + if( relativeWidthsTotal > 1.0f ) + { + relativeWidthsTotal = 1.0f; + } + + // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums + const float fillRowCount( rowCount - relativeRowCount - fixedRowCount ); + const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount ); + + // walk through the data containers + for( unsigned int row = 0; row < rowCount; ++row ) + { + float relativeHeight = 0.0f; + // if we have a fixed height, relative height is 0 + if( mFixedHeights[ row ] > 0.0f ) + { + relativeHeight = 0.0f; + } + // else if we're given a specific row height %, use that + else if( mRelativeHeights[ row ] > 0.0f ) + { + relativeHeight = mRelativeHeights[ row ]; + } + // else if there are fill rows + else if( fillRowCount > 0 ) + { + // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows + relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount; + } + for( unsigned int column = 0; column < columnCount; ++column ) + { + float relativeWidth = 0.0f; + // if we have a fixed width, relative width is 0 + if( mFixedWidths[ column ] > 0.0f ) + { + relativeWidth = 0.0f; + } + // else if we're given a specific column width %, use that + else if( mRelativeWidths[ column ] > 0.0f ) + { + relativeWidth = mRelativeWidths[ column ]; + } + // else if there are fill columns + else if( fillColumnCount > 0 ) + { + // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns + relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount; + } + // store the value + mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight ); + } + } +} + +TableView::~TableView() +{ + // nothing to do +} + +Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + Actor nextFocusableActor; + + if ( !currentFocusedActor ) + { + // Nothing is currently focused, so the child in the first cell should be focused. + nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0)); + } + else + { + Toolkit::TableView::CellPosition position; + if( FindChildPosition( currentFocusedActor, position ) ) + { + // The current focused actor is a child of TableView + bool focusLost = false; + int currentRow = position.rowIndex; + int currentColumn = position.columnIndex; + int numberOfColumns = GetColumns(); + int numberOfRows = GetRows(); + + switch ( direction ) + { + case Control::Left: + { + if(--currentColumn < 0) + { + currentColumn = numberOfColumns - 1; + if(--currentRow < 0) + { + currentRow = loopEnabled ? numberOfRows - 1 : 0; + focusLost = (currentRow == 0); + } + } + break; + } + case Control::Right: + { + if(++currentColumn > numberOfColumns - 1) + { + currentColumn = 0; + if(++currentRow > numberOfRows - 1) + { + currentRow = loopEnabled ? 0 : numberOfRows - 1; + focusLost = (currentRow == numberOfRows - 1); + } + } + break; + } + case Control::Up: + { + if(--currentRow < 0) + { + currentRow = loopEnabled ? numberOfRows - 1 : 0; + focusLost = (currentRow == 0); + } + break; + } + case Control::Down: + { + if(++currentRow > numberOfRows - 1) + { + currentRow = loopEnabled ? 0 : numberOfRows - 1; + focusLost = (currentRow == numberOfRows - 1); + } + break; + } + } + + // Move the focus if we haven't lost it. + if(!focusLost) + { + nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn)); + } + } + else + { + // The current focused actor is not within table view, so the child in the first cell should be focused. + nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0)); + } + } + + return nextFocusableActor; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/table-view/table-view-impl.h b/dali-toolkit/internal/controls/table-view/table-view-impl.h new file mode 100644 index 0000000..5f7a5d1 --- /dev/null +++ b/dali-toolkit/internal/controls/table-view/table-view-impl.h @@ -0,0 +1,348 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TABLE_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_TABLE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include "array-2d.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * TableView is a custom control for laying out actors in a table layout + * @see Dali::Toolkit:TableView for more details + */ +class TableView : public ControlImpl +{ +public: + + /** + * Structure for the layout data + */ + struct CellData + { + // data members + Actor actor; + Toolkit::TableView::CellPosition position; + }; + + /** + * Create a new TableView. + * @return A smart-pointer to the newly allocated TableView. + */ + static Toolkit::TableView New( unsigned int initialRows, unsigned int initialColumns ); + + /** + * @copydoc Toolkit::TableView::AddChild + */ + bool AddChild( Actor child, Toolkit::TableView::CellPosition position ); + + /** + * @copydoc Toolkit::TableView::GetChildAt + */ + Actor GetChildAt( Toolkit::TableView::CellPosition position ); + + /** + * @copydoc Toolkit::TableView::RemoveChildAt + */ + Actor RemoveChildAt( Toolkit::TableView::CellPosition position ); + + /** + * @copydoc Toolkit::TableView::FindChildPosition + */ + bool FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position ); + + /** + * @copydoc Toolkit::TableView::InsertRow + */ + void InsertRow( unsigned int rowIndex ); + + /** + * @copydoc Toolkit::TableView::DeleteRow( unsigned int rowIndex ) + */ + void DeleteRow( unsigned int rowIndex ); + + /** + * @copydoc Toolkit::TableView::DeleteRow( unsigned int rowIndex, std::vector& removed ) + */ + void DeleteRow( unsigned int rowIndex, std::vector& removed ); + + /** + * @copydoc Toolkit::TableView::InsertColumn + */ + void InsertColumn( unsigned int columnIndex ); + + /** + * @copydoc Toolkit::TableView::DeleteColumn( unsigned int columnIndex ) + */ + void DeleteColumn( unsigned int columnIndex ); + + /** + * @copydoc Toolkit::TableView::DeleteColumn( unsigned int columnIndex, std::vector& removed ) + */ + void DeleteColumn( unsigned int columnIndex, std::vector& removed ); + + /** + * @copydoc Toolkit::TableView::Resize( unsigned int rows, unsigned int columns ) + */ + void Resize( unsigned int rows, unsigned int columns ); + + /** + * @copydoc Toolkit::TableView::Resize( unsigned int rows, unsigned int columns, std::vector& removed ) + */ + void Resize( unsigned int rows, unsigned int columns, std::vector& removed ); + + /** + * @copydoc Toolkit::TableView::SetCellPadding + */ + void SetCellPadding( Size padding ); + + /** + * @copydoc Toolkit::TableView::GetCellPadding + */ + Size GetCellPadding(); + + /** + * @copydoc Toolkit::TableView::SetFixedHeight + */ + void SetFixedHeight( unsigned int rowIndex, float height ); + + /** + * @copydoc Toolkit::TableView::GetFixedHeight + */ + float GetFixedHeight( unsigned int rowIndex ) const; + + /** + * @copydoc Toolkit::TableView::SetRelativeHeight + */ + void SetRelativeHeight( unsigned int rowIndex, float heightPercentage ); + + /** + * @copydoc Toolkit::TableView::GetRelativeHeight + */ + float GetRelativeHeight( unsigned int rowIndex ) const; + + /** + * @copydoc Toolkit::TableView::SetFixedWidth + */ + void SetFixedWidth( unsigned int columnIndex, float width ); + + /** + * @copydoc Toolkit::TableView::GetFixedWidth + */ + float GetFixedWidth( unsigned int columnIndex ) const; + + /** + * @copydoc Toolkit::TableView::SetRelativeWidth + */ + void SetRelativeWidth( unsigned int columnIndex, float widthPercentage ); + + /** + * @copydoc Toolkit::TableView::GetRelativeWidth + */ + float GetRelativeWidth( unsigned int columnIndex ) const; + + /** + * @copydoc Toolkit::TableView::SetLayoutAnimationDuration + */ + void SetLayoutAnimationDuration( float duration ); + + /** + * @copydoc Toolkit::TableView::GetLayoutAnimationDuration + */ + float GetLayoutAnimationDuration(); + + + /** + * @copydoc Toolkit::TableView::GetRows + */ + unsigned int GetRows(); + + /** + * @copydoc Toolkit::TableView::GetColumns + */ + unsigned int GetColumns(); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::ControlImpl::OnControlChildAdd(Actor& child) + */ + virtual void OnControlChildAdd(Actor& child); + + /** + * @copydoc Toolkit::ControlImpl::OnControlChildRemove(Actor& child) + */ + virtual void OnControlChildRemove(Actor& child); + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + + /** + * @copydoc Toolkit::ControlImpl::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::ControlImpl::GetNextKeyboardFocusableActor + */ + virtual Actor GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + +private: // Implementation + + /** + * Construct a new TableView. + */ + TableView( unsigned int initialRows, unsigned int initialColumns ); + + /** + * Resizes the data containers to match the new size + * @param [in] rows in the table + * @param [in] columns in the table + */ + void ResizeContainers( unsigned int rows, unsigned int columns ); + + /** + * Resizes the data containers to match the new size + * @param [in] rows in the table + * @param [in] columns in the table + * @param [out] removed celldata + */ + void ResizeContainers( unsigned int rows, unsigned int columns, std::vector& removed ); + + /** + * Helper to get the list of lost actors in the case when table looses cells. + * Also handles the case when actors span multiple cells + * @param lost cells + * @param removed actors + * @param rowsRemoved from table + * @param columnsRemoved from table + */ + void RemoveAndGetLostActors( const std::vector& lost, std::vector& removed, + unsigned int rowsRemoved, unsigned int columnsRemoved ); + + /** + * Helper to remove all instances of the actor + * @param child actor to remove + * @return true if the actor was found + */ + bool RemoveAllInstances( Actor child ); + + /** + * Helper to update relative sizes + * @param fixedHeightsTotal sum of the fixed height rows + * @param fixedWidthsTotal sum of the fixed width columns + */ + void UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal ); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~TableView(); + + /** + * Helper class to prevent child adds and removes from causing relayout + * when we're already anyways going to do one in the end + */ + class RelayoutingLock + { + public: // API + + /** + * Constructor, sets the lock boolean + */ + RelayoutingLock( TableView& parent ) + : mLock( parent.mLayoutingChild ) + { + mLock = true; + } + + /** + * Destructor, releases lock boolean + */ + ~RelayoutingLock() + { + mLock = false; + // Note, we could also call Relayout here. This would save one line of code + // from each method that uses this lock but destructors are not meant to do + // big processing so better to not do it here. This destructor would also + // be called in case of an exception and we don't definitely want to do Relayout + // in that situation + } + + private: + bool& mLock; + }; + +private: + + // Undefined copy constructor and assignment operators + TableView(const TableView&); + TableView& operator=(const TableView& rhs); + +private: // Data + + Array2d mCellData; + Array2d mRelativeSizes; + std::vector mFixedHeights; + std::vector mRelativeHeights; + std::vector mFixedWidths; + std::vector mRelativeWidths; + Size mPadding; + bool mLayoutingChild; + float mConstraintDuration; + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::TableView& GetImpl( Toolkit::TableView& tableView ) +{ + DALI_ASSERT_ALWAYS(tableView); + + Dali::RefObject& handle = tableView.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::TableView& GetImpl( const Toolkit::TableView& tableView ) +{ + DALI_ASSERT_ALWAYS(tableView); + + const Dali::RefObject& handle = tableView.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TABLE_VIEW_H__ diff --git a/dali-toolkit/internal/controls/text-input/text-input-impl.cpp b/dali-toolkit/internal/controls/text-input/text-input-impl.cpp new file mode 100644 index 0000000..61bd6c7 --- /dev/null +++ b/dali-toolkit/internal/controls/text-input/text-input-impl.cpp @@ -0,0 +1,5072 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define GET_LOCALE_TEXT(string) dgettext("sys_string", string) + +using namespace std; +using namespace Dali; + +// Local Data +namespace +{ + +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT"); +#endif + +const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits::max() ); // Max possible number +const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits::max() ); // Max possible number +const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f ); // Selection cursor image size +const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f ); +const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); +const Vector4 LIGHTBLUE( 10.0f/255.0f, 140.0f/255.0f, 210.0f/255.0f, 1.0f ); // Used for Selection highlight + +const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" ); +const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" ); +const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); +const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" ); +const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" ); +const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" ); + +const char* DEFAULT_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" ); +const char* DEFAULT_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" ); +const char* DEFAULT_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" ); +const char* DEFAULT_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" ); +const char* DEFAULT_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" ); +const char* DEFAULT_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" ); + +const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f ); + +const std::string OPTION_SELECT_WORD("select_word"); ///< "Select Word" popup option. +const std::string OPTION_SELECT_ALL("select_all"); ///< "Select All" popup option. +const std::string OPTION_CUT("cut"); ///< "Cut" popup option. +const std::string OPTION_COPY("copy"); ///< "Copy" popup option. +const std::string OPTION_PASTE("paste"); ///< "Paste" popup option. +const std::string OPTION_CLIPBOARD("clipboard"); ///< "Clipboard" popup option. + +const std::size_t CURSOR_BLINK_INTERVAL = 500; ///< Cursor blink interval +const float CHARACTER_THRESHOLD( 2.5f ); ///< the threshold of a line. +const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.0f ); ///< 1. Highlight rendered (z-offset). +const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.1f ); ///< 2. Text rendered (z-offset). +const float UI_Z_OFFSET( 0.2f ); ///< 3. Text Selection Handles/Cursor z-offset. + +const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET); ///< Text Selection Handles/Cursor offset. +const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle One's Offset +const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f); ///< Handle Two's Offset +const float TOP_HANDLE_TOP_OFFSET(-1.5f); ///< Offset between top handle and cutCopyPaste pop-up +const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f); ///< Offset between bottom handle and cutCopyPaste pop-up +const float CURSOR_THICKNESS(6.0f); +const Degree CURSOR_ANGLE_OFFSET(2.0f); ///< Offset from the angle of italic angle. + +const std::string NEWLINE( "\n" ); + +const TextStyle DEFAULT_TEXT_STYLE; + +const unsigned int SCROLL_TICK_INTERVAL = 50u; +const float SCROLL_THRESHOLD = 10.f; +const float SCROLL_SPEED = 15.f; + +/** + * Whether the given style is the default style or not. + * @param[in] style The given style. + * @return \e true if the given style is the default. Otherwise it returns \e false. + */ +bool IsDefaultStyle( const TextStyle& style ) +{ + return DEFAULT_TEXT_STYLE == style; +} + +/** + * Whether the given styled text is using the default style or not. + * @param[in] textArray The given text. + * @return \e true if the given styled text is using the default style. Otherwise it returns \e false. + */ +bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray ) +{ + for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it ) + { + const TextStyle& style( (*it).mStyle ); + + if( !IsDefaultStyle( style ) ) + { + return false; + } + } + + return true; +} + +/** + * Selection state enumeration (FSM) + */ +enum SelectionState +{ + SelectionNone, ///< Currently not encountered selected section. + SelectionStarted, ///< Encountered selected section + SelectionFinished ///< Finished selected section +}; + +std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable ) +{ + for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend(); + it != endIt; + ++it ) + { + if( ( *it ).mIsVisible ) + { + return --cursorPosition; + } + + --cursorPosition; + } + + return 0; +} + +std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable ) +{ + for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it ) + { + if( ( *it ).mIsVisible ) + { + return cursorPosition; + } + + ++cursorPosition; + } + + return cursorPosition; +} + +/** + * Whether the given position plus the cursor size offset is inside the given boundary. + * + * @param[in] position The given position. + * @param[in] cursorSize The cursor size. + * @param[in] controlSize The given boundary. + * + * @return whether the given position is inside the given boundary. + */ +bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize ) +{ + return ( position.x >= -Math::MACHINE_EPSILON_1000 ) && + ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) && + ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) && + ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 ); +} + +/** + * Splits a text in two halves. + * + * If the text's number of characters is odd, firstHalf has one more character. + * + * @param[in] text The text to be split. + * @param[out] firstHalf The first half of the text. + * @param[out] secondHalf The second half of the text. + */ +void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text, + Toolkit::MarkupProcessor::StyledTextArray& firstHalf, + Toolkit::MarkupProcessor::StyledTextArray& secondHalf ) +{ + firstHalf.clear(); + secondHalf.clear(); + + const std::size_t textLength = text.size(); + const std::size_t half = ( textLength / 2 ) + ( textLength % 2 ); + + firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half ); + secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() ); +} + +} // end of namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +BaseHandle Create() +{ + return Toolkit::TextInput::New(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT, &TextInput::DoConnectSignal ); +SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT, &TextInput::DoConnectSignal ); +SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED, &TextInput::DoConnectSignal ); +SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal ); +SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED, &TextInput::DoConnectSignal ); +SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES, &TextInput::DoConnectSignal ); + +} + +// [TextInput::HighlightInfo] ///////////////////////////////////////////////// + +void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 ) +{ + QuadCoordinates quad(x1, y1, x2, y2); + mQuadList.push_back( quad ); +} + +void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max) +{ + for(std::size_t i = 0;i < mQuadList.size(); i++) + { + QuadCoordinates& quad = mQuadList[i]; + + quad.min.Clamp(min, max); + quad.max.Clamp(min, max); + } // end for +} + +// [TextInput] //////////////////////////////////////////////////////////////// + +Dali::Toolkit::TextInput TextInput::New() +{ + // Create the implementation + TextInputPtr textInput(new TextInput()); + // Pass ownership to CustomActor via derived handle + Dali::Toolkit::TextInput handle(*textInput); + + textInput->Initialize(); + + return handle; +} + +TextInput::TextInput() +:ControlImpl( true ), + mState( StateEdit ), + mStyledText(), + mInputStyle(), + mLineHeight( 0.f ), + mDisplayedTextView(), + mStyledPlaceHolderText(), + mMaxStringLength( DEFAULT_MAX_SIZE ), + mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ), + mCursorPosition( 0 ), + mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ), + mIsSelectionHandleOneFlipped( false ), + mIsSelectionHandleTwoFlipped( false ), + mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ), + mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ), + mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ), + mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ), + mSelectionHandleOnePosition( 0 ), + mSelectionHandleTwoPosition( 0 ), + mPreEditString(), + mPreEditStartPosition( 0 ), + mPreEditLength ( 0 ), + mNumberOfSurroundingCharactersDeleted( 0 ), + mTouchStartTime( 0 ), + mTextLayoutInfo(), + mCurrentCopySelecton(), + mScrollTimer(), + mScrollDisplacement(), + mCurrentHandlePosition(), + mCurrentSelectionId(), + mCurrentSelectionHandlePosition(), + mRequestedSelection( 0, 0 ), + mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ), + mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ), + mClipboard(), + mOverrideAutomaticAlignment( false ), + mCursorRTLEnabled( false ), + mClosestCursorPositionEOL ( false ), + mCursorBlinkStatus( true ), + mCursorVisibility( false ), + mGrabHandleVisibility( false ), + mIsCursorInScrollArea( true ), + mIsGrabHandleInScrollArea( true ), + mEditModeActive( false ), + mEditOnTouch( true ), + mTextSelection( true ), + mExceedEnabled( true ), + mGrabHandleEnabled( true ), + mIsSelectionHandleFlipEnabled( true ), + mPreEditFlag( false ), + mIgnoreCommitFlag( false ), + mIgnoreFirstCommitFlag( false ), + mSelectingText( false ), + mPreserveCursorPosition( false ), + mSelectTextOnCommit( false ), + mUnderlinedPriorToPreEdit ( false ), + mCommitByKeyInput( false ), + mPlaceHolderSet( false ) +{ + // Updates the line height accordingly with the input style. + UpdateLineHeight(); +} + +TextInput::~TextInput() +{ + StopCursorBlinkTimer(); +} + +// Public + +std::string TextInput::GetText() const +{ + std::string text; + + // Return text-view's text only if the text-input's text is not empty + // in order to not to return the placeholder text. + if( !mStyledText.empty() ) + { + text = mDisplayedTextView.GetText(); + } + + return text; +} + +std::string TextInput::GetMarkupText() const +{ + std::string markupString; + MarkupProcessor::GetMarkupString( mStyledText, markupString ); + + return markupString; +} + +void TextInput::SetPlaceholderText( const std::string& placeHolderText ) +{ + // Get the placeholder styled text array from the markup string. + MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText ); + + if( mStyledText.empty() ) + { + // Set the placeholder text only if the styled text is empty. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } +} + +std::string TextInput::GetPlaceholderText() +{ + // Traverses the styled placeholder array getting only the text. + // Note that for some languages a 'character' could be represented by more than one 'char' + + std::string placeholderText; + for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it ) + { + placeholderText.append( (*it).mText.GetText() ); + } + + return placeholderText ; +} + +void TextInput::SetInitialText(const std::string& initialText) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() ); + + if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted. + { + mPreEditFlag = false; + mIgnoreCommitFlag = true; + } + + SetText( initialText ); + PreEditReset( false ); // Reset keyboard as text changed +} + +void TextInput::SetText(const std::string& initialText) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() ); + + GetStyledTextArray( initialText, mStyledText ); + + if( mStyledText.empty() ) + { + // If the initial text is empty, set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + else + { + mDisplayedTextView.SetText( mStyledText ); + mPlaceHolderSet = false; + } + + GetTextLayoutInfo(); + + mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size(); + + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition( mCursorPosition ); + imfManager.SetSurroundingText( initialText ); + imfManager.NotifyCursorPosition(); + + if( IsScrollEnabled() ) + { + ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) ); + } + + ShowGrabHandleAndSetVisibility( false ); + + RemoveHighlight(); + + DrawCursor(); +} + +void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText ) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" ); + + mDisplayedTextView.SetText( styleText ); + mPlaceHolderSet = false; + + // If text alignment hasn't been manually set by application developer, then we + // automatically determine the alignment based on the content of the text i.e. what + // language the text begins with. + // TODO: This should determine different alignments for each line (broken by '\n') of text. + if(!mOverrideAutomaticAlignment) + { + // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols) + bool leftToRight(true); + + if( !styleText.empty() ) + { + bool breakOut(false); + + for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter ) + { + const Text& text = textIter->mText; + + for( std::size_t i = 0; i < text.GetLength(); ++i ) + { + Character character( text[i] ); + if( character.GetCharacterDirection() != Character::Neutral ) + { + leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight ); + breakOut = true; + break; + } + } + } + } + + // Based on this direction, either left or right align text if not manually set by application developer. + mDisplayedTextView.SetTextAlignment( static_cast( + ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) | + Toolkit::Alignment::VerticalTop ) ); + mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right); + } +} + +void TextInput::SetMaxCharacterLength(std::size_t maxChars) +{ + mMaxStringLength = maxChars; +} + +void TextInput::SetNumberOfLinesLimit(std::size_t maxLines) +{ + DALI_ASSERT_DEBUG( maxLines > 0 ) + + if ( maxLines > 0) + { + mNumberOflinesLimit = maxLines; + } +} + +std::size_t TextInput::GetNumberOfLinesLimit() const +{ + return mNumberOflinesLimit; +} + +std::size_t TextInput::GetNumberOfCharacters() const +{ + return mStyledText.size(); +} + +Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal() +{ + return mInputStartedSignalV2; +} + +Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal() +{ + return mInputFinishedSignalV2; +} + +Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal() +{ + return mCutAndPasteToolBarDisplayedV2; +} + +Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal() +{ + return mStyleChangedSignalV2; +} + +Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal() +{ + return mMaxInputCharactersReachedSignalV2; +} + +Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal() +{ + return mInputTextExceedBoundariesSignalV2; +} + +bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle); + + if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName ) + { + textInput.InputStartedSignal().Connect( tracker, functor ); + } + else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName ) + { + textInput.InputFinishedSignal().Connect( tracker, functor ); + } + else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName ) + { + textInput.StyleChangedSignal().Connect( tracker, functor ); + } + else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName ) + { + textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor ); + } + else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName ) + { + textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint) +{ + if(editMode) + { + // update line height before calculate the actual position. + UpdateLineHeight(); + + if(!mEditModeActive) + { + if( setCursorOnTouchPoint ) + { + // Sets the cursor position for the given touch point. + ReturnClosestIndex( touchPoint, mCursorPosition ); + + // Creates the grab handle. + if( IsGrabHandleEnabled() ) + { + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + + CreateGrabHandle(); + + mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position + mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position + mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET ); + ShowGrabHandleAndSetVisibility( true ); + + // Scrolls the text-view if needed. + if( IsScrollEnabled() ) + { + ScrollTextViewToMakeCursorVisible( cursorPosition ); + } + } + } + else + { + mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string. + } + } + + StartEditMode(); + } + else + { + EndEditMode(); + } +} + +bool TextInput::IsEditable() const +{ + return mEditModeActive; +} + +void TextInput::SetEditOnTouch( bool editOnTouch ) +{ + mEditOnTouch = editOnTouch; +} + +bool TextInput::IsEditOnTouch() const +{ + return mEditOnTouch; +} + +void TextInput::SetTextSelectable( bool textSelectable ) +{ + mTextSelection = textSelectable; +} + +bool TextInput::IsTextSelectable() const +{ + return mTextSelection; +} + +bool TextInput::IsTextSelected() const +{ + return mHighlightMeshActor; +} + +void TextInput::DeSelectText() +{ + RemoveHighlight(); + HidePopup(); + CursorUpdate(); +} + +void TextInput::SetGrabHandleImage(Dali::Image image ) +{ + if (image) + { + CreateGrabHandle(image); + } +} + +void TextInput::SetCursorImage(Dali::Image image, const Vector4& border ) +{ + DALI_ASSERT_DEBUG ( image && "Create cursor image invalid") + + if ( image ) + { + mCursor.SetImage( image ); + mCursor.SetNinePatchBorder( border ); + } +} + +Vector3 TextInput::GetSelectionHandleSize() +{ + return DEFAULT_SELECTION_HANDLE_SIZE; +} + +void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border ) +{ + DALI_ASSERT_DEBUG ( image && "Create cursor image invalid") + + if ( image ) + { + mCursorRTL.SetImage( image); + mCursorRTL.SetNinePatchBorder( border ); + } +} + +void TextInput::EnableGrabHandle(bool toggle) +{ + // enables grab handle with will in turn de-activate magnifier + mGrabHandleEnabled = toggle; +} + +bool TextInput::IsGrabHandleEnabled() +{ + // if false then magnifier will be shown instead. + return mGrabHandleEnabled; +} + +void TextInput::EnableSelectionHandleFlip( bool toggle ) +{ + // Deprecated function. To be removed. + mIsSelectionHandleFlipEnabled = toggle; +} + +bool TextInput::IsSelectionHandleFlipEnabled() +{ + // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen. + return true; +} + +void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin ) +{ + // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed. + Vector3 textInputSize = mDisplayedTextView.GetCurrentSize(); + const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w ); + + mSelectionHandleFlipMargin = margin; +} + +void TextInput::SetBoundingRectangle( const Rect& boundingRectangle ) +{ + // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications. + Vector2 stageSize = Dali::Stage::GetCurrent().GetSize(); + + const float originX = boundingRectangle.x - 0.5f * stageSize.width; + const float originY = boundingRectangle.y - 0.5f * stageSize.height; + + const Vector4 boundary( originX, + originY, + originX + boundingRectangle.width, + originY + boundingRectangle.height ); + + mBoundingRectangleWorldCoordinates = boundary; +} + +const Rect TextInput::GetBoundingRectangle() const +{ + Vector2 stageSize = Dali::Stage::GetCurrent().GetSize(); + + const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width; + const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height; + + RectboundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y); + + return boundingRect; +} + +const Vector4& TextInput::GetSelectionHandleFlipMargin() +{ + return mSelectionHandleFlipMargin; +} + +void TextInput::SetTextColor( const Vector4& color ) +{ + mDisplayedTextView.SetColor( color ); +} + +void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask ) +{ + if( style != mInputStyle ) + { + // different style. + bool emitSignal = false; + + // mask: modify style according to mask, if different emit signal. + const TextStyle oldInputStyle( mInputStyle ); + + // Copy the new style. + mInputStyle.Copy( style, mask ); + + // if style has changed, emit signal. + if( oldInputStyle != mInputStyle ) + { + emitSignal = true; + } + + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + // Changing font point size will require the cursor to be re-sized + DrawCursor(); + + if( emitSignal ) + { + EmitStyleChangedSignal(); + } + } +} + +void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask ) +{ + if ( IsTextSelected() ) + { + const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1; + + if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() ) + { + ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]); + } + + // Keeps the old style to be compared with the new one. + const TextStyle oldInputStyle( mInputStyle ); + + // Copy only those parameters from the style which are set in the mask. + mInputStyle.Copy( style, mask ); + + if( mInputStyle != oldInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } +} + +void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask ) +{ + if( !mStyledText.empty() ) + { + ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 ); + } +} + +TextStyle TextInput::GetStyleAtCursor() const +{ + TextStyle style; + + if ( !mStyledText.empty() && ( mCursorPosition > 0 ) ) + { + DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) ); + + style = mStyledText.at( mCursorPosition-1 ).mStyle; + } + else // No text. + { + style = mInputStyle; + + if ( mInputStyle.GetFontPointSize() < Math::MACHINE_EPSILON_1000 ) + { + Dali::Font defaultFont = Dali::Font::New(); + style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) ); + } + } + + return style; +} + +TextStyle TextInput::GetStyleAt( std::size_t position ) const +{ + DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) ); + + if( position >= mStyledText.size() ) + { + position = mStyledText.size() - 1; + } + + return mStyledText.at( position ).mStyle; +} + +void TextInput::SetTextAlignment( Toolkit::Alignment::Type align ) +{ + mDisplayedTextView.SetTextAlignment( align ); + mOverrideAutomaticAlignment = true; +} + +void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification ) +{ + mDisplayedTextView.SetLineJustification( justification ); + mOverrideAutomaticAlignment = true; +} + +void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ) +{ + mDisplayedTextView.SetFadeBoundary( fadeBoundary ); +} + +const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const +{ + return mDisplayedTextView.GetFadeBoundary(); +} + +Toolkit::Alignment::Type TextInput::GetTextAlignment() const +{ + return mDisplayedTextView.GetTextAlignment(); +} + +void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy ) +{ + mDisplayedTextView.SetMultilinePolicy( policy ); +} + +Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const +{ + return mDisplayedTextView.GetMultilinePolicy(); +} + +void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy ) +{ + mDisplayedTextView.SetWidthExceedPolicy( policy ); +} + +Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const +{ + return mDisplayedTextView.GetWidthExceedPolicy(); +} + +void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy ) +{ + mDisplayedTextView.SetHeightExceedPolicy( policy ); +} + +Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const +{ + return mDisplayedTextView.GetHeightExceedPolicy(); +} + +void TextInput::SetExceedEnabled( bool enable ) +{ + mExceedEnabled = enable; +} + +bool TextInput::GetExceedEnabled() const +{ + return mExceedEnabled; +} + +void TextInput::SetBackground(Dali::Image image ) +{ + // TODO Should add this function and add public api to match. +} + +bool TextInput::OnTouchEvent(const TouchEvent& event) +{ + return false; +} + +bool TextInput::OnKeyEvent(const KeyEvent& event) +{ + switch( event.state ) + { + case KeyEvent::Down: + { + return OnKeyDownEvent(event); + } + break; + + case KeyEvent::Up: + { + return OnKeyUpEvent(event); + } + break; + + default: + { + return false; + } + break; + } +} + +void TextInput::OnKeyInputFocusGained() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" ); + + mEditModeActive = true; + + mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top + + mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position + + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + ImfManager imfManager = ImfManager::Get(); + + // Connect the signals to use in text input. + VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged ); + VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection ); + + // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes. + SetTextDirection(); + + GetTextLayoutInfo(); + + DrawCursor(); + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + Toolkit::TextInput handle( GetOwner() ); + mInputStartedSignalV2.Emit( handle ); + + imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived); + + // Notify that the text editing start. + imfManager.Activate(); + + // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated. + imfManager.SetRestoreAferFocusLost( true ); + + imfManager.SetCursorPosition( mCursorPosition ); + imfManager.NotifyCursorPosition(); + + mClipboard = Clipboard::Get(); // Store handle to clipboard + + // Now in edit mode we can accept string to paste from clipboard + if( Adaptor::IsAvailable() ) + { + ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() ); + if ( notifier ) + { + notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected ); + } + } +} + +void TextInput::OnKeyInputFocusLost() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" ); + + if( mPreEditFlag ) + { + // If key input focus is lost, it removes the + // underline from the last pre-edit text. + RemovePreEditStyle(); + const std::size_t numberOfCharactersDeleted = DeletePreEdit(); + InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted ); + } + + ImfManager imfManager = ImfManager::Get(); + + // The text editing is finished. Therefore the imf manager don't have restore activation. + imfManager.SetRestoreAferFocusLost( false ); + + // Notify that the text editing finish. + imfManager.Deactivate(); + + imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived); + + // Disconnect signal used the text input. + VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection ); + + Toolkit::TextInput handle( GetOwner() ); + mInputFinishedSignalV2.Emit( handle ); + mEditModeActive = false; + mPreEditFlag = false; + RemoveHighlight(); + SetCursorVisibility( false ); + StopCursorBlinkTimer(); + + ShowGrabHandleAndSetVisibility( false ); + + mClipboard.Reset(); + // No longer in edit mode so do not want to receive string from clipboard + if( Adaptor::IsAvailable() ) + { + ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() ); + if ( notifier ) + { + notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected ); + } + Clipboard clipboard = Clipboard::Get(); + clipboard.HideClipboard(); + } +} + +void TextInput::OnControlStageConnection() +{ + Vector2 stageSize = Dali::Stage::GetCurrent().GetSize(); + + if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO ) + { + SetBoundingRectangle( Rect( 0.0f, 0.0f, stageSize.width, stageSize.height )); + } +} + +void TextInput::CreateActiveLayer() +{ + Actor self = Self(); + mActiveLayer = Layer::New(); + + mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER); + mActiveLayer.SetParentOrigin( ParentOrigin::CENTER); + mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION ); + + self.Add( mActiveLayer ); + mActiveLayer.RaiseToTop(); +} + +void TextInput::OnInitialize() +{ + CreateTextViewActor(); + + SetUpTouchEvents(); + + // Create 2 cursors (standard LTR and RTL cursor for when text can be added at + // different positions depending on language) + Image mCursorImage = Image::New( DEFAULT_CURSOR ); + mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER ); + mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER ); + + Actor self = Self(); + self.Add( mCursor ); + self.Add( mCursorRTL ); + + mCursorVisibility = false; + + CreateActiveLayer(); // todo move this so layer only created when needed. + + // Assign names to image actors + mCursor.SetName("mainCursor"); + mCursorRTL.SetName("rtlCursor"); +} + +void TextInput::OnControlSizeSet(const Vector3& targetSize) +{ + mDisplayedTextView.SetSize( targetSize ); + GetTextLayoutInfo(); + mActiveLayer.SetSize(targetSize); +} + +void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + Relayout( mDisplayedTextView, size, container ); + GetTextLayoutInfo(); + + DrawCursor(); +} + +Vector3 TextInput::GetNaturalSize() +{ + Vector3 naturalSize = mDisplayedTextView.GetNaturalSize(); + + if( mEditModeActive && ( Vector3::ZERO == naturalSize ) ) + { + // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height. + naturalSize.height = mLineHeight; + } + + return naturalSize; +} + +float TextInput::GetHeightForWidth( float width ) +{ + float height = mDisplayedTextView.GetHeightForWidth( width ); + + if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) ) + { + // If the height is zero, it means there is no text. Let's return the cursor height. + height = mLineHeight; + } + + return height; +} + +/*end of Virtual methods from parent*/ + +// Private Internal methods + +void TextInput::OnHandlePan(Actor actor, PanGesture gesture) +{ + switch (gesture.state) + { + case Gesture::Started: + // fall through so code not duplicated + case Gesture::Continuing: + { + if (actor == mGrabArea) + { + SetCursorVisibility( true ); + ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea ); + MoveGrabHandle( gesture.displacement ); + HidePopup(); // Do not show popup whilst handle is moving + } + else if (actor == mHandleOneGrabArea) + { + // the displacement in PanGesture is affected by the actor's rotation. + mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x; + mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y; + + MoveSelectionHandle( HandleOne, gesture.displacement ); + + mState = StateDraggingHandle; + HidePopup(); + } + else if (actor == mHandleTwoGrabArea) + { + // the displacement in PanGesture is affected by the actor's rotation. + mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x; + mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y; + + MoveSelectionHandle( HandleTwo, gesture.displacement ); + + mState = StateDraggingHandle; + HidePopup(); + } + } + break; + + case Gesture::Finished: + { + // Revert back to non-pressed selection handle images + if (actor == mGrabArea) + { + mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement ); + SetCursorVisibility( true ); + SetUpPopUpSelection(); + ShowPopup(); + } + if (actor == mHandleOneGrabArea) + { + // the displacement in PanGesture is affected by the actor's rotation. + mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x; + mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y; + + mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement ); + + mSelectionHandleOne.SetImage( mSelectionHandleOneImage ); + mState = StateEdit; + ShowPopupCutCopyPaste(); + } + if (actor == mHandleTwoGrabArea) + { + // the displacement in PanGesture is affected by the actor's rotation. + mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x; + mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y; + + mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement ); + + mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage ); + mState = StateEdit; + ShowPopupCutCopyPaste(); + } + } + break; + default: + break; + } +} + +// Stop the flashing animation so easy to see when moved. +bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch) +{ + if (touch.GetPoint(0).state == TouchPoint::Down) + { + SetCursorVisibility( true ); + StopCursorBlinkTimer(); + } + else if (touch.GetPoint(0).state == TouchPoint::Up) + { + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + } + return false; +} + +// selection handle one +bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch) +{ + if (touch.GetPoint(0).state == TouchPoint::Down) + { + mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed ); + } + else if (touch.GetPoint(0).state == TouchPoint::Up) + { + mSelectionHandleOne.SetImage( mSelectionHandleOneImage ); + } + return false; +} + +// selection handle two +bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch) +{ + if (touch.GetPoint(0).state == TouchPoint::Down) + { + mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed ); + } + else if (touch.GetPoint(0).state == TouchPoint::Up) + { + mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage ); + } + return false; +} + +void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap) +{ + // If text exists then select nearest word. + if ( !mStyledText.empty()) + { + HidePopup(); + + ShowGrabHandleAndSetVisibility( false ); + + + if ( mPreEditFlag ) + { + // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which + // converts the pre-edit word being displayed to a committed word. + if ( !mUnderlinedPriorToPreEdit ) + { + TextStyle style; + style.SetUnderline( false ); + ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 ); + } + mPreEditFlag = false; + mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit. + // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location. + PreEditReset( false ); + } + mCursorPosition = 0; + + mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition(); + ReturnClosestIndex( tap.localPoint, mCursorPosition ); + + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); + + std::size_t start = 0; + std::size_t end = 0; + Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end ); + + SelectText( start, end ); + } + // if no text but clipboard has content then show paste option + if ( mClipboard.NumberOfItems() || !mStyledText.empty() ) + { + ShowPopupCutCopyPaste(); + } + + // If no text and clipboard empty then do nothing +} + +// TODO: Change the function name to be more general. +void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap) +{ + DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false" + , (mEditOnTouch)?"true":"false" + , (mEditModeActive)?"true":"false"); + + if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor ) + { + return; + } + + if( mGrabArea == actor ) + { + if( mPopUpPanel.GetState() == TextInputPopup::StateHidden || mPopUpPanel.GetState() == TextInputPopup::StateHiding ) + { + SetUpPopUpSelection(); + ShowPopup(); + } + + return; + } + + HidePopup(); + RemoveHighlight(); + + mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition(); + + // Initially don't create the grab handle. + bool createGrabHandle = false; + + if ( !mEditModeActive ) + { + // update line height before calculate the actual position. + UpdateLineHeight(); + + // Only start edit mode if TextInput configured to edit on touch + if ( mEditOnTouch ) + { + // Set the initial cursor position in the tap point. + ReturnClosestIndex(tap.localPoint, mCursorPosition ); + + // Create the grab handle. + // TODO Make this a re-usable function. + if ( IsGrabHandleEnabled() ) + { + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + + CreateGrabHandle(); + + mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position + mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position + mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET ); + ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea ); + + } + + // Edit mode started after grab handle created to ensure the signal InputStarted is sent last. + // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created, + // otherwise the Grab handle will be shown when selecting. + + StartEditMode(); + } + } + else + { + // Show the keyboard if it was hidden. + if (!VirtualKeyboard::IsVisible()) + { + VirtualKeyboard::Show(); + } + + // Reset keyboard as tap event has occurred. + // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location. + PreEditReset( true ); + + GetTextLayoutInfo(); + + if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle. + { + // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead. + + ReturnClosestIndex(tap.localPoint, mCursorPosition ); + + DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition ); + + // Notify keyboard so it can 're-capture' word for predictive text. + // As we have done a reset, is this required, expect IMF keyboard to request this information. + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); + + const TextStyle oldInputStyle( mInputStyle ); + + mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position + + DrawCursor(); + + // Create the grab handle. + // Grab handle is created later. + createGrabHandle = true; + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } + } + + if ( createGrabHandle && IsGrabHandleEnabled() ) + { + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + + CreateGrabHandle(); + + mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position + mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position + mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET ); + ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea ); + + } +} + +void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress) +{ + DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" ); + + if(longPress.state == Dali::Gesture::Started) + { + // Start edit mode on long press + if ( !mEditModeActive ) + { + StartEditMode(); + } + + // If text exists then select nearest word. + if ( !mStyledText.empty()) + { + HidePopup(); + + ShowGrabHandleAndSetVisibility( false ); + + + if ( mPreEditFlag ) + { + // PreEdit will be committed here without needing a commit from IMF. Remove pre-edit underline and reset flags which + // converts the pre-edit word being displayed to a committed word. + if ( !mUnderlinedPriorToPreEdit ) + { + TextStyle style; + style.SetUnderline( false ); + ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 ); + } + mPreEditFlag = false; + mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit. + // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location. + PreEditReset( false ); + } + mCursorPosition = 0; + + mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition(); + ReturnClosestIndex( longPress.localPoint, mCursorPosition ); + + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); + + std::size_t start = 0; + std::size_t end = 0; + Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end ); + + SelectText( start, end ); + } + + // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing + if ( mClipboard.NumberOfItems() || !mStyledText.empty() ) + { + ShowPopupCutCopyPaste(); + } + } +} + +void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier ) +{ + const Text clipboardText( notifier.GetContent() ); + PasteText( clipboardText ); + + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + ShowGrabHandleAndSetVisibility( false ); + + + HidePopup(); +} + +bool TextInput::OnPopupButtonPressed( Toolkit::Button button ) +{ + mPopUpPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed ); + + const std::string& name = button.GetName(); + + if(name == OPTION_SELECT_WORD) + { + std::size_t start = 0; + std::size_t end = 0; + Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end ); + + SelectText( start, end ); + } + else if(name == OPTION_SELECT_ALL) + { + SetCursorVisibility(false); + StopCursorBlinkTimer(); + + std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size(); + std::size_t start = 0; + + SelectText( start, end ); + } + else if(name == OPTION_CUT) + { + bool ret = CopySelectedTextToClipboard(); + + if ( ret ) + { + DeleteHighlightedText( true ); + CursorUpdate(); + } + + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + HidePopup(); + } + else if(name == OPTION_COPY) + { + CopySelectedTextToClipboard(); + + RemoveHighlight(); + + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + HidePopup(); + } + else if(name == OPTION_PASTE) + { + const Text retrievedString( mClipboard.GetItem( 0 ) ); // currently can only get first item in clip board, index 0; + + PasteText(retrievedString); + + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + ShowGrabHandleAndSetVisibility( false ); + + + HidePopup(); + } + else if(name == OPTION_CLIPBOARD) + { + // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes + // Hence pass the false parameter for signalFinished. + HidePopup( true, false ); + mClipboard.ShowClipboard(); + } + + return false; +} + +bool TextInput::OnCursorBlinkTimerTick() +{ + // Cursor blinking + mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus ); + if ( mCursorRTLEnabled ) + { + mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus ); + } + mCursorBlinkStatus = !mCursorBlinkStatus; + + return true; +} + +void TextInput::OnPopupHideFinished(TextInputPopup& popup) +{ + popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished ); + + // Change Popup menu to Cut/Copy/Paste if text has been selected. + if(mHighlightMeshActor && mState == StateEdit) + { + ShowPopupCutCopyPaste(); + } +} + +//FIXME this routine needs to be re-written as it contains too many branches. +bool TextInput::OnKeyDownEvent(const KeyEvent& event) +{ + std::string keyName = event.keyPressedName; + std::string keyString = event.keyPressed; + + DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() ); + + // Do not consume "Tab" and "Escape" keys. + if(keyName == "Tab" || keyName == "Escape") + { + // Escape key to end the edit mode + EndEditMode(); + + return false; + } + + HidePopup(); // If Pop-up shown then hides it as editing text. + + // Update Flag, indicates whether to update the text-input contents or not. + // Any key stroke that results in a visual change of the text-input should + // set this flag to true. + bool update(false); + + // Whether to scroll text to cursor position. + // Scroll is needed always the cursor is updated and after the pre-edit is received. + bool scroll = false; + + if (keyName == "Return") + { + if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1 + { + bool preEditFlagPreviouslySet( mPreEditFlag ); + + if (mHighlightMeshActor) + { + // replaces highlighted text with new line + DeleteHighlightedText( false ); + } + mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 ); + + // If we are in pre-edit mode then pressing enter will cause a commit. But the commit string does not include the + // '\n' character so we need to ensure that the immediately following commit knows how it occurred. + if ( mPreEditFlag ) + { + mCommitByKeyInput = true; + } + + // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit. + if ( preEditFlagPreviouslySet && !mPreEditFlag ) + { + mPreEditFlag = true; + mIgnoreCommitFlag = false; + } + + update = true; + } + else + { + RemoveHighlight(); + } + } // Return + else if ( keyName == "space" ) + { + if ( mHighlightMeshActor ) + { + // Some text is selected so erase it before adding space. + DeleteHighlightedText( true ); + update = true; + } + + mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0); + + // If we are in pre-edit mode then pressing the space-bar will cause a commit. But the commit string does not include the + // ' ' character so we need to ensure that the immediately following commit knows how it occurred. + if ( mPreEditFlag ) + { + mCommitByKeyInput = true; + } + + update = true; + } // space + else if (keyName == "BackSpace") + { + if ( mHighlightMeshActor ) + { + // Some text is selected so erase it + DeleteHighlightedText( true ); + update = true; + } + else + { + if ( mCursorPosition > 0 ) + { + DeleteCharacter( mCursorPosition ); + update = true; + } + } + } // BackSpace + else if (keyName == "Right") + { + AdvanceCursor(); + RemoveHighlight(); + } + else if (keyName == "Left") + { + AdvanceCursor(true); + RemoveHighlight(); + } + else // event is a character + { + // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case + if ( !keyString.empty() ) + { + if ( mHighlightMeshActor ) + { + // replaces highlighted text with new character + DeleteHighlightedText( false ); + } + + + // Received key String + mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 ); + update = true; + } + } + + // If key event has resulted in a change in the text/cursor, then trigger a relayout of text + // as this is a costly operation. + if(update) + { + CursorUpdate(); + } + + if(update || scroll) + { + if( IsScrollEnabled() ) + { + // Calculates the new cursor position (in actor coordinates) + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition ); + + ScrollTextViewToMakeCursorVisible( cursorPosition ); + } + } + + return true; +} + +bool TextInput::OnKeyUpEvent(const KeyEvent& event) +{ + std::string keyName = event.keyPressedName; + std::string keyString = event.keyPressed; + + DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() ); + + // The selected text become deselected when the key code is DALI_KEY_BACK. + if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") ) + { + DeSelectText(); + return true; + } + + return false; +} + +void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition ) +{ + // Updates the stored scroll position. + mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition(); + + const Vector3& controlSize = GetControlSize(); + Size cursorSize( CURSOR_THICKNESS, 0.f ); + + // Updates the cursor and grab handle position and visibility. + if( mGrabHandle || mCursor ) + { + cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height; + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + + mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize ); + + mActualGrabHandlePosition = cursorPosition.GetVectorXY(); + + if( mGrabHandle ) + { + ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea ); + mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET ); + } + + if( mCursor ) + { + mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea ); + mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET ); + } + } + + // Updates the selection handles and highlighted text position and visibility. + if( mSelectionHandleOne && mSelectionHandleTwo ) + { + const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition); + const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition); + cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height; + const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize ); + cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height; + const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize ); + + mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY(); + mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY(); + + mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible ); + mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible ); + mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset ); + mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset ); + + if( mHighlightMeshActor ) + { + mHighlightMeshActor.SetVisible( true ); + UpdateHighlight(); + } + } +} + +void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition ) +{ + // Scroll the text to make the cursor visible. + const Size cursorSize( CURSOR_THICKNESS, + GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height ); + + // Need to scroll the text to make the cursor visible and to cover the whole text-input area. + + const Vector3& controlSize = GetControlSize(); + + // Calculates the new scroll position. + Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset; + if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) ) + { + scrollOffset.x += cursorPosition.x; + } + + if( cursorPosition.y - cursorSize.height < 0.f ) + { + scrollOffset.y += ( cursorPosition.y - cursorSize.height ); + } + else if( cursorPosition.y > controlSize.height ) + { + scrollOffset.y += cursorPosition.y; + } + + // Sets the new scroll position. + SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work. + SetScrollPosition( scrollOffset ); +} + +void TextInput::StartScrollTimer() +{ + if( !mScrollTimer ) + { + mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL ); + mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick ); + } + + if( !mScrollTimer.IsRunning() ) + { + mScrollTimer.Start(); + } +} + +void TextInput::StopScrollTimer() +{ + if( mScrollTimer ) + { + mScrollTimer.Stop(); + } +} + +bool TextInput::OnScrollTimerTick() +{ + // TODO: need to set the new style accordingly the new handle position. + + if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) ) + { + // nothing to do if all handles are invisible or doesn't exist. + return true; + } + + // Text scrolling + + // Choose between the grab handle or the selection handles. + Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition; + std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition; + Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition; + + std::size_t newCursorPosition = 0; + ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition ); + + // Whether the handle's position is different of the previous one and in the case of the selection handle, + // the new selection handle's position needs to be different of the other one. + const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition : + ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) : + ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition ); + + if( differentSelectionHandles ) + { + handlePosition = newCursorPosition; + + const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition ); + + Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY(); + + Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition(); + scrollPosition += scrollDelta; + SetScrollPosition( scrollPosition ); + + if( mDisplayedTextView.IsScrollPositionTrimmed() ) + { + StopScrollTimer(); + } + + currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY(); + } + + actualHandlePosition.x += mScrollDisplacement.x; + actualHandlePosition.y += mScrollDisplacement.y; + + return true; +} + +// Public Internal Methods (public for testing purpose) + +void TextInput::SetUpTouchEvents() +{ + if ( !mTapDetector ) + { + mTapDetector = TapGestureDetector::New(); + // Attach the actors and connect the signal + mTapDetector.Attach(Self()); + + // As contains children which may register for tap the default control detector is not used. + mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap); + } + + if ( !mDoubleTapDetector ) + { + mDoubleTapDetector = TapGestureDetector::New(); + mDoubleTapDetector.SetTapsRequired( 2 ); + mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap); + + // Only attach and detach the actor to the double tap detector when we enter/leave edit mode + // so that we do not, unnecessarily, have a double tap request all the time + } + + if ( !mPanGestureDetector ) + { + mPanGestureDetector = PanGestureDetector::New(); + mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan); + } + + if ( !mLongPressDetector ) + { + mLongPressDetector = LongPressGestureDetector::New(); + mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress); + mLongPressDetector.Attach(Self()); + } +} + +void TextInput::CreateTextViewActor() +{ + mDisplayedTextView = Toolkit::TextView::New(); + mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT); + mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord); + mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original ); + mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original ); + mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left ); + mDisplayedTextView.SetTextAlignment( static_cast( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) ); + mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) ); + mDisplayedTextView.SetSizePolicy( Control::Fixed, Control::Fixed ); + + mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled ); + + Self().Add( mDisplayedTextView ); +} + +// Start a timer to initiate, used by the cursor to blink. +void TextInput::StartCursorBlinkTimer() +{ + if ( !mCursorBlinkTimer ) + { + mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL ); + mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick ); + } + + if ( !mCursorBlinkTimer.IsRunning() ) + { + mCursorBlinkTimer.Start(); + } +} + +// Start a timer to initiate, used by the cursor to blink. +void TextInput::StopCursorBlinkTimer() +{ + if ( mCursorBlinkTimer ) + { + mCursorBlinkTimer.Stop(); + } +} + +void TextInput::StartEditMode() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" ); + + if(!mEditModeActive) + { + SetKeyInputFocus(); + } + + if ( mDoubleTapDetector ) + { + mDoubleTapDetector.Attach( Self() ); + } +} + +void TextInput::EndEditMode() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" ); + + ClearKeyInputFocus(); + + if ( mDoubleTapDetector ) + { + mDoubleTapDetector.Detach( Self() ); + } +} + +void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength ) +{ + if ( mPreEditFlag && ( preEditStringLength > 0 ) ) + { + mUnderlinedPriorToPreEdit = mInputStyle.GetUnderline(); + TextStyle style; + style.SetUnderline( true ); + ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 ); + } +} + +void TextInput::RemovePreEditStyle() +{ + if ( !mUnderlinedPriorToPreEdit ) + { + TextStyle style; + style.SetUnderline( false ); + SetActiveStyle( style, TextStyle::UNDERLINE ); + } +} + +// IMF related methods + + +ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent ) +{ + bool update( false ); + bool preeditResetRequired ( false ); + + if (imfEvent.eventName != ImfManager::GETSURROUNDING ) + { + HidePopup(); // If Pop-up shown then hides it as editing text. + } + + switch ( imfEvent.eventName ) + { + case ImfManager::PREEDIT: + { + mIgnoreFirstCommitFlag = false; + + // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case + if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) ) + { + // replaces highlighted text with new character + DeleteHighlightedText( false ); + } + + preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset ); + + if( IsScrollEnabled() ) + { + // Calculates the new cursor position (in actor coordinates) + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition ); + ScrollTextViewToMakeCursorVisible( cursorPosition ); + } + + update = true; + + break; + } + case ImfManager::COMMIT: + { + if( mIgnoreFirstCommitFlag ) + { + // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard). + mIgnoreFirstCommitFlag = false; + } + else + { + // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited. + + // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case + if ( mHighlightMeshActor && (!imfEvent.predictiveString.empty()) ) + { + // replaces highlighted text with new character + DeleteHighlightedText( false ); + } + + // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is + // not needed, one such scenario is when the pre-edit word is too long to fit. + if ( !mIgnoreCommitFlag ) + { + update = CommitReceived( imfEvent.predictiveString ); + } + else + { + mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon. + } + } + + if( update ) + { + if( IsScrollEnabled() ) + { + // Calculates the new cursor position (in actor coordinates) + const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition ); + + ScrollTextViewToMakeCursorVisible( cursorPosition ); + } + } + break; + } + case ImfManager::DELETESURROUNDING: + { + DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n", + (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast( mCursorPosition+imfEvent.cursorOffset) ); + + mPreEditFlag = false; + + std::size_t toDelete = 0; + std::size_t numberOfCharacters = 0; + + if( mHighlightMeshActor ) + { + // delete highlighted text. + toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ); + numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete; + } + else + { + if( std::abs( imfEvent.cursorOffset ) < mCursorPosition ) + { + toDelete = mCursorPosition + imfEvent.cursorOffset; + } + if( toDelete + imfEvent.numberOfChars > mStyledText.size() ) + { + numberOfCharacters = mStyledText.size() - toDelete; + } + else + { + numberOfCharacters = imfEvent.numberOfChars; + } + } + DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition); + DeleteRange( toDelete, numberOfCharacters ); + + mCursorPosition = toDelete; + mNumberOfSurroundingCharactersDeleted = numberOfCharacters; + + DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition); + break; + } + case ImfManager::GETSURROUNDING: + { + // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with + // the next key pressed. Instead the Select function sets the cursor position and surrounding text. + if (! ( mHighlightMeshActor || mSelectingText ) ) + { + std::string text( GetText() ); + DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition ); + + imfManager.SetCursorPosition( mCursorPosition ); + imfManager.SetSurroundingText( text ); + } + + if( 0 != mNumberOfSurroundingCharactersDeleted ) + { + mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted ); + mNumberOfSurroundingCharactersDeleted = 0; + + if( mStyledText.empty() ) + { + // Styled text is empty, so set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + } + break; + } + case ImfManager::VOID: + { + DALI_ASSERT_DEBUG( false ); + } + } // end switch + + ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired ); + + return callbackData; +} + +bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset ) +{ + mPreserveCursorPosition = false; // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position. + + DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n", + mPreserveCursorPosition, mCursorPosition, mPreEditFlag ); + + bool preeditResetRequest ( false ); + + if( mPreEditFlag ) // Already in pre-edit state. + { + if( mStyledText.size() >= mMaxStringLength ) + { + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n"); + // Cannot fit these characters into field, clear pre-edit. + if ( !mUnderlinedPriorToPreEdit ) + { + TextStyle style; + style.SetUnderline( false ); + ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 ); + } + mIgnoreCommitFlag = true; + preeditResetRequest = false; // this will reset the keyboard's predictive suggestions. + mPreEditFlag = false; + EmitMaxInputCharactersReachedSignal(); + } + else + { + // delete existing pre-edit string + const std::size_t numberOfCharactersToReplace = DeletePreEdit(); + + // Store new pre-edit string + mPreEditString.SetText( keyString ); + + if ( keyString.empty() ) + { + mPreEditFlag = false; + mCursorPosition = mPreEditStartPosition; + + if( mStyledText.empty() ) + { + // Styled text is empty, so set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + else + { + mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace ); + } + GetTextLayoutInfo(); + } + else + { + // Insert new pre-edit string. InsertAt updates the size and position table. + mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace ); + // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min. + mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength ); + ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength ); + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition); + } + // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'. + DrawCursor(); + } + } + else // mPreEditFlag not set + { + if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit. + { + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n"); + // new pre-edit so move into pre-edit state by setting flag + mPreEditFlag = true; + mPreEditString.SetText( keyString ); // store new pre-edit string + mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from + mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 ); + // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min. + mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength ); + ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength ); + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition); + + // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'. + DrawCursor(); + } + else + { + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n"); + } + } + + return preeditResetRequest; +} + +bool TextInput::CommitReceived(const std::string& keyString ) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n", + mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" ); + + bool update( false ); + + RemovePreEditStyle(); + + const std::size_t styledTextSize( mStyledText.size() ); + if( styledTextSize >= mMaxStringLength ) + { + // Cannot fit these characters into field, clear pre-edit. + if ( mPreEditFlag ) + { + mIgnoreCommitFlag = true; + mPreEditFlag = false; + } + EmitMaxInputCharactersReachedSignal(); + } + else + { + if( mPreEditFlag ) + { + // delete existing pre-edit string + const std::size_t numberOfCharactersToReplace = DeletePreEdit(); + mPreEditFlag = false; + + DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n", + (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition ); + + if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit. + { + // No need to update cursor position as Cursor location given by touch. + InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace ); + mPreserveCursorPosition = false; + } + else + { + // Cursor not set by touch so needs to be re-positioned to input more text + mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this + + // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key. + if ( mCommitByKeyInput ) + { + mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() ); + mCommitByKeyInput = false; + } + } + + if ( mSelectTextOnCommit ) + { + SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection ); + } + + update = true; + } + else // mPreEditFlag not set + { + if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored. + { + if( mStyledText.empty() && mPlaceHolderSet ) + { + // If the styled text is empty and the placeholder text is set, it needs to be cleared. + mDisplayedTextView.SetText( "" ); + mNumberOfSurroundingCharactersDeleted = 0; + mPlaceHolderSet = false; + } + mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted ); + update = true; + mNumberOfSurroundingCharactersDeleted = 0; + } + else + { + mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored. + } + } + } + + mSelectTextOnCommit = false; + + DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n", + mCursorPosition, mPreEditFlag, (update)?"true":"false" ); + + return update; +} + +// End of IMF related methods + +std::size_t TextInput::DeletePreEdit() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false"); + + DALI_ASSERT_DEBUG( mPreEditFlag ); + + const std::size_t preEditStringLength = mPreEditString.GetLength(); + const std::size_t styledTextSize = mStyledText.size(); + + std::size_t endPosition = mPreEditStartPosition + preEditStringLength; + + // Prevents erase items outside mStyledText bounds. + if( mPreEditStartPosition > styledTextSize ) + { + DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" ); + mPreEditStartPosition = styledTextSize; + } + + if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) ) + { + DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" ); + endPosition = styledTextSize; + } + + mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition ); + + // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters, + // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to + // reuse glyphs. + // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call. + + return preEditStringLength; +} + +void TextInput::PreEditReset( bool preserveCursorPosition ) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n", + preserveCursorPosition, mCursorPosition); + + // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor. + mPreserveCursorPosition = preserveCursorPosition; + + // Reset incase we are in a pre-edit state. + ImfManager::Get().Reset(); // Will trigger a commit message +} + +void TextInput::CursorUpdate() +{ + DrawCursor(); + + std::string text( GetText() ); + ImfManager imfManager = ImfManager::Get(); + imfManager.SetSurroundingText( text ); // Notifying IMF of a cursor change triggers a surrounding text request so updating it now. + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); +} + +/* Delete highlighted characters redisplay*/ +void TextInput::DeleteHighlightedText( bool inheritStyle ) +{ + DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + + if(mHighlightMeshActor) + { + mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ); + + MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition; + MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ); + + // Get the styled text of the characters to be deleted as it may be needed if + // the "exceed the text-input's boundaries" option is disabled. + MarkupProcessor::StyledTextArray styledCharactersToDelete; + + styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end ); + + mStyledText.erase( start, end ); // erase range of characters + + // Remove text from TextView. + + if( mStyledText.empty() ) + { + // Styled text is empty, so set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + else + { + const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition; + + mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters ); + + // It may happen than after removing a white space or a new line character, + // two words merge, this new word could be big enough to not fit in its + // current line, so moved to the next one, and make some part of the text to + // exceed the text-input's boundary. + if( !mExceedEnabled ) + { + // Get the new text layout after removing some characters. + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + + // Get text-input's size. + const Vector3& size = GetControlSize(); + + if( ( mTextLayoutInfo.mTextSize.width > size.width ) || + ( mTextLayoutInfo.mTextSize.height > size.height ) ) + { + mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete ); + + mStyledText.insert( mStyledText.begin() + mCursorPosition, + styledCharactersToDelete.begin(), + styledCharactersToDelete.end() ); + } + } + } + GetTextLayoutInfo(); + + RemoveHighlight(); + + if( inheritStyle ) + { + const TextStyle oldInputStyle( mInputStyle ); + + mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } + } +} + +void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters ) +{ + DALI_ASSERT_DEBUG( start <= mStyledText.size() ); + DALI_ASSERT_DEBUG( !mStyledText.empty() ); + + DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false"); + + + if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) ) + { + MarkupProcessor::StyledTextArray::iterator itStart = mStyledText.begin() + start; + MarkupProcessor::StyledTextArray::iterator itEnd = mStyledText.begin() + start + ncharacters; + + mStyledText.erase(itStart, itEnd); + + // update the selection handles if they are visible. + if( mHighlightMeshActor ) + { + std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition ); + std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition ); + + if( minHandle >= start + ncharacters ) + { + minHandle -= ncharacters; + } + else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) ) + { + minHandle = start; + } + + if( maxHandle >= start + ncharacters ) + { + maxHandle -= ncharacters; + } + else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) ) + { + maxHandle = start; + } + } + + // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit. + } + + DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false"); + + // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change. + // This is a performance decision as the use of this function often means the text is being replaced or just deleted. + // Mean we do not re-draw the text more than we have too. +} + +/* Delete character at current cursor position and redisplay*/ +void TextInput::DeleteCharacter( std::size_t positionToDelete ) +{ + // Ensure positionToDelete is not out of bounds. + DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() ); + DALI_ASSERT_DEBUG( !mStyledText.empty() ); + DALI_ASSERT_DEBUG( positionToDelete > 0 ); + + DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete ); + + + if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() ) // don't try to delete if no characters left of cursor + { + MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + positionToDelete - 1; + + // Get the styled text of the character to be deleted as it may be needed if + // the "exceed the text-input's boundaries" option is disabled. + const MarkupProcessor::StyledText styledCharacterToDelete( *it ); + + mStyledText.erase(it); // erase the character left of positionToDelete + + if( mStyledText.empty() ) + { + // Styled text is empty, so set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + else + { + mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 ); + + const Character characterToDelete = styledCharacterToDelete.mText[0]; + + // It may happen than after removing a white space or a new line character, + // two words merge, this new word could be big enough to not fit in its + // current line, so moved to the next one, and make some part of the text to + // exceed the text-input's boundary. + if( !mExceedEnabled ) + { + if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() ) + { + // Get the new text layout after removing one character. + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + + // Get text-input's size. + const Vector3& size = GetControlSize(); + + if( ( mTextLayoutInfo.mTextSize.width > size.width ) || + ( mTextLayoutInfo.mTextSize.height > size.height ) ) + { + MarkupProcessor::StyledTextArray array; + array.push_back( styledCharacterToDelete ); + mDisplayedTextView.InsertTextAt( positionToDelete - 1, array ); + + mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete ); + } + } + } + } + GetTextLayoutInfo(); + + ShowGrabHandleAndSetVisibility( false ); + + mCursorPosition = positionToDelete -1; + + const TextStyle oldInputStyle( mInputStyle ); + + mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } +} + +/*Insert new character into the string and (optionally) redisplay text-input*/ +std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace ) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition ); + + // Ensure insertionPosition is not out of bounds. + DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() ); + + bool textExceedsMaximunNumberOfCharacters = false; + bool textExceedsBoundary = false; + std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary ); + + ShowGrabHandleAndSetVisibility( false ); + + if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary ) + { + if( mPreEditFlag ) + { + mIgnoreCommitFlag = true; + mPreEditFlag = false; + // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared. + // Although can not directly call PreEditReset() as it will cause a recursive emit loop. + } + + if( textExceedsMaximunNumberOfCharacters ) + { + EmitMaxInputCharactersReachedSignal(); + } + + if( textExceedsBoundary ) + { + EmitInputTextExceedsBoundariesSignal(); + PreEditReset( false ); + } + } + + return insertedStringLength; +} + +ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border ) +{ + ImageActor cursor; + + if ( cursorImage ) + { + cursor = ImageActor::New( cursorImage ); + } + else + { + cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) ); + } + + cursor.SetStyle(ImageActor::STYLE_NINE_PATCH); + cursor.SetNinePatchBorder( border ); + + cursor.SetParentOrigin(ParentOrigin::TOP_LEFT); + cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER); + cursor.SetVisible(false); + + return cursor; +} + +void TextInput::AdvanceCursor(bool reverse, std::size_t places) +{ + // As cursor is not moving due to grab handle, handle should be hidden. + ShowGrabHandleAndSetVisibility( false ); + + bool cursorPositionChanged = false; + if (reverse) + { + if ( mCursorPosition >= places ) + { + mCursorPosition = mCursorPosition - places; + cursorPositionChanged = true; + } + } + else + { + if ((mCursorPosition + places) <= mStyledText.size()) + { + mCursorPosition = mCursorPosition + places; + cursorPositionChanged = true; + } + } + + if( cursorPositionChanged ) + { + const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 ); + + const TextStyle oldInputStyle( mInputStyle ); + mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position. + + DrawCursor(); + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); + } +} + +void TextInput::DrawCursor(const std::size_t nthChar) +{ + // Get height of cursor and set its size + Size size( CURSOR_THICKNESS, 0.0f ); + if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty()) + { + size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height; + } + else + { + // Measure Font so know how big text will be if no initial text to measure. + size.height = mLineHeight; + } + + mCursor.SetSize(size); + + // If the character is italic then the cursor also tilts. + mCursor.SetRotation( mInputStyle.GetItalics() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS ); + + DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ); + + if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ) + { + Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValid; // Alternate cursor validity flag. + bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid ); + + SetAltCursorEnabled( altPositionValid ); + + if(!altPositionValid) + { + mCursor.SetPosition( position + UI_OFFSET ); + } + else + { + size.height *= 0.5f; + mCursor.SetSize(size); + mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) ); + + // TODO: change this cursor pos, to be the one where the cursor is sourced from. + Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ); + size.height = rowSize.height * 0.5f; + mCursorRTL.SetSize(size); + mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) ); + } + + if( IsScrollEnabled() ) + { + // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled. + mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() ); + } + } // EditMode +} + +void TextInput::SetAltCursorEnabled( bool enabled ) +{ + mCursorRTLEnabled = enabled; + mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled ); +} + +void TextInput::SetCursorVisibility( bool visible ) +{ + mCursorVisibility = visible; + mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea ); + mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled ); +} + +void TextInput::CreateGrabHandle( Dali::Image image ) +{ + if ( !mGrabHandle ) + { + if ( !image ) + { + mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE); + } + else + { + mGrabHandleImage = image; + } + + mGrabHandle = ImageActor::New(mGrabHandleImage); + mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT); + mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER); + + mGrabHandle.SetDrawMode(DrawMode::OVERLAY); + + ShowGrabHandleAndSetVisibility( false ); + + CreateGrabArea( mGrabHandle ); + + mActiveLayer.Add(mGrabHandle); + } +} + +void TextInput::CreateGrabArea( Actor& parent ) +{ + mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mGrabArea.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor + mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown); + mTapDetector.Attach( mGrabArea ); + mPanGestureDetector.Attach( mGrabArea ); + + parent.Add(mGrabArea); +} + +Vector3 TextInput::MoveGrabHandle( const Vector2& displacement ) +{ + Vector3 actualHandlePosition; + + if (mGrabHandle) + { + mActualGrabHandlePosition.x += displacement.x; + mActualGrabHandlePosition.y += displacement.y; + + // Grab handle should jump to the nearest character and take cursor with it + std::size_t newCursorPosition = 0; + ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition ); + + actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ); + + bool handleVisible = true; + + if( IsScrollEnabled() ) + { + const Vector3 controlSize = GetControlSize(); + const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) ); + // Scrolls the text if the handle is not in a visible position + handleVisible = IsPositionInsideBoundaries( actualHandlePosition, + cursorSize, + controlSize ); + + if( handleVisible ) + { + StopScrollTimer(); + mCurrentHandlePosition = actualHandlePosition; + mScrollDisplacement = Vector2::ZERO; + } + else + { + if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) ) + { + mScrollDisplacement.x = -SCROLL_SPEED; + } + else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) ) + { + mScrollDisplacement.x = SCROLL_SPEED; + } + if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) ) + { + mScrollDisplacement.y = -SCROLL_SPEED; + } + else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) ) + { + mScrollDisplacement.y = SCROLL_SPEED; + } + StartScrollTimer(); + } + } + + if( handleVisible && // Only redraw cursor and do updates if position changed + ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true). + { + mCursorPosition = newCursorPosition; + + mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET ); + + const TextStyle oldInputStyle( mInputStyle ); + + mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position + + CursorUpdate(); // Let keyboard know the new cursor position so can 're-capture' for prediction. + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } + } + + return actualHandlePosition; +} + +void TextInput::ShowGrabHandle( bool visible ) +{ + if ( IsGrabHandleEnabled() ) + { + if( mGrabHandle ) + { + mGrabHandle.SetVisible( mGrabHandleVisibility ); + } + StartMonitoringStageForTouch(); + } +} + +void TextInput::ShowGrabHandleAndSetVisibility( bool visible ) +{ + mGrabHandleVisibility = visible; + ShowGrabHandle( visible ); +} + +// Callbacks connected to be Property notifications for Boundary checking. + +void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source) +{ + mIsSelectionHandleOneFlipped = true; + mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f ); + mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT); +} + +void TextInput::OnReturnToLeftBoundary(PropertyNotification& source) +{ + mIsSelectionHandleOneFlipped = false; + mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f ); + mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT); +} + +void TextInput::OnRightBoundaryExceeded(PropertyNotification& source) +{ + mIsSelectionHandleTwoFlipped = true; + mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f ); + mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT); +} + +void TextInput::OnReturnToRightBoundary(PropertyNotification& source) +{ + mIsSelectionHandleTwoFlipped = false; + mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f ); + mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT); +} + +// todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions. +void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source) +{ + mSelectionHandleOne.SetOpacity(0.0f); +} + +void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source) +{ + mSelectionHandleOne.SetOpacity(1.0f); +} + +void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source) +{ + mSelectionHandleTwo.SetOpacity(0.0f); +} + +void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source) +{ + mSelectionHandleTwo.SetOpacity(1.0f); +} + +// End of Callbacks connected to be Property notifications for Boundary checking. + +void TextInput::SetUpHandlePropertyNotifications() +{ + /* Property notifications for handles exceeding the boundary and returning back within boundary */ + + Vector3 handlesize = GetSelectionHandleSize(); + + // Exceeding horizontal boundary + PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) ); + leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded ); + + PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) ); + rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded ); + + // Within horizontal boundary + PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) ); + leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary ); + + PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) ); + rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary ); + + // Exceeding vertical boundary + PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y, + OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y, + mBoundingRectangleWorldCoordinates.w - handlesize.y ) ); + verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary ); + + PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y, + OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y, + mBoundingRectangleWorldCoordinates.w - handlesize.y ) ); + verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary ); + + // Within vertical boundary + PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y, + InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y, + mBoundingRectangleWorldCoordinates.w - handlesize.y ) ); + verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary ); + + PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y, + InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y, + mBoundingRectangleWorldCoordinates.w - handlesize.y ) ); + verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary ); +} + +void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage, Dali::Image handleTwoImage ) +{ + mSelectionHandleOnePosition = start; + mSelectionHandleTwoPosition = end; + + if ( !mSelectionHandleOne ) + { + // create normal and pressed images + mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE ); + mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED ); + + mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage ); + mSelectionHandleOne.SetName("SelectionHandleOne"); + mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. + mIsSelectionHandleOneFlipped = false; + mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text + + mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea"); + + mHandleOneGrabArea.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor + mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + mTapDetector.Attach( mHandleOneGrabArea ); + mPanGestureDetector.Attach( mHandleOneGrabArea ); + + mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched); + + mSelectionHandleOne.Add( mHandleOneGrabArea ); + mActiveLayer.Add( mSelectionHandleOne ); + } + + if ( !mSelectionHandleTwo ) + { + // create normal and pressed images + mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO ); + mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED ); + + mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage ); + mSelectionHandleTwo.SetName("SelectionHandleTwo"); + mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mIsSelectionHandleTwoFlipped = false; + mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text + + mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea"); + mHandleTwoGrabArea.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) ); // grab area to be larger than text actor + mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + mTapDetector.Attach( mHandleTwoGrabArea ); + mPanGestureDetector.Attach( mHandleTwoGrabArea ); + + mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched); + + mSelectionHandleTwo.Add( mHandleTwoGrabArea ); + + mActiveLayer.Add( mSelectionHandleTwo ); + } + + SetUpHandlePropertyNotifications(); + + // update table as text may have changed. + GetTextLayoutInfo(); + + mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ); + mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ); + + mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset ); + mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset ); + + // Calculates and set the visibility if the scroll mode is enabled. + bool isSelectionHandleOneVisible = true; + bool isSelectionHandleTwoVisible = true; + if( IsScrollEnabled() ) + { + const Vector3& controlSize( GetControlSize() ); + isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize ); + isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize ); + mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible ); + mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible ); + } + + CreateHighlight(); // function will only create highlight if not already created. +} + +Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement ) +{ + Vector3 actualHandlePosition; + + if ( mSelectionHandleOne && mSelectionHandleTwo ) + { + const Vector3& controlSize = GetControlSize(); + + Size cursorSize( CURSOR_THICKNESS, 0.f ); + + // Get a reference of the wanted selection handle (handle one or two). + Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition; + + // Get a reference for the current position of the handle and a copy of its pair + std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition; + const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition; + + // Get a handle of the selection handle actor + ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo; + + // Selection handles should jump to the nearest character + std::size_t newHandlePosition = 0; + ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition ); + + actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition ); + + bool handleVisible = true; + + if( IsScrollEnabled() ) + { + mCurrentSelectionId = handleId; + + cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height; + // Restricts the movement of the grab handle inside the boundaries of the text-input. + handleVisible = IsPositionInsideBoundaries( actualHandlePosition, + cursorSize, + controlSize ); + + if( handleVisible ) + { + StopScrollTimer(); + mCurrentSelectionHandlePosition = actualHandlePosition; + mScrollDisplacement = Vector2::ZERO; + } + else + { + if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) ) + { + mScrollDisplacement.x = -SCROLL_SPEED; + } + else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) ) + { + mScrollDisplacement.x = SCROLL_SPEED; + } + if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) ) + { + mScrollDisplacement.y = -SCROLL_SPEED; + } + else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) ) + { + mScrollDisplacement.y = SCROLL_SPEED; + } + StartScrollTimer(); + } + } + + if ( handleVisible && // Ensure the handle is visible. + ( newHandlePosition != pairSelectionHandlePosition ) && // Ensure handle one is not the same position as handle two. + ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved. + { + currentSelectionHandlePosition = newHandlePosition; + + Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset; + selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset ); + + UpdateHighlight(); + + if ( handleId == HandleOne ) + { + const TextStyle oldInputStyle( mInputStyle ); + + // Set Active Style to that of first character in selection + if( mSelectionHandleOnePosition < mStyledText.size() ) + { + mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle; + } + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + } + } + } + + return actualHandlePosition; // Returns Handle position passed in if new value not assigned. +} + +void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId) +{ + + const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition; + ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo; + + if ( selectionHandleActor ) + { + const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition ); + Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset; + selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset ); + + if( IsScrollEnabled() ) + { + const Size cursorSize( CURSOR_THICKNESS, + GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height ); + selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition, + cursorSize, + GetControlSize() ) ); + } + } +} + +std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const +{ + // Note: we're allowing caller to request a logical position of size (i.e. end of string) + // For now the visual position of end of logical string will be end of visual string. + DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ); + + return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size(); +} + +void TextInput::GetVisualTextSelection(std::vector& selectedVisualText, std::size_t startSelection, std::size_t endSelection) +{ + std::vector::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin(); + std::vector::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection); + std::vector::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection); + std::vector::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end(); + + selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ); + + // Deselect text prior to startSelectionIt + for(;it!=startSelectionIt;++it) + { + selectedVisualText[*it] = false; + } + + // Select text from startSelectionIt -> endSelectionIt + for(;it!=endSelectionIt;++it) + { + selectedVisualText[*it] = true; + } + + // Deselect text after endSelection + for(;it!=end;++it) + { + selectedVisualText[*it] = false; + } + + selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false ); +} + +// Calculate the dimensions of the quads they will make the highlight mesh +TextInput::HighlightInfo TextInput::CalculateHighlightInfo() +{ + // At the moment there is no public API to modify the block alignment option. + const bool blockAlignEnabled = true; + + mNewHighlightInfo.mQuadList.clear(); // clear last quad information. + + if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() ) + { + Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(); + Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); + + // Get vector of flags representing characters that are selected (true) vs unselected (false). + std::vector selectedVisualText; + GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + std::vector::iterator selectedIt(selectedVisualText.begin()); + std::vector::iterator selectedEndIt(selectedVisualText.end()); + + SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text. + float rowLeft = 0.0f; + float rowRight = 0.0f; + // Keep track of the TextView's min/max extents. Should be able to query this from TextView. + float maxRowLeft = std::numeric_limits::max(); + float maxRowRight = 0.0f; + + Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it; + + // Scan through entire text. + while(it != end) + { + // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection. + + Toolkit::TextView::CharacterLayoutInfo& charInfo(*it); + bool charSelected( false ); + if( selectedIt != selectedEndIt ) + { + charSelected = *selectedIt++; + } + + if(selectionState == SelectionNone) + { + if(charSelected) + { + selectionState = SelectionStarted; + rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x; + rowRight = rowLeft + charInfo.mSize.width; + } + } + else if(selectionState == SelectionStarted) + { + // break selection on: + // 1. new line causing selection break. (\n or wordwrap) + // 2. character not selected. + if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD || + !charSelected) + { + // finished selection. + // TODO: TextView should have a table of visual rows, and each character a reference to the row + // that it resides on. That way this enumeration is not necessary. + Vector2 min, max; + if(lastIt->mIsNewLineChar) + { + // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line. + lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 ); + } + const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) ); + maxRowLeft = std::min(maxRowLeft, min.x); + maxRowRight = std::max(maxRowRight, max.x); + float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y; + float rowTop = rowBottom - rowSize.height; + + // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards + if(charSelected && blockAlignEnabled) + { + rowRight = std::numeric_limits::max(); + } + mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom ); + + selectionState = SelectionNone; + + // Still selected? start a new selection + if( charSelected ) + { + // if block-align mode then set rowLeft to min, so it can be clamped afterwards + rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x; + rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width; + selectionState = SelectionStarted; + } + } + else + { + // build up highlight(s) with this selection data. + rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft ); + rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight ); + } + } + + lastIt = it++; + } + + // If reached end, and still on selection, then close selection. + if(it == end) + { + if(selectionState == SelectionStarted) + { + // finished selection. + Vector2 min, max; + if(lastIt->mIsNewLineChar) + { + lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 ); + } + const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) ); + maxRowLeft = std::min(maxRowLeft, min.x); + maxRowRight = std::max(maxRowRight, max.x); + float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y; + float rowTop = rowBottom - rowSize.height; + mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom ); + } + } + + // Get the top left and bottom right corners. + const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ); + const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height ); + const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height ); + + // Clamp quads so they appear to clip to borders of the whole text. + mNewHighlightInfo.Clamp2D( topLeft, bottomRight ); + + // For block-align align Further Clamp quads to max left and right extents + if(blockAlignEnabled) + { + // BlockAlign: Will adjust highlight to block: + // i.e. + // H[ello] (top row right = max of all rows right) + // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right) + // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right) + // [text] (bottom row left = min of all rows left) + // (common in SMS messaging selection) + // + // As opposed to the default which is tight text highlighting. + // H[ello] + // [this] + // [is some] + // [text] + // (common in regular text editors/web browser selection) + + mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) ); + } + + // Finally clamp quads again so they don't exceed the boundry of the control. + const Vector3& controlSize = GetControlSize(); + mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) ); + } // end if + + return mNewHighlightInfo; +} + +void TextInput::UpdateHighlight() +{ +// Construct a Mesh with a texture to be used as the highlight 'box' for selected text +// +// Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads. +// +// [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ] +// [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ] +// [ BOTTOM] [ MIDDLE ] [ MIDDLE ] +// [BOTTOM] [ MIDDLE ] +// [BOTTOM] +// +// Each quad is created as 2 triangles. +// Middle is just 1 quad regardless of its size. +// +// (0,0) (0,0) +// 0* *2 0* *2 +// TOP TOP +// 3* *1 3* *1 +// 4* *1 4* *6 +// MIDDLE BOTTOM +// 6* *5 7* *5 +// 6* *8 +// BOTTOM +// 9* *7 +// + + if ( mHighlightMeshActor ) + { + // vertex and triangle buffers should always be present if MeshActor is alive. + HighlightInfo newHighlightInfo = CalculateHighlightInfo(); + MeshData::VertexContainer vertices; + Dali::MeshData::FaceIndices faceIndices; + + if( !newHighlightInfo.mQuadList.empty() ) + { + std::vector::iterator iter = newHighlightInfo.mQuadList.begin(); + std::vector::iterator endIter = newHighlightInfo.mQuadList.end(); + + // vertex position defaults to (0 0 0) + MeshData::Vertex vertex; + // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor. + vertex.nZ = 1.0f; + + for(std::size_t v = 0; iter != endIter; ++iter,v+=4 ) + { + // Add each quad geometry (a sub-selection) to the mesh data. + + // 0-----1 + // |\ | + // | \ A | + // | \ | + // | B \ | + // | \| + // 2-----3 + + QuadCoordinates& quad = *iter; + // top-left (v+0) + vertex.x = quad.min.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // top-right (v+1) + vertex.x = quad.max.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // bottom-left (v+2) + vertex.x = quad.min.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // bottom-right (v+3) + vertex.x = quad.max.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // triangle A (3, 1, 0) + faceIndices.push_back( v + 3 ); + faceIndices.push_back( v + 1 ); + faceIndices.push_back( v ); + + // triangle B (0, 2, 3) + faceIndices.push_back( v ); + faceIndices.push_back( v + 2 ); + faceIndices.push_back( v + 3 ); + + mMeshData.SetFaceIndices( faceIndices ); + } + + BoneContainer bones(0); // passed empty as bones not required + mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial ); + mHighlightMesh.UpdateMeshData(mMeshData); + } + } +} + +void TextInput::ClearPopup() +{ + mPopUpPanel.Clear(); +} + +void TextInput::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption) +{ + mPopUpPanel.AddOption(name, caption, icon, finalOption); +} + +void TextInput::SetPopupPosition(const Vector3& position) +{ + mPopUpPanel.Self().SetPosition( position ); +} + +void TextInput::HidePopup(bool animate, bool signalFinished ) +{ + if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown ) ) + { + mPopUpPanel.Hide( animate ); + + if( animate && signalFinished ) + { + mPopUpPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished ); + } + } +} + +void TextInput::ShowPopup(bool animate) +{ + Vector3 position; + + if(mHighlightMeshActor && mState == StateEdit) + { + Vector3 topHandle; + Size rowSize; + // When text is selected, show popup above top handle (and text), or below bottom handle. + // topHandle: referring to the top most point of the handle or the top line of selection. + if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) + { + topHandle = mSelectionHandleOneActualPosition; + rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition ); + } + else + { + topHandle = mSelectionHandleTwoActualPosition; + rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition ); + } + topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height; + position = Vector3(topHandle.x, topHandle.y, 0.0f); + + // bottomHandle: referring to the bottom most point of the handle or the bottom line of selection. + Vector3 bottomHandle; + bottomHandle.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y ); + bottomHandle.y += GetSelectionHandleSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET; + mPopUpPanel.SetAlternativeOffset(Vector2(0.0f, bottomHandle.y - topHandle.y)); + } + else + { + // When no text is selected, show popup at world position of grab handle or cursor + position = GetActualPositionFromCharacterPosition( mCursorPosition ); + const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition ); + position.y -= rowSize.height; + // if can't be positioned above, then position below row. + Vector2 alternativePopUpPosition( 0.0f, position.y ); // default if no grab handle + if ( mGrabHandle ) + { + alternativePopUpPosition.y = rowSize.height + ( mGrabHandle.GetCurrentSize().height * DEFAULT_GRAB_HANDLE_RELATIVE_SIZE.y ) ; + // If grab handle enabled then position pop-up below the grab handle. + } + mPopUpPanel.SetAlternativeOffset( alternativePopUpPosition ); + } + + // reposition popup above the desired cursor posiiton. + Vector3 textViewSize = mDisplayedTextView.GetCurrentSize(); + textViewSize.z = 0.0f; + // World position = world position of ParentOrigin of cursor (i.e. top-left corner of TextView) + cursor position; + Vector3 worldPosition = mDisplayedTextView.GetCurrentWorldPosition() - (textViewSize * 0.5f) + position; + + SetPopupPosition( worldPosition ); + + // Show popup + mPopUpPanel.Show(animate); + StartMonitoringStageForTouch(); + + mPopUpPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed ); +} + +void TextInput::ShowPopupCutCopyPaste() +{ + ClearPopup(); + // Check the selected text is whole text or not. + if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) ) + { + Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL ); + AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon ); + } + + if ( !mStyledText.empty() ) + { + Image cutIcon = Image::New( DEFAULT_ICON_CUT ); + Image copyIcon = Image::New( DEFAULT_ICON_COPY ); + AddPopupOption( OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon ); + AddPopupOption( OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true ); + } + + if(mClipboard.NumberOfItems()) + { + Image pasteIcon = Image::New( DEFAULT_ICON_PASTE ); + Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD ); + AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon ); + AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ); + } + + mPopUpPanel.Hide(false); + ShowPopup(); +} + +void TextInput::SetUpPopUpSelection() +{ + ClearPopup(); + + // If no text exists then don't offer to select + if ( !mStyledText.empty() ) + { + Image selectIcon = Image::New( DEFAULT_ICON_SELECT ); + Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL ); + AddPopupOption( OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon ); + AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon ); + } + // if clipboard has valid contents then offer paste option + if( mClipboard.NumberOfItems() ) + { + Image pasteIcon = Image::New( DEFAULT_ICON_PASTE ); + Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD ); + AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true ); + AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ); + } + + mPopUpPanel.Hide(false); +} + +bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex ) +{ + bool found = false; + closestIndex = 0; + + std::vector matchedCharacters; + bool lastRightToLeftChar(false); /// RTL state of previous character encountered (character on the left of touch point) + bool rightToLeftChar(false); /// RTL state of current character encountered (character on the right of touch point) + float glyphIntersection(0.0f); /// Glyph intersection, the point between the two nearest characters touched. + + const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset ); + + if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) + { + float closestYdifference = std::numeric_limits::max(); + std::size_t lineOffset = 0; /// Keep track of position of the first character on the matched line of interest. + std::size_t numberOfMatchedCharacters = 0; + + // 1. Find closest character line to y part of source, create vector of all entries in that Y position + // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines. + + for( std::vector::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it ) + { + const Toolkit::TextView::CharacterLayoutInfo& info( *it ); + float baselinePosition = info.mPosition.y - info.mDescender; + + if( info.mIsVisible ) + { + // store difference between source y point and the y position of the current character + float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) ); + + if( currentYdifference < closestYdifference ) + { + // closest so far; store this difference and clear previous matchedCharacters as no longer closest + lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(); + closestYdifference = currentYdifference; + matchedCharacters.clear(); + numberOfMatchedCharacters = 0; // reset count + } + + // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array. + if( fabsf( closestYdifference - currentYdifference ) < CHARACTER_THRESHOLD ) + { + // ignore new line character. + if( !info.mIsNewLineChar ) + { + matchedCharacters.push_back( info ); + numberOfMatchedCharacters++; + } + } + } + } // End of loop checking each character's y position in the character layout table + + // Check if last character is a newline, if it is + // then need pretend there is an imaginary line afterwards, + // and check if user is touching below previous line. + const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] ); + + if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) ) + { + closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size(); + } + else + { + std::vector::const_iterator it = matchedCharacters.begin(); + std::vector::const_iterator endIt = matchedCharacters.end(); + + bool matched( false ); + + // 2 Iterate through matching list of y positions and find closest matching X position. + for( ; it != endIt; ++it ) + { + const Toolkit::TextView::CharacterLayoutInfo& info( *it ); + + if( info.mIsVisible ) + { + // stop when on left side of character's center. + const float characterMidPointPosition = info.mPosition.x + ( info.mSize.width * 0.5f ) ; + if( sourceScrollOffset.x < characterMidPointPosition ) + { + if(info.mIsRightToLeftCharacter) + { + rightToLeftChar = true; + } + glyphIntersection = info.mPosition.x; + matched = true; + break; + } + + lastRightToLeftChar = info.mIsRightToLeftCharacter; + } + } + + if( it == endIt ) + { + rightToLeftChar = lastRightToLeftChar; + } + + std::size_t matchCharacterIndex = it - matchedCharacters.begin(); + closestIndex = lineOffset + matchCharacterIndex; + + mClosestCursorPositionEOL = false; // reset + if ( it == endIt && !matched ) + { + mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character. + } + + // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse) + if( rightToLeftChar && lastRightToLeftChar ) + { + --closestIndex; // (-1 = numeric_limits::max()) + } + } + } + + // closestIndex is the visual index, need to convert it to the logical index + if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() ) + { + if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) + { + // Checks for situations where user is touching between LTR and RTL + // characters. To identify if the user means the end of a LTR string + // or the beginning of an RTL string, and vice versa. + if( closestIndex > 0 ) + { + if( rightToLeftChar && !lastRightToLeftChar ) + { + // [LTR] [RTL] + // |..|..| + // AAA BBB + // A: In this touch range, the user is indicating that they wish to place + // the cursor at the end of the LTR text. + // B: In this touch range, the user is indicating that they wish to place + // the cursor at the end of the RTL text. + + // Result of touching A area: + // [.....LTR]|[RTL......]+ + // + // |: primary cursor (for typing LTR chars) + // +: secondary cursor (for typing RTL chars) + + // Result of touching B area: + // [.....LTR]+[RTL......]| + // + // |: primary cursor (for typing RTL chars) + // +: secondary cursor (for typing LTR chars) + + if( sourceScrollOffset.x < glyphIntersection ) + { + --closestIndex; + } + } + else if( !rightToLeftChar && lastRightToLeftChar ) + { + if( sourceScrollOffset.x < glyphIntersection ) + { + --closestIndex; + } + } + } + + closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex]; + // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor + // one further ahead + if( rightToLeftChar && !lastRightToLeftChar ) + { + ++closestIndex; + } + } + else if( closestIndex == numeric_limits::max() ) // -1 RTL (after last arabic character on line) + { + closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size(); + } + else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line) + { + closestIndex = 0; + } + } + + return found; +} + +float TextInput::GetLineJustificationPosition() const +{ + const Vector3& size = mDisplayedTextView.GetCurrentSize(); + Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment(); + float alignmentOffset = 0.f; + + // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings. + if( alignment & Toolkit::Alignment::HorizontalLeft ) + { + alignmentOffset = 0.f; + } + else if( alignment & Toolkit::Alignment::HorizontalCenter ) + { + alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width ); + } + else if( alignment & Toolkit::Alignment::HorizontalRight ) + { + alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width; + } + + Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification(); + float justificationOffset = 0.f; + + switch( justification ) + { + case Toolkit::TextView::Left: + { + justificationOffset = 0.f; + break; + } + case Toolkit::TextView::Center: + { + justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width; + break; + } + case Toolkit::TextView::Right: + { + justificationOffset = mTextLayoutInfo.mTextSize.width; + break; + } + case Toolkit::TextView::Justified: + { + justificationOffset = 0.f; + break; + } + default: + { + DALI_ASSERT_ALWAYS( false ); + } + } // end switch + + return alignmentOffset + justificationOffset; +} + +Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const +{ + /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current. + A newline character is not inserted in this case */ + + DALI_ASSERT_DEBUG( !(characterPosition <= 0 )); + + Vector3 cursorPosition; + + Toolkit::TextView::CharacterLayoutInfo currentCharInfo; + + if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) + { + // end character so use + currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ]; + cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ; + } + else + { + currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ]; + } + + Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1]; + + // If previous character on a different line then use current characters position + if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender ) - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 ) + { + if ( mClosestCursorPositionEOL ) + { + cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ; + } + else + { + cursorPosition = Vector3(currentCharInfo.mPosition); + } + } + else + { + // Previous character is on same line so use position of previous character plus it's width. + cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ; + } + + return cursorPosition; +} + +Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const +{ + bool direction(false); + Vector3 alternatePosition; + bool alternatePositionValid(false); + + return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid ); +} + +Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const +{ + Vector3 cursorPosition( 0.f, 0.f, 0.f ); + + alternatePositionValid = false; + directionRTL = false; + + if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() ) + { + std::size_t visualCharacterPosition; + + // When cursor is not at beginning, consider possibility of + // showing 2 cursors. (whereas at beginning we only ever show one cursor) + if(characterPosition > 0) + { + // Cursor position should be the end of the last character. + // If the last character is LTR, then the end is on the right side of the glyph. + // If the last character is RTL, then the end is on the left side of the glyph. + visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ]; + + if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible ) + { + visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition ); + } + + Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ]; + if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() ) + { + // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled. + const Vector3& size = GetControlSize(); + + if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height ) + { + --visualCharacterPosition; + } + info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ]; + } + + if(!info.mIsNewLineChar) + { + cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap. + } + else + { + // When cursor points to first character on new line, position cursor at the start of this glyph. + if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size()) + { + std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ]; + const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ]; + const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f ); + + cursorPosition.x = infoNext.mPosition.x + start; + cursorPosition.y = infoNext.mPosition.y; + } + else + { + // If cursor points to the end of text, then can only position + // cursor where the new line starts based on the line-justification position. + cursorPosition.x = GetLineJustificationPosition(); + + if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size()) + { + // If this is after the last character, then we can assume that the new cursor + // should be exactly one row below the current row. + + const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1)); + cursorPosition.y = info.mPosition.y + rowRect.height; + } + else + { + // If this is not after last character, then we can use this row's height. + // should be exactly one row below the current row. + + const Size rowRect(GetRowRectFromCharacterPosition(characterPosition)); + cursorPosition.y = info.mPosition.y + rowRect.height; + } + } + } + + directionRTL = info.mIsRightToLeftCharacter; + + // 1. When the cursor is neither at the beginning or the end, + // we can show multiple cursors under situations when the cursor is + // between RTL and LTR text... + if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size()) + { + std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1; + + DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size()); + const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ]; + + if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter) + { + // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL) + // Text: [...LTR...]|[...RTL...] + // Cursor pos: ^ + // Alternate cursor pos: ^ + // In which case we need to display an alternate cursor for the RTL text. + + alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width; + alternatePosition.y = infoAlt.mPosition.y; + alternatePositionValid = true; + } + else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter) + { + // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL) + // Text: |[...RTL...] [...LTR....] + // Cursor pos: ^ + // Alternate cursor pos: ^ + // In which case we need to display an alternate cursor for the RTL text. + + alternatePosition.x = infoAlt.mPosition.x; + alternatePosition.y = infoAlt.mPosition.y; + alternatePositionValid = true; + } + } + else + { + // 2. When the cursor is at the end of the text, + // and we have multi-directional text, + // we can also consider showing mulitple cursors. + // The rule here is: + // If first and last characters on row are different + // Directions, then two cursors need to be displayed. + + // Get first logical glyph on row + std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 ); + + std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ]; + const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ]; + + if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter) + { + // For text Starting as LTR and ending as RTL. End cursor position is as follows: + // Text: [...LTR...]|[...RTL...] + // Cursor pos: ^ + // Alternate cursor pos: ^ + // In which case we need to display an alternate cursor for the RTL text, this cursor + // should be at the end of the given line. + + const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ]; + alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width; + alternatePosition.y = infoAlt.mPosition.y; + alternatePositionValid = true; + } + else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL + { + // For text Starting as RTL and ending as LTR. End cursor position is as follows: + // Text: |[...RTL...] [...LTR....] + // Cursor pos: ^ + // Alternate cursor pos: ^ + // In which case we need to display an alternate cursor for the RTL text. + + const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ]; + alternatePosition.x = infoAlt.mPosition.x; + alternatePosition.y = infoAlt.mPosition.y; + alternatePositionValid = true; + } + } + } // characterPosition > 0 + else if(characterPosition == 0) + { + // When the cursor position is at the beginning, it should be at the start of the current character. + // If the current character is LTR, then the start is on the right side of the glyph. + // If the current character is RTL, then the start is on the left side of the glyph. + visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ]; + + if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible ) + { + visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition ); + } + + const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ]; + const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f); + + cursorPosition.x = info.mPosition.x + start; + cursorPosition.y = info.mPosition.y; + directionRTL = info.mIsRightToLeftCharacter; + } + } + else + { + // If the character table is void, place the cursor accordingly the text alignment. + const Vector3& size = GetControlSize(); + + Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment(); + float alignmentOffset = 0.f; + + // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings. + if( alignment & Toolkit::Alignment::HorizontalLeft ) + { + alignmentOffset = 0.f; + } + else if( alignment & Toolkit::Alignment::HorizontalCenter ) + { + alignmentOffset = 0.5f * ( size.width ); + } + else if( alignment & Toolkit::Alignment::HorizontalRight ) + { + alignmentOffset = size.width; + } + + // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings. + cursorPosition.x = alignmentOffset; + + // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings. + if( alignment & Toolkit::Alignment::VerticalTop ) + { + cursorPosition.y = mLineHeight; + } + else if( alignment & Toolkit::Alignment::VerticalCenter ) + { + cursorPosition.y = 0.5f * ( size.height + mLineHeight ); + } + else if( alignment & Toolkit::Alignment::VerticalBottom ) + { + cursorPosition.y = size.height; + } + } + + cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x; + cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y; + if( alternatePositionValid ) + { + alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x; + alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y; + } + + return cursorPosition; +} + +std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const +{ + // scan string from current position to beginning of current line to note direction of line + while(logicalPosition) + { + logicalPosition--; + std::size_t visualPosition = GetVisualPosition(logicalPosition); + if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar) + { + logicalPosition++; + break; + } + } + + return logicalPosition; +} + +Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const +{ + Vector2 min, max; + + return GetRowRectFromCharacterPosition( characterPosition, min, max ); +} + +Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const +{ + // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height. + if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) + { + min = Vector2::ZERO; + max = Vector2(0.0f, mLineHeight); + return max; + } + + // TODO: This info should be readily available from text-view, we should not have to search hard for it. + Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(); + Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); + + // If cursor is pointing to end of line, then start from last character. + characterPosition = std::min( characterPosition, static_cast(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) ); + + Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition; + + // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted. + if( !it->mIsVisible ) + { + characterPosition = FindVisibleCharacter( Left, characterPosition ); + it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition; + } + + // Scan characters left and right of cursor, stopping when end of line/string reached or + // y position greater than threshold of reference line. + + // 1. scan left until we reach the beginning or a different line. + Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it; + float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD; + // min-x position is the left-most char's left (x) + // max-x position is the right-most char's right (x) + // min-y position is the minimum of all character's top (y) + // max-y position is the maximum of all character's bottom (y+height) + min.y = validCharIt->mPosition.y; + max.y = validCharIt->mPosition.y + validCharIt->mSize.y; + + while(true) + { + validCharIt = it; + min.y = std::min(min.y, validCharIt->mPosition.y); + max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y); + + if(it == begin) + { + break; + } + + --it; + + if( (it->mPosition.y < referenceLine) || + (it->mIsNewLineChar) || + (!it->mIsVisible) ) + { + break; + } + } + + // info refers to the first character on this line. + min.x = validCharIt->mPosition.x; + + // 2. scan right until we reach end or a different line + it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition; + referenceLine = it->mPosition.y + CHARACTER_THRESHOLD; + + while(it != end) + { + if( (it->mPosition.y > referenceLine) || + (it->mIsNewLineChar) || + (!it->mIsVisible) ) + { + break; + } + + validCharIt = it; + min.y = std::min(min.y, validCharIt->mPosition.y); + max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y); + + ++it; + } + + DALI_ASSERT_DEBUG ( validCharIt != end && "validCharIt invalid") + + if ( validCharIt != end ) + { + // info refers to the last character on this line. + max.x = validCharIt->mPosition.x + validCharIt->mSize.x; + } + + return Size( max.x - min.x, max.y - min.y ); +} + +bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const +{ + Actor popUpPanel = mPopUpPanel.GetRootActor(); + + if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) ) + { + return true; + } + else + { + Dali::Actor parent( touchedActor.GetParent() ); + + if ( parent ) + { + return WasTouchedCheck( parent ); + } + } + + return false; +} + +void TextInput::StartMonitoringStageForTouch() +{ + Stage stage = Stage::GetCurrent(); + stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched ); +} + +void TextInput::EndMonitoringStageForTouch() +{ + Stage stage = Stage::GetCurrent(); + stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched ); +} + +void TextInput::OnStageTouched(const TouchEvent& event) +{ + if( event.GetPointCount() > 0 ) + { + if ( TouchPoint::Down == event.GetPoint(0).state ) + { + const Actor touchedActor(event.GetPoint(0).hitActor); + + bool popUpShown( false ); + + if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown ) ) + { + popUpShown = true; + } + + bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor )); + + if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched ) + { + EndMonitoringStageForTouch(); + HidePopup( true, false ); + } + + if ( ( IsGrabHandleEnabled() && mGrabHandle ) && !textInputTouched ) + { + EndMonitoringStageForTouch(); + ShowGrabHandleAndSetVisibility( false ); + } + } + } +} + +void TextInput::SelectText(std::size_t start, std::size_t end) +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false", + IsGrabHandleEnabled()?"true":"false", + start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() ); + DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" ); + DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" ); + + StartMonitoringStageForTouch(); + + if ( mEditModeActive ) // Only allow text selection when in edit mode + { + // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight. + mSelectingText = true; + + ImfManager imfManager = ImfManager::Get(); + mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text. + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.SetSurroundingText( GetText() ); + imfManager.NotifyCursorPosition(); + // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead. + + // Hide grab handle when selecting. + ShowGrabHandleAndSetVisibility( false ); + + if( start != end ) // something to select + { + SetCursorVisibility( false ); + StopCursorBlinkTimer(); + + CreateSelectionHandles(start, end); + UpdateHighlight(); + + const TextStyle oldInputStyle( mInputStyle ); + mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position. + + if( oldInputStyle != mInputStyle ) + { + // Updates the line height accordingly with the input style. + UpdateLineHeight(); + + EmitStyleChangedSignal(); + } + + HidePopup(); + } + + mSelectingText = false; + } +} + +MarkupProcessor::StyledTextArray TextInput::GetSelectedText() +{ + MarkupProcessor::StyledTextArray currentSelectedText; + + if ( IsTextSelected() ) + { + MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + + for(; it != end; ++it) + { + MarkupProcessor::StyledText& styledText( *it ); + currentSelectedText.push_back( styledText ); + } + } + return currentSelectedText; +} + +void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end) +{ + const std::size_t beginIndex = std::min( begin, end ); + const std::size_t endIndex = std::max( begin, end ); + + // Apply the style + MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex ); + + // Create a styled text array used to replace the text into the text-view. + MarkupProcessor::StyledTextArray text; + text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 ); + + mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text ); + GetTextLayoutInfo(); + + if( IsScrollEnabled() ) + { + // Need to set the scroll position as the text's size may have changed. + ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) ); + } + + ShowGrabHandleAndSetVisibility( false ); + + DrawCursor(); + + UpdateHighlight(); + + // Set Handle positioning as the new style may have repositioned the characters. + SetSelectionHandlePosition(HandleOne); + SetSelectionHandlePosition(HandleTwo); +} + +void TextInput::KeyboardStatusChanged(bool keyboardShown) +{ + // Just hide the grab handle when keyboard is hidden. + if (!keyboardShown ) + { + ShowGrabHandleAndSetVisibility( false ); + + // If the keyboard is not now being shown, then hide the popup panel + mPopUpPanel.Hide( true ); + } +} + +// Removes highlight and resumes edit mode state +void TextInput::RemoveHighlight() +{ + DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n"); + + if ( mHighlightMeshActor ) + { + if ( mSelectionHandleOne ) + { + mActiveLayer.Remove( mSelectionHandleOne ); + mSelectionHandleOne.Reset(); + mSelectionHandleOneOffset.x = 0.0f; + } + if ( mSelectionHandleTwo ) + { + mActiveLayer.Remove( mSelectionHandleTwo ); + mSelectionHandleTwo.Reset(); + mSelectionHandleTwoOffset.x = 0.0f; + } + + mNewHighlightInfo.mQuadList.clear(); + + Self().Remove( mHighlightMeshActor ); + + SetCursorVisibility( true ); + StartCursorBlinkTimer(); + + mHighlightMeshActor.Reset(); + // NOTE: We cannot dereference mHighlightMesh, due + // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly. + + HidePopup(); + } + + mSelectionHandleOnePosition = 0; + mSelectionHandleTwoPosition = 0; +} + +void TextInput::CreateHighlight() +{ + if ( !mHighlightMeshActor ) + { + mMeshData = MeshData( ); + mMeshData.SetHasNormals( true ); + + mCustomMaterial = Material::New("CustomMaterial"); + mCustomMaterial.SetDiffuseColor( LIGHTBLUE ); + + mMeshData.SetMaterial( mCustomMaterial ); + + mHighlightMesh = Mesh::New( mMeshData ); + + mHighlightMeshActor = MeshActor::New( mHighlightMesh ); + mHighlightMeshActor.SetName( "HighlightMeshActor" ); + mHighlightMeshActor.SetInheritShaderEffect( false ); + mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET ); + mHighlightMeshActor.SetAffectedByLighting(false); + + Self().Add(mHighlightMeshActor); + } +} + + +bool TextInput::CopySelectedTextToClipboard() +{ + mCurrentCopySelecton.clear(); + + mCurrentCopySelecton = GetSelectedText(); + + std::string stringToStore; + + /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce + * a marked up string. + */ + MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end()); + MarkupProcessor::GetPlainString( selectedText, stringToStore ); + bool success = mClipboard.SetItem( stringToStore ); + return success; +} + +void TextInput::PasteText( const Text& text ) +{ + // Update Flag, indicates whether to update the text-input contents or not. + // Any key stroke that results in a visual change of the text-input should + // set this flag to true. + bool update = false; + if( mHighlightMeshActor ) + { + /* if highlighted, delete entire text, and position cursor at start of deleted text. */ + mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition); + + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition( mCursorPosition ); + imfManager.NotifyCursorPosition(); + DeleteHighlightedText( true ); + update = true; + } + + bool textExceedsMaximunNumberOfCharacters = false; + bool textExceedsBoundary = false; + + std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary ); + + mCursorPosition += insertedStringLength; + ImfManager imfManager = ImfManager::Get(); + imfManager.SetCursorPosition ( mCursorPosition ); + imfManager.NotifyCursorPosition(); + + update = update || ( insertedStringLength > 0 ); + if( update ) + { + CursorUpdate(); + } + + if( insertedStringLength < text.GetLength() ) + { + EmitMaxInputCharactersReachedSignal(); + } + + if( textExceedsBoundary ) + { + EmitInputTextExceedsBoundariesSignal(); + } +} + +void TextInput::SetTextDirection() +{ + // Put the cursor to the right if we are empty and an RTL language is being used. + if ( mStyledText.empty() ) + { + VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() ); + + // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center + // alignment as we do not want to set the text direction if we've been asked to be in the center. + // + // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to + // set vertical alignment but are being forced to set the horizontal alignment as well with the + // current API. + int alignment( mDisplayedTextView.GetTextAlignment() & + ( Toolkit::Alignment::VerticalTop | + Toolkit::Alignment::VerticalCenter | + Toolkit::Alignment::VerticalBottom | + Toolkit::Alignment::HorizontalCenter ) ); + Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() ); + + // If our alignment is in the center, then do not change. + if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) ) + { + alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight; + } + + // If our justification is in the center, then do not change. + if ( justification != Toolkit::TextView::Center ) + { + justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right; + } + + mDisplayedTextView.SetTextAlignment( static_cast(alignment) ); + mDisplayedTextView.SetLineJustification( justification ); + } +} + +void TextInput::UpdateLineHeight() +{ + Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) ); + mLineHeight = font.GetLineHeight(); + + // If the height exceed policy is shrink or exceed the boundaries of the text-input is not allowed, then modify the line height is needed. + + const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty(); + + if( !mExceedEnabled || shrink ) + { + mLineHeight = std::min( mLineHeight, GetControlSize().height ); + } +} + +std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const +{ + std::size_t position = 0; + + const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size(); + + switch( direction ) + { + case Left: + { + position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable ); + + if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible ) + { + position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable ); + } + break; + } + case Right: + { + position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable ); + if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible ) + { + position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable ); + } + break; + } + case ByEnd: + { + position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable ); + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." ); + } + } + + return position; +} + +void TextInput::SetSortModifier( float depthOffset ) +{ + if(mDisplayedTextView) + { + mDisplayedTextView.SetSortModifier(depthOffset); + } +} + +void TextInput::SetSnapshotModeEnabled( bool enable ) +{ + if(mDisplayedTextView) + { + mDisplayedTextView.SetSnapshotModeEnabled( enable ); + } +} + +bool TextInput::IsSnapshotModeEnabled() const +{ + bool snapshotEnabled = false; + + if(mDisplayedTextView) + { + snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled(); + } + + return snapshotEnabled; +} + +void TextInput::SetScrollEnabled( bool enable ) +{ + if( mDisplayedTextView ) + { + mDisplayedTextView.SetScrollEnabled( enable ); + } + + if( !enable ) + { + // Don't set cursor's and handle's visibility to false if they are outside the + // boundaries of the text-input. + mIsCursorInScrollArea = true; + mIsGrabHandleInScrollArea = true; + if( mSelectionHandleOne && mSelectionHandleTwo ) + { + mSelectionHandleOne.SetVisible( true ); + mSelectionHandleTwo.SetVisible( true ); + + if( mHighlightMeshActor ) + { + mHighlightMeshActor.SetVisible( true ); + } + } + } +} + +bool TextInput::IsScrollEnabled() const +{ + bool scrollEnabled = false; + + if( mDisplayedTextView ) + { + scrollEnabled = mDisplayedTextView.IsScrollEnabled(); + } + + return scrollEnabled; +} + +void TextInput::SetScrollPosition( const Vector2& position ) +{ + if( mDisplayedTextView ) + { + mDisplayedTextView.SetScrollPosition( position ); + } +} + +Vector2 TextInput::GetScrollPosition() const +{ + Vector2 scrollPosition; + + if( mDisplayedTextView ) + { + scrollPosition = mDisplayedTextView.GetScrollPosition(); + } + + return scrollPosition; +} + +std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary ) +{ + // determine number of characters that we can write to style text buffer, this is the insertStringLength + std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() ); + textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength(); + + // Add style to the new input text. + MarkupProcessor::StyledTextArray textToInsert; + for( std::size_t i = 0; i < insertedStringLength; ++i ) + { + const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle ); + textToInsert.push_back( newStyledCharacter ); + } + + //Insert text to the TextView. + const bool emptyTextView = mStyledText.empty(); + if( emptyTextView && mPlaceHolderSet ) + { + // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text. + mDisplayedTextView.SetText( textToInsert ); + } + else + { + if( 0 == numberOfCharactersToReplace ) + { + mDisplayedTextView.InsertTextAt( position, textToInsert ); + } + else + { + mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert ); + } + } + mPlaceHolderSet = false; + + if( textToInsert.empty() ) + { + // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text. + GetTextLayoutInfo(); + } + else + { + // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet. + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + } + + textExceedsBoundary = false; + + if( !mExceedEnabled ) + { + const Vector3& size = GetControlSize(); + + if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) ) + { + // If new text does not fit within TextView + mDisplayedTextView.RemoveTextFrom( position, insertedStringLength ); + // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text. + GetTextLayoutInfo(); + textExceedsBoundary = true; + insertedStringLength = 0; + } + + if( textExceedsBoundary ) + { + // Add the part of the text which fits on the text-input. + + // Split the text which doesn't fit in two halves. + MarkupProcessor::StyledTextArray firstHalf; + MarkupProcessor::StyledTextArray secondHalf; + SplitText( textToInsert, firstHalf, secondHalf ); + + // Clear text. This text will be filled with the text inserted. + textToInsert.clear(); + + // Where to insert the text. + std::size_t positionToInsert = position; + + bool end = text.GetLength() <= 1; + while( !end ) + { + // Insert text and check ... + const std::size_t textLength = firstHalf.size(); + mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf ); + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + + if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) ) + { + // Inserted text doesn't fit. + + // Remove inserted text + mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength ); + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + + // The iteration finishes when only one character doesn't fit. + end = textLength <= 1; + + if( !end ) + { + // Prepare next two halves for next iteration. + MarkupProcessor::StyledTextArray copyText = firstHalf; + SplitText( copyText, firstHalf, secondHalf ); + } + } + else + { + // Text fits. + + // store text to be inserted in mStyledText. + textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() ); + + // Increase the inserted characters counter. + insertedStringLength += textLength; + + // Prepare next two halves for next iteration. + MarkupProcessor::StyledTextArray copyText = secondHalf; + SplitText( copyText, firstHalf, secondHalf ); + + // Update where next text has to be inserted + positionToInsert += textLength; + } + } + } + } + + if( textToInsert.empty() && emptyTextView ) + { + // No character has been added and the text-view was empty. + // Set the placeholder text. + mDisplayedTextView.SetText( mStyledPlaceHolderText ); + mPlaceHolderSet = true; + } + else + { + MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position; + mStyledText.insert( it, textToInsert.begin(), textToInsert.end() ); + mPlaceHolderSet = false; + } + + return insertedStringLength; +} + +void TextInput::GetTextLayoutInfo() +{ + if( mStyledText.empty() ) + { + // The text-input has no text, clear the text-view's layout info. + mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo(); + } + else + { + if( mDisplayedTextView ) + { + mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo ); + } + else + { + // There is no text-view. + mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo(); + } + } +} + +void TextInput::EmitStyleChangedSignal() +{ + // emit signal if input style changes. + + Toolkit::TextInput handle( GetOwner() ); + mStyleChangedSignalV2.Emit( handle, mInputStyle ); +} + +void TextInput::EmitMaxInputCharactersReachedSignal() +{ + // emit signal if max characters is reached during text input. + DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n"); + + Toolkit::TextInput handle( GetOwner() ); + mMaxInputCharactersReachedSignalV2.Emit( handle ); +} + +void TextInput::EmitInputTextExceedsBoundariesSignal() +{ + // Emit a signal when the input text exceeds the boundaries of the text input. + + Toolkit::TextInput handle( GetOwner() ); + mInputTextExceedBoundariesSignalV2.Emit( handle ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-input/text-input-impl.h b/dali-toolkit/internal/controls/text-input/text-input-impl.h new file mode 100644 index 0000000..3f59538 --- /dev/null +++ b/dali-toolkit/internal/controls/text-input/text-input-impl.h @@ -0,0 +1,1465 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_INPUT_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_INPUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class TextInput; +class TextView; + +typedef IntrusivePtr TextInputPtr; + +class TextInput : public ControlImpl +{ +public: + + /** + * Create a new TextInput + * @return instrusive ptr to a TextInput + */ + static Dali::Toolkit::TextInput New(); + + /** + * @copydoc Toolkit::TextInput::GetText + */ + std::string GetText() const; + + /** + * @copydoc Toolkit::TextInput::GetMarkupText() + */ + std::string GetMarkupText() const; + + /** + * @copydoc Toolkit::TextInput::SetPlaceholderText + */ + void SetPlaceholderText( const std::string& placeHolderText ); + + /** + * @copydoc Toolkit::TextInput::SetPlaceholderText + */ + std::string GetPlaceholderText(); + + /** + * @copydoc Toolkit::TextInput::SetInitialText + */ + void SetInitialText(const std::string& initialText); + + /** + * set the text to be displayed in text-input, will overwrite any existing text. + * can be used to clear the text-input by passing an empty string. + * @param [in] initialText text to be initially displayed + */ + void SetText(const std::string& initialText); + + /** + * @copydoc Toolkit::TextInput::SetMaxCharacterLength + */ + void SetMaxCharacterLength(std::size_t maxChars); + + /** + * @copydoc Toolkit::TextInput::SetNumberOfLinesLimit + */ + void SetNumberOfLinesLimit(std::size_t maxLines); + + /** + * @copydoc Toolkit::TextInput::GetNumberOfLinesLimit + */ + std::size_t GetNumberOfLinesLimit() const; + + /** + * @copydoc Toolkit::TextInput::GetFont + */ + Font GetFont() const; + + /** + * @copydoc Toolkit::TextInput::SetFont + */ + void SetFont(Font font); + + /** + * @copydoc Toolkit::TextInput::InputStartedSignal() + */ + Toolkit::TextInput::InputSignalV2& InputStartedSignal(); + + /** + * @copydoc Toolkit::TextInput::InputFinishedSignal() + */ + Toolkit::TextInput::InputSignalV2& InputFinishedSignal(); + + /** + * @copydoc Toolkit::TextInput::CutAndPasteToolBarDisplayedSignal() + */ + Toolkit::TextInput::InputSignalV2& CutAndPasteToolBarDisplayedSignal(); + + /** + * @copydoc Toolkit::TextInput::StyleChangedSignal() + */ + Toolkit::TextInput::StyleChangedSignalV2& StyleChangedSignal(); + + /** + * @copydoc Toolkit::TextInput::MaxInputCharactersReachedSignal() + */ + Toolkit::TextInput::MaxInputCharactersReachedSignalV2& MaxInputCharactersReachedSignal(); + + /** + * @copydoc Toolkit::TextInput::InputTextExceedBoundariesSignal() + */ + Toolkit::TextInput::InputTextExceedBoundariesSignalV2& InputTextExceedBoundariesSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + + /** + * @see Toolkit::TextInput::SetEditMode(bool editMode) + * @see Toolkit::TextInput::SetEditMode(bool editMode, const Vector2& touchPoint) + * + * @param[in] editMode true or false to indicate editMode on or off. + * @param[in] setCursorOnTouchPoint Whether to use the touch point to set the cursor position. + * @param[in] touchPoint A position in actor coordinates within the text-input. + */ + void SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint = Vector2::ZERO); + + /** + * @copydoc Toolkit::TextInput::GetEditMode + */ + bool IsEditable() const; + + /** + * @copydoc Toolkit::TextInput::SetTextSelectable + */ + void SetTextSelectable(bool textSelectable = true); + + /** + * @copydoc Toolkit::TextInput::IsTextSelectable + */ + bool IsTextSelectable() const; + + /** + * @copydoc Toolkit::TextInput::IsTextSelected + */ + bool IsTextSelected() const; + + /** + * @copydoc Toolkit::TextInput::DeSelectText + */ + void DeSelectText(); + + /** + * @copydoc Toolkit::TextInput::SetEditOnTouch + */ + void SetEditOnTouch(bool editOnTouch); + + /** + * @copydoc Toolkit::TextInput::IsEditOnTouch + */ + bool IsEditOnTouch() const; + + /** + * @copydoc Toolkit::TextInput::SetGrabHandleImage + */ + void SetGrabHandleImage(Dali::Image image); + + /** + * @copydoc Toolkit::TextInput::SetCursorImage + */ + void SetCursorImage(Dali::Image image, const Vector4& border ); + + /** + * @copydoc Toolkit::TextInput::GetSelectionHandleSize + */ + Vector3 GetSelectionHandleSize(); + + /** + * @copydoc Toolkit::TextInput::SetRTLCursorImage + */ + void SetRTLCursorImage(Dali::Image image, const Vector4& border ); + + /** + * @copydoc Toolkit::TextInput::EnableGrabHandle + */ + void EnableGrabHandle(bool toggle); + + /** + * @copydoc Toolkit::TextInput::IsGrabHandleEnabled + */ + bool IsGrabHandleEnabled(); + + /** + * @copydoc Toolkit::TextInput::EnableSelectionHandleFlip + */ + void EnableSelectionHandleFlip( bool toggle ); + + /** + * @copydoc Toolkit::TextInput::IsSelectionHandleFlipEnabled + */ + bool IsSelectionHandleFlipEnabled(); + + /** + * @copydoc Toolkit::TextInput::SetSelectionHandleFlipMargin + */ + void SetSelectionHandleFlipMargin( const Vector4& margin ); + + /** + * @copydoc Toolkit::TextInput::SetBoundingRectangle + */ + void SetBoundingRectangle( const Rect& boundingRectangle ); + + /** + * @copydoc Toolkit::TextInput::GetBoundingRectangle + */ + const Rect GetBoundingRectangle() const; + + /** + * @copydoc Toolkit::TextInput::GetSelectionHandleFlipMargin + */ + const Vector4& GetSelectionHandleFlipMargin(); + + /** + * @copydoc Toolkit::TextInput::SetTextColor + */ + void SetTextColor( const Vector4& color ); + + /** + * @copydoc Toolkit::TextInput::SetActiveStyle + */ + void SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask ); + + /** + * @copydoc Toolkit::TextInput::ApplyStyle + */ + void ApplyStyle( const TextStyle& style, const TextStyle::Mask mask ); + + /** + * @copydoc Toolkit::TextInput::ApplyStyleToAll + */ + void ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask ); + + /** + * @copydoc Toolkit::TextInput::GetStyleAtCursor + */ + TextStyle GetStyleAtCursor() const; + + /** + * Retrieves the character style for the given position. + * @param[in] position The character position which style is required. + * @return The style for the given position. + */ + TextStyle GetStyleAt( std::size_t position ) const; + + /** + * @copydoc Toolkit::TextInput::SetTextAlignment() + */ + void SetTextAlignment( Toolkit::Alignment::Type align ); + + /** + * @copydoc Toolkit::TextInput::SetTextLineJustification() + */ + void SetTextLineJustification( Toolkit::TextView::LineJustification justification ); + + /** + * @copydoc Toolkit::TextInput::SetFadeBoundary() + */ + void SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ); + + /** + * @copydoc Toolkit::TextInput::GetFadeBoundary() + */ + const Toolkit::TextView::FadeBoundary& GetFadeBoundary() const; + + /** + * @copydoc Toolkit::TextInput::GetTextAlignment() + */ + Toolkit::Alignment::Type GetTextAlignment() const; + + /** + * @copydoc Toolkit::TextInput::SetMultilinePolicy() + */ + void SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy ); + + /** + * @copydoc Toolkit::TextInput::GetMultilinePolicy() + */ + Toolkit::TextView::MultilinePolicy GetMultilinePolicy() const; + + /** + * @copydoc Toolkit::TextInput::SetWidthExceedPolicy() + */ + void SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * @copydoc Toolkit::TextInput::GetWidthExceedPolicy() + */ + Toolkit::TextView::ExceedPolicy GetWidthExceedPolicy() const; + + /** + * @copydoc Toolkit::TextInput::SetHeightExceedPolicy() + */ + void SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * @copydoc Toolkit::TextInput::GetHeightExceedPolicy() + */ + Toolkit::TextView::ExceedPolicy GetHeightExceedPolicy() const; + + /** + * @copydoc Toolkit::TextInput::SetExceedEnabled() + */ + void SetExceedEnabled( bool enable ); + + /** + * @copydoc Toolkit::TextInput::GetExceedEnabled() + */ + bool GetExceedEnabled() const; + + /** + * @copydoc Toolkit::TextInput::SetBackground + */ + void SetBackground(Dali::Image image ); + + /** + * @copydoc Toolkit::TextInput::SetNumberOfLines + */ + void SetNumberOfLines(std::size_t lines); + + /** + * @copydoc Toolkit::TextInput::GetNumberOfLines + */ + std::size_t GetNumberOfLines(); + + /** + * @copydoc Toolkit::TextInput::GetNumberOfCharacters + */ + std::size_t GetNumberOfCharacters() const; + +private: + + /** + * structure to hold each highlight box needed for text selection + */ + struct HighlightBox + { + Vector3 size; ///< size of the highlight box + Vector3 position; ///< position of highlight box + ImageActor highlightBoxActor; ///< as actor that is the highlight box + }; + + /** + * structure to hold each character in displayed string and its position from the left + */ + struct CharPositions + { + char character; ///< todo change to UTF to aid multi-language support + Vector3 position; + Vector2 size; + }; + + /** + * structure to hold coordinates of each quad, which will make up the mesh. + */ + struct QuadCoordinates + { + /** + * Default constructor + */ + QuadCoordinates() + { + } + + /** + * Constructor + * @param[in] x1 left co-ordinate + * @param[in] y1 top co-ordinate + * @param[in] x2 right co-ordinate + * @param[in] y2 bottom co-ordinate + */ + QuadCoordinates(float x1, float y1, float x2, float y2) + : min(x1, y1), + max(x2, y2) + { + } + + Vector2 min; ///< top-left (minimum) position of quad + Vector2 max; ///< bottom-right (maximum) position of quad + }; + + typedef std::vector QuadContainer; + + /** + * structure for information required to build the highlight mesh + */ + struct HighlightInfo + { + /** + * Adds a Quad (2D rectangular sub-selection) + * @param[in] x1 left co-ordinate + * @param[in] y1 top co-ordinate + * @param[in] x2 right co-ordinate + * @param[in] y2 bottom co-ordinate + */ + void AddQuad( float x1, float y1, float x2, float y2 ); + + /** + * Clamps all quads to fit within a min -> max 2D boundary. + */ + void Clamp2D(const Vector2& min, const Vector2& max); + + QuadContainer mQuadList; ///< List of quads (sub-selections that form to create complete selection) + }; + + /** + * Holds requested selection start and end points for highlighted text. + */ + struct SelectionParameters + { + SelectionParameters( size_t start, size_t end ) + : mStartOfSelection( start ), mEndOfSelection( end ) + { + + } + + size_t mStartOfSelection; + size_t mEndOfSelection; + }; + + enum State + { + StateEdit, + StateDraggingHandle + }; + + enum SelectionHandleId + { + HandleOne, ///< Selection handle one which is on the left + HandleTwo ///< Selection handle two which is on the right + }; + + /** + * Two different behaviours are needed to convert a touch point into a character index. + * When a tap is received and the touch point doesn't hit any character, the final character selected might + * be different than the one selected if the event is a pan. + * i.e. If a tap is received and the touch point doesn't hit any character the expected position of the cursor + * would be the end or the beginning of a line. However, this behaviour would be weird while panning. + */ + enum TouchToIndex + { + TapMode, ///< Touch point to character index conversion mode used for Tap events. + DragMode ///< Touch point to character index conversion mode used for Pan events. + }; + + /** + * Used to set the direction when find the next visible character. + */ + enum FindVisibleCharacterDirection + { + Left, ///< Find visible characters on the left. + Right, ///< Find visible characters on the right. + ByEnd ///< Start finding visible characters by the end. + }; + + /** + * + */ + virtual bool OnTouchEvent(const TouchEvent& event); + + /** + * From CustomActorImpl; called after the Text Input actor is touched + * @param[in] event The KeyEvent event. + * @return True if the event should be consumed. + */ + virtual bool OnKeyEvent(const KeyEvent& event); + + /** + * From CustomActorImpl; called when this actor gains keyboard focus. + */ + virtual void OnKeyInputFocusGained(); + + /** + * From CustomActorImpl; called when this actor loses keyboard focus. + */ + virtual void OnKeyInputFocusLost(); + + /** + * From ControlImpl; called whenever the control is added to the stage. + */ + virtual void OnControlStageConnection(); + +private: // From ControlImpl + + /** + * Creation of the layer that is used by top level active parts of the TextInput like handles + */ + void CreateActiveLayer(); + + /** + * @copydoc Toolkit::ControlImpl::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::ControlImpl::OnControlSizeSet() + */ + virtual void OnControlSizeSet(const Vector3& targetSize); + + /** + * @copydoc Toolkit::ControlImpl::OnRelaidOut() + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + + /** + * Retrieves the text-input's natural size by calling TextView::GetNaturalSize(). + * + * @return The natural size. + */ + virtual Vector3 GetNaturalSize(); + + /** + * Retrieves the text-input's \e height for a given \e width by calling TextView::GetHeightForWidth(). + * + * @param[in] width The given \e width. + * + * @return The \e height for the given \e width. + */ + virtual float GetHeightForWidth( float width ); + +protected: + + /** + * Construct a new TextInput. + */ + TextInput(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~TextInput(); + +private: + + // Undefined + TextInput(const TextInput&); + + // Undefined + TextInput& operator=(const TextInput& rhs); + + /** + * Callback when a handle is panned/moved, either selection handles or grab handle + * @param actor Handle of the selection or grab handle. + * @param gesture Data structure with the parameters of the gesture. + */ + void OnHandlePan(Actor actor, PanGesture gesture); + + /** + * Callback for touch down on Grab handle + * @param[in] actor touched + * @param[in] touch touch event + */ + bool OnPressDown(Dali::Actor actor, const TouchEvent& touch); + + /** + * Callback for touch down on Selection handle one + * @param[in] actor touched + * @param[in] touch touch event, used to determine if down or up event + */ + bool OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch); + + /** + * Callback for touch down on Selection handle two + * @param[in] actor touched + * @param[in] touch touch event, used to determine if down or up event + */ + bool OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch); + + /** + * Callback for tap on TextInput + * @param[in] actor + * @param[in] tap touch event + */ + void OnTextTap(Dali::Actor actor, Dali::TapGesture tap); + + /** + * Callback for double tap on TextInput + * @param[in] actor + * @param[in] tap touch event + */ + void OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap); + + /** + * Callback for long press on TextInput + * @param[in] actor + * @param[in] longPress long press event + */ + void OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress); + + /** + * Callback for the ClipboardEventNotifier when text is selected in the clipboard window. + * @param[in] notifier The Clipboard Event Notifier. + */ + void OnClipboardTextSelected( ClipboardEventNotifier& notifier ); + + /** + * Callback for when a button is pressed in popup panel + * @param[in] button handle to the button pressed. + */ + bool OnPopupButtonPressed( Toolkit::Button button ); + + /** + * Callback when handle timer ticks. + * + * Cursor should become visible/invisible to simulate blinking. + * + * @return True if the timer should be keep running. + */ + bool OnCursorBlinkTimerTick(); + + /** + * Invoked upon popup Hide animation completing. + * @note Only called for animating hide, not called for instantaneous (animate = false) + * @param[in] popup The popup which was hidden. + */ + void OnPopupHideFinished(TextInputPopup& popup); + + /** + * Called in OnKeyEvent to handle key down events. + * @param[in] event The KeyEvent event. + * @return True if the event should be consumed. + */ + bool OnKeyDownEvent(const KeyEvent& event); + + /** + * Called in OnKeyEvent to handle key up events. + * @param[in] event The KeyEvent event. + * @return True if the event should be consumed. + */ + bool OnKeyUpEvent(const KeyEvent& event); + + /** + * Callback called when the text-view is scrolled. + * + * Updates the selection and grab handles, and the highlighted text. + * + * @param[in] textView Handle of the text-view. + * @param[in] scrollPosition The difference with the previous scroll position. + */ + void OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition ); + + /** + * Scrolls the text-view to make the cursor visible. + * + * @param[in] cursorPosition The actual cursor position in actor coordinates. + */ + void ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition ); + + /** + * Creates and starts a timer to scroll the text when handles are close to the edges of the text-input. + * + * It only starts the timer if it's already created. + */ + void StartScrollTimer(); + + /** + * Stops the timer used to scroll the text. + */ + void StopScrollTimer(); + + /** + * Callback called by the timer used to scroll the text. + * + * It calculates and sets a new scroll position. + */ + bool OnScrollTimerTick(); + +public: // Public to allow internal testing. + + /** + * Register for touch events + */ + void SetUpTouchEvents(); + + /** + * Sets up TextView Actor + */ + void CreateTextViewActor(); + + /** + * Set Styled Text for text input. + * @param[in] styleText The new styled text for the text input. + */ + void SetText( const MarkupProcessor::StyledTextArray& styleText ); + + /** + * Start a timer to signal cursor to blink. + */ + void StartCursorBlinkTimer(); + + /** + * Stop the timer signalling the cursor to blink. + */ + void StopCursorBlinkTimer(); + + /** + * Starts input, setting focus and showing keyboard.. + */ + void StartEditMode(); + + /** + * Called when End of input and focus no longer required, keyboard is hidden. + */ + void EndEditMode(); + + /** + * Applies a style to the current pre-edit / predicted word to show it is being edited. + * @param[in] preEditStartPosition position in text array that the predicted word starts at + * @param[in] preEditStringLength used to calculate how many characters need their style changed. + */ + void ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength ); + + /** + * Restores style to value before applying Pre-Edit style. + */ + void RemovePreEditStyle(); + + /** + * Event received from IMF manager + * @return ImfCallbackData data struture undicating if update is needed, cursor position and current text + */ + ImfManager::ImfCallbackData ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData& test ); + + /** + * Called when the OnKey event is a Pre-edit string + * @param[in] keyString String received in Pre-edit + * @param[in] cursorOffset the cursor offset from where the pre-edit word starts + * @return bool true if preedit reset is required. + */ + bool PreEditReceived( const std::string& keyString, std::size_t cursorOffset ); + + /** + * Called when the OnKey event is a Commit string + * @param[in] keyString String received in Comment + * @return update flag to trigger cursor update of TextInput only when needed. + */ + bool CommitReceived( const std::string& keyString ); + + /** + * Deletes Pre-edit string + * By default it doesn't update the character's size and position table, which is a costly + * operation. As in many cases deletion and insertion (InsertAt) of text + * occurs in the same action i.e. preedit/commit. It makes sense to + * delete without updating, and then insert with updating. + * + * @return The number of characters to be deleted. + */ + std::size_t DeletePreEdit(); + + /** + * Reset all pre-edit flag and signal IMF keyboard that the current pre-edit word has been comitted. + * This may be due to the cursor being moved by user or reached the max character limit. + * @param[in] preserveCursorPosition Set true to keep cursor in current position, eg. touch event caused cursor to move. + */ + void PreEditReset( bool preserveCursorPosition ); + + /** + * Called after cursor position needs updating. + * Redraws cursor and notifies VirtualKeyboard + */ + void CursorUpdate(); + + /** + * Delete highlighted characters + * @param[in] inheritStyle Whether style from previous character in the string should be inherited. + */ + void DeleteHighlightedText( bool inheritStyle ); + + /* + * Delete range of characters + * @param[in] start position of characters to delete + * @param[in] ncharacters number of characters to delete + */ + void DeleteRange(std::size_t start, std::size_t ncharacters); + + /** + * Delete character at current cursor position and redisplay + * @param[in] positionToDelete position of character to delete + */ + void DeleteCharacter( std::size_t positionToDelete ); + + /** + * Add or replaces characters to currently displayed string at cursor position + * By default it doesn't update the character's size and position table, which is a costly + * operation. As in many cases deletion and insertion (InsertAt) of text + * occurs in the same action i.e. preedit/commit. It makes sense to + * delete without updating, and then insert with updating. + * @param[in] newText string to add to TextInput display string. + * @param[in] insertionPosition position to insert at. + * @param[in] numberOfCharactersToReplace The number of characters to replace. + * @return number of characters to offset the cursor by. + */ + std::size_t InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace ); + + /** + * Creates a cursor from the supplied image + * @param[in] cursorImage the image to be used for the cursor. + * @param[in] border the nine patch border corresponding to the supplied image. + * @return the image actor to be used as the cursor. + */ + ImageActor CreateCursor( Image cursorImage, const Vector4& border ); + + /** + * Moves cursor to the right + * param[in] reverse if true then cursor moves in the reverse direction (to the left) + * param[in] places number of character cursor should move. + */ + void AdvanceCursor(bool reverse = false, std::size_t places = 1); + + /** + * Draw a cursor / caret at position where new text should appear + * @param[in] nthChar the position along the text string in which new text should appear. + */ + void DrawCursor(const std::size_t nthChar = 0); + + /** + * Sets cursor visibility + * This sets visibility of the cursor. Which is comprised of 2 + * cursors. The conventional cursor, and the alternate (RTL) cursor, + * which only appears when the cursor is at a character that can have + * a character appended to different visual positions depending on whether that + * character to be appended is RTL or LTR. + * @param[in] visible true - enable visibility for cursor, false - disable visiblity for cursor + */ + void SetCursorVisibility( bool visible ); + + /** + * Sets alternate cursor enable state + * @see SetCursorVisibility + * alternate cursor will only be visible if both SetCursorVisiblity + * and cursor enabled have been set to true. + */ + void SetAltCursorEnabled( bool enabled ); + + /** + * Create the grab handle that positions the cursor + * @param[in] image to be used for grab handle + * + */ + void CreateGrabHandle(Image image=Image()); + + /** + * Create Grab area to be used by Grab Handle + */ + void CreateGrabArea( Actor& parent); + + /** + * Move grab handle to the required character position + * + * @param[in] displacement Displacement of the grab handle in actor coordinates. + * + * @return The new actual position the handle has been set to. + */ + Vector3 MoveGrabHandle( const Vector2& displacement ); + + /** + * Show or hide the grab handle without baking the visibility flag. + * Used when the Grab handle needs to be invisible due to text-view scrolling making it out of view + * + * @param[in] visible bool flag to set as true is grab handle should be shown, else false to hide. + */ + void ShowGrabHandle( bool visible ); + + /** + * Show or hide the grab handle and bake the visibility flag. + * Used when the state of text-input changes to a state which the grabhandle is not required. E.g. Selection mode starts or edit mode ends. + * Calls ShowGrabHandle. + * + * @param[in] visible bool flag to set as true is grab handle should be shown, else false to hide. + */ + void ShowGrabHandleAndSetVisibility( bool visible ); + + /* Boundary Property Notifications when handle exceed bounding box*/ + + /** + * PropertyNotification Callback when left boundary exceeded so handle can be flipped. + * @param[in] source PropertyNotification + */ + void OnLeftBoundaryExceeded( PropertyNotification& source ); + /** + * PropertyNotification Callback when within left boundary so handle can be flipped back. + * @param[in] source PropertyNotification + */ + void OnReturnToLeftBoundary( PropertyNotification& source ); + /** + * PropertyNotification Callback when right boundary exceeded so handle can be flipped. + * @param[in] source PropertyNotification + */ + void OnRightBoundaryExceeded( PropertyNotification& source ); + /** + * * PropertyNotification Callback when within right boundary so handle can be flipped back. + * @param[in] source PropertyNotification + */ + void OnReturnToRightBoundary( PropertyNotification& source ); + + /** + * PropertyNotification Callbacks for hiding handle one when it exceeds boundary. + * @param[in] source PropertyNotification + */ + void OnHandleOneLeavesBoundary( PropertyNotification& source ); + /** + * PropertyNotification Callbacks for showing hidden handle one when returns within boundary + * @param[in] source PropertyNotification + */ + void OnHandleOneWithinBoundary( PropertyNotification& source ); + /** + * PropertyNotification Callbacks for hiding handle two it when exceeds boundary. + * @param[in] source PropertyNotification + */ + void OnHandleTwoLeavesBoundary( PropertyNotification& source ); + /** + * PropertyNotification Callbacks for showing hidden handle two when returns within boundary + * * @param[in] source PropertyNotification + */ + void OnHandleTwoWithinBoundary( PropertyNotification& source ); + + /** + * Set up property notifications on the position of the handles to facilitate flipping and hiding when at screen boundary. + */ + void SetUpHandlePropertyNotifications(); + + /** + * Create the selection handles that bound text to be selected for copy/cut. + * @param[in] start initial position of start selection handle. + * @param[in] end initial position of end selection handle. + * @param[in] handleOneImage (optional) to be used for selection handle + * @param[in] handleTwoImage (optional) to be used for selection handle + */ + void CreateSelectionHandles( std::size_t start = 0, std::size_t end = std::numeric_limits::max(), Dali::Image handleOneImage = Dali::Image(), Dali::Image handleTwoImage = Dali::Image() ); + + /** + * Move the selection handles to required positions in text. + * + * @param[in] handleId the handle to position + * @param[in] displacement Displacement of the selection handle in actor coordinates. + * + * @return The new actual position the handle has been set to. + */ + Vector3 MoveSelectionHandle(SelectionHandleId handleId, const Vector2& displacement); + + /** + * Calculate and position the specified selection handle the given index position + * + * @param[in] handleId the handle to position + */ + void SetSelectionHandlePosition(SelectionHandleId handleId); + + /** + * Gets the visual position of a logical position. + * @note This is preferred over directly accessing the Map, as it resolves visual + * positions outside of the character map range. + * @param[in] logicalPosition The logical position + * @return Visual position is returned. + */ + std::size_t GetVisualPosition(std::size_t logicalPosition) const; + + /** + * Gets a table of the visual text positions which has a flag + * for each Character. The flag is either true (character selected) + * or false (character deselected) + * @note startSelection can be greater or less than endSelection + * + * @param[in,out] selectedVisualText The vector to be resized and populated with the selected flags + * @param[in] startSelection The start selection point for the text + * @param[in] endSelection The end selection point for the text + */ + void GetVisualTextSelection(std::vector& selectedVisualText, std::size_t startSelection, std::size_t endSelection); + + /** + * Iterates between selection handles and computes the info required to build the highlight mesh + */ + HighlightInfo CalculateHighlightInfo(); + + /** + * Calculates new Mesh data so highlight moves with selection handles. + */ + void UpdateHighlight(); + + /** + * Removes popup, and its options. + */ + void ClearPopup(); + + /** + * Adds a popup option. + * @note Creates popup frame if not already created. + * @param[in] name The unique name for this option. + * @param[in] caption The caption (label) for this option + * @param[in] icon the image icon to be displayed for this option + * @param[in] finalOption Flag to indicate that this is the final option. + * (set to true on the last option you add) + */ + void AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption = false); + + /** + * Sets popup position + * @param[in] position The actual position for this popup. + */ + void SetPopupPosition(const Vector3& position); + + /** + * Hides the popup + * @param[in] animate (optional) whether to animate popup to hide state over time (i.e. tween). + * @param[in] signalFinished (optional) whether to perform an animation finish operation after the hide animation completes. Requires animate to be true. + */ + void HidePopup( bool animate = true, bool signalFinished = true ); + + /** + * Shows the popup + * @param[in] animate (optional) whether to animate popup to show state over time (i.e. tween). + */ + void ShowPopup(bool animate = true); + + /** + * Shows the cut-copy-paste popup + */ + void ShowPopupCutCopyPaste(); + + /** + * Setup the selection popup and clipboard if relevant so the correct options are shown when ShowPopup is called. + */ + void SetUpPopUpSelection(); + + /** + * Return the logical index containing the character position closest to the source. + * Used for positioning the grab handle at characters when dragged along. + * Two different behaviours are needed in case the source point doesn't actually touch a + * character. @see TouchToIndex. + * @param[in] source float to match against + * @param[out] closestIndex index to the vector of character's size and position. + * @return \e true if the source point is actually inside the geometry provided by TextView. + */ + bool ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex ); + + /** + * Returns the X-position of the current line justification + * (relative to left of text-view container) + * @return X position for line justification + */ + float GetLineJustificationPosition() const; + + /** + * Currently the cursor is positioned at the previous characters position plus it's width. + * If the previous character is on a different line then this function returns the correct position. + * @param[in] characterPosition the character position index that the cursor should be at + * @return position of cursor/handle + */ + Vector3 PositionCursorAfterWordWrap( std::size_t characterPosition ) const; + + + /** + * Return a vector which is the actual position for the given character position + * The character position is where a cursor would be position for that character. + * @param[in] characterPosition the logical (input) position in the 'string' of characters. + * + * @return Vector3 the actual x,y,z position + */ + Vector3 GetActualPositionFromCharacterPosition(std::size_t characterPosition ) const; + + /** + * Return a vector which is the actual position for the given character position + * The character position is where a cursor would be positioned for that character to be inserted. + * An additional alternatePosition is also set in circumstances where the possible writing + * of characters would be in the opposite direction. + * e.g. "HelloشقشلاهؤEnglish" + * | | + * * + + * [*] - Primary actual position for cursor i.e. continuing writing LTR (English) + * [+] - Alternate actual position for cursor i.e. writing RTL (Arabic) + * + * @param[in] characterPosition the logical (input) position in the 'string' of characters. + * @param[out] directionRTL Whether the actual x,y,z position is after LTR (false) or RTL (true) text. + * @param[out] alternatePosition the actual x,y,z position of the cursor if user types + * in alternate direction to current flow of text. + * @param[out] alternatePositionValid whether this alternate position is valid. + * @return Vector3 the actual x,y,z position + */ + Vector3 GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const; + + /** + * Retrieve the character position of the first character on the row of text + * that this character resides on. + * @param[in] logicalPosition the position in the 'string' of characters. + * @return logical character position of start of row. + */ + std::size_t GetRowStartFromCharacterPosition(std::size_t logicalPosition) const; + + /** + * Retrieve the dimensions of this row of text that the character resides on. + * @param[in] characterPosition the position in the 'string' of characters. + * @return The size of the rectangle representing this row + */ + Size GetRowRectFromCharacterPosition(std::size_t characterPosition) const; + + /** + * Retrieve the dimensions (and min-max) of this row of text that the character resides on. + * @param[in] characterPosition the position in the 'string' of characters. + * @param[out] min the top-left position of the rectangle representing this row + * @param[out] max the bottom-right position of the rectangle representing this row + * @return The size of the rectangle representing this row (max - min) + */ + Size GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const; + + /** + * Checks if the provided touchedActor was this text-input + * @param[in] touchedActor the touched actor that will be checked against this text-input + * @return true if the touchActor was the text-input or one of its children + */ + bool WasTouchedCheck( const Actor& touchedActor ) const; + + /** + * Connect to the stage touch event + */ + void StartMonitoringStageForTouch(); + + /** + * Disconnect from the stage touch event + */ + void EndMonitoringStageForTouch(); + + /** + * Callback for when the screen is touched, this will be connected when selection starts and disconnected when it ends. + * @param[in] event The touch event + */ + void OnStageTouched(const TouchEvent& event); + + + /** + * Select the text between the given values + * @param[in] start position within array to start selection + * @param[in] end position within array to end selection + */ + void SelectText(std::size_t start, std::size_t end); + + /** + * Gets selected text and returns it as a StyleText vector + * @return StyledText vector that is selected. + */ + MarkupProcessor::StyledTextArray GetSelectedText(); + + /** + * Applies the given style to all text, selected or not selected. + * By default all style settings are applied but a bit mask could be used to modify only certain style settings. + */ + void ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end); + + /** + * Callback for when the keyboard status changes + * @param[in] keyboardShown Whether the keyboard has just been shown or not. + */ + void KeyboardStatusChanged(bool keyboardShown); + + /** + * Hide highlight shown between selection handles. + */ + void RemoveHighlight(); + + /** + * Highlights text that has been selected + */ + void CreateHighlight(); + + /** + * Copies selected text to clipboard + * @return bool true if copy was successful. + */ + bool CopySelectedTextToClipboard(); + + /** + * Pastes the given text to currently displayed string at the current cursor position + * @param[in] text Text to be pasted + */ + void PasteText( const Text& text ); + + /** + * Sets the direction of the text if it is empty. + */ + void SetTextDirection(); + + /** + * Updates the line height accordingly with the current text input style. + */ + void UpdateLineHeight(); + + /** + * Finds a visible character. + * + * The \e direction parameters specifies from where to start to look for a visible character. \e Left means start by characters in lower + * positions than \e cursorPosition, \e Right means start by characters in greater positions than \e cursorPosition and \e ByEnd starts + * by the last characters. + * + * If \e Left or \e Right is provided and any character is found, then looks in the other direction. + * + * @param[in] direction Direction used to find a visible character. + * @param[in] cursorPosition Starting position. + * + * @return The found visible character. + */ + std::size_t FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const; + + /** + * @copydoc SetSortModifier() + */ + void SetSortModifier( float depthOffset ); + + /** + * @copydoc SetSnapshotModeEnabled() + */ + void SetSnapshotModeEnabled( bool enable ); + + /** + * @copydoc IsSnapshotModeEnabled() + */ + bool IsSnapshotModeEnabled() const; + + /** + * @copydoc SetScrollEnabled() + */ + void SetScrollEnabled( bool enable ); + + /** + * @copydoc IsScrollEnabled() + */ + bool IsScrollEnabled() const; + + /** + * @copydoc SetScrollPosition() + */ + void SetScrollPosition( const Vector2& position ); + + /** + * @copydoc GetScrollPosition() + */ + Vector2 GetScrollPosition() const; + + /** + * Insert or replaces the given text in the given position. It checks if the text exceeds the maximum allowed number of characters + * and if it fits in the text-input's boundaries. + * + * @param[in] text Text to be inserted. + * @param[in] position Position where the text is inserted. + * @param[in] numberOfCharactersToReplace The number of characters to replace. + * @param[out] textExceedsMaximunNumberOfCharacters The number of characters that have been inserted. + * @param[out] textExceedsBoundary Whether the inserted text has exceeded the boundaries of the text-input. + * + * @return The number of characters that have been inserted. + */ + std::size_t DoInsertAt( const Text& text, std::size_t position, std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary ); + + /** + * Retrieve Text-view's layout info. + */ + void GetTextLayoutInfo(); + + /** + * Emits the style changed signal. + */ + void EmitStyleChangedSignal(); + + /** + * Emits max input characters reached signal. + */ + void EmitMaxInputCharactersReachedSignal(); + + /** + * Emits a signal when the input text exceeds the boundaries of the text-input. + */ + void EmitInputTextExceedsBoundariesSignal(); + +private: + + State mState; ///< Current State of Text input. + MarkupProcessor::StyledTextArray mStyledText; ///< String currently displayed by TextView with style info. + TextStyle mInputStyle; ///< Stores the current input style. + float mLineHeight; ///< Stores the current line height accordingly with the input style. + Toolkit::TextView mDisplayedTextView; ///< Actor which actually display text + + MarkupProcessor::StyledTextArray mStyledPlaceHolderText; ///< Text used as place holder with style info. + + std::size_t mMaxStringLength; ///< Max number of characters for string + std::size_t mNumberOflinesLimit; ///< Limit on the number of lines this TextInput will display. + + ImageActor mCursor; ///< Cursor overlayed on Text to show where new text will be inserted + ImageActor mCursorRTL; ///< Right To Left Cursor overlayed on Text (where new RTL text would be inserted) + Animation mCursorAnimation; ///< animator for cursor blinking. + std::size_t mCursorPosition; ///< position along string cursor is at. + Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink + + Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else. + + Image mGrabHandleImage; ///< image to be used for grab handle + ImageActor mGrabHandle; ///< Handle used to move cursor for editing + + Actor mGrabArea; ///< invisible actor that receives pans events for the grab handle. + PanGestureDetector mPanGestureDetector; ///< Pan gesture for handles + TapGestureDetector mTapDetector; ///< used to start editing + TapGestureDetector mDoubleTapDetector; ///< double tap detector + + Vector3 mActualGrabHandlePosition; ///< actual position of grab handle, this might not be snapped to a character + + LongPressGestureDetector mLongPressDetector; ///< Gesture to start selection + + /*Selection handles*/ + Image mSelectionHandleOneImage; ///< image used for selection handle one + Image mSelectionHandleOneImagePressed; ///< image used for selection handle one pressed state + Image mSelectionHandleTwoImage; ///< image used for selection handle two + Image mSelectionHandleTwoImagePressed; ///< image used for selection handle two pressed state + + bool mIsSelectionHandleOneFlipped:1; ///< Flag to know whether the handle one is flipped or not. + bool mIsSelectionHandleTwoFlipped:1; ///< Flag to know whether the handle two is flipped or not. + Vector3 mSelectionHandleOneOffset; ///< Handle One's Offset + Vector3 mSelectionHandleTwoOffset; ///< Handle Two's Offset + Vector3 mSelectionHandleOneActualPosition; ///< Actual x y position of handle + Vector3 mSelectionHandleTwoActualPosition; ///< Actual x y position of handle + std::size_t mSelectionHandleOnePosition; ///< Position of handle along the string of text + std::size_t mSelectionHandleTwoPosition; ///< Position of handle along the string of text + ImageActor mSelectionHandleOne; ///< First selection handle used for selecting text to cut&paste + ImageActor mSelectionHandleTwo; ///< Second selection handle used for selecting text to cut&paste + Actor mHandleOneGrabArea; ///< invisible actor that receives pans events for the selection handle. + Actor mHandleTwoGrabArea; ///< invisible actor that receives pans events for the selection handle. + + Mesh mHighlightMesh; ///< Mesh Data for highlight + MeshActor mHighlightMeshActor; ///< Mesh Actor to display highlight + MeshData mMeshData; ///< Container to hold meshData for highlight + Material mCustomMaterial; ///< Custom material used for highlight + HighlightInfo mNewHighlightInfo; ///< Geometry info to create highlight. + + Text mPreEditString; ///< Holds current input string prior to it being committed. + std::size_t mPreEditStartPosition; ///< Cursor position for start of pre-edit string + std::size_t mPreEditLength; ///< Cursor position for length of current pre-edit string after purging characters that do not fit. + std::size_t mNumberOfSurroundingCharactersDeleted; ///< Stores the number of surrounding characters deleted. + unsigned long mTouchStartTime; ///< Touch Start time (time in ms when down press began) + + Toolkit::TextView::TextLayoutInfo mTextLayoutInfo; ///< It contains a table layout info per character sorted by the character's visual index (retrieved from TextView), + ///< a reorder map that stores each character's visual (output) index according to its logical (input) index, + ///< a reorder map that stores each character's logical (input) index according to its visual (output) index, + ///< the text size after layout and the scroll offset. + + MarkupProcessor::StyledTextArray mCurrentCopySelecton; ///< Array to store copied text. + TextInputPopup mPopUpPanel; ///< Panel to house cut and paste, select all buttons. + + Timer mScrollTimer; + Vector2 mScrollDisplacement; + Vector3 mCurrentHandlePosition; + SelectionHandleId mCurrentSelectionId; + Vector3 mCurrentSelectionHandlePosition; + SelectionParameters mRequestedSelection; + Vector4 mSelectionHandleFlipMargin; + Vector4 mBoundingRectangleWorldCoordinates; + + Clipboard mClipboard; ///< Handle to clipboard + + bool mOverrideAutomaticAlignment:1; ///< Whether to override the alignment automatically set by the text content (e.g. european LTR or arabic RTL) + bool mCursorRTLEnabled:1; ///< Enable state of Alternate RTL Cursor (need to keep track of this as it's not always enabled) + bool mClosestCursorPositionEOL:1; ///< closest cursor position is end of line. + bool mCursorBlinkStatus:1; ///< \e true shows the cursor, \e false hiddes it. + + bool mCursorVisibility:1; ///< Visibility status of Cursor + bool mGrabHandleVisibility:1; ///< Visibility status of the grab handle. + + bool mIsCursorInScrollArea:1; ///< Whether the cursor is inside the boundaries of the text-input. + bool mIsGrabHandleInScrollArea:1; ///< Whether the grab handle is inside the boundaries of the text-input. + + bool mEditModeActive:1; ///< true to indicate TextInput is in edit mode. + bool mEditOnTouch:1; ///< true indicates edit mode starts by touching/tapping the TextInput + bool mTextSelection:1; ///< true indicates text selection is enabled. + bool mExceedEnabled:1; ///< Whether text-input editing text beyond its boundary is enabled or not. By default is enabled. + bool mGrabHandleEnabled:1; ///< Flag to enable the grab handle instead of the default magnifier. + bool mIsSelectionHandleFlipEnabled:1; ///< Flag to enable the selection handle flip + + bool mPreEditFlag:1; ///< Flag to indicate if key string received is still in pre-edit mode (Ecore IMF keyboard) + bool mIgnoreCommitFlag:1; ///< Flag to indicate if the commit string should be ignored, maybe due to a keyboard reset. + bool mIgnoreFirstCommitFlag:1; ///< Whether to ignore the first commit. + bool mSelectingText:1; ///< Ignore surrounding text as selecting text + + bool mPreserveCursorPosition:1; ///< Indicates that the commit message is from a reset and does not require the cursor to be moved + + bool mSelectTextOnCommit:1; + + bool mUnderlinedPriorToPreEdit:1; ///< As predictive text adds underline style this flag enables us to revert back or keep underline. + + bool mCommitByKeyInput:1; ///< Set if a commit is done by key input rather than automatically (usually when a space bar or enter is pressed). + + bool mPlaceHolderSet:1; ///< Whether the place holder text is set. + + Toolkit::TextInput::InputSignalV2 mInputStartedSignalV2; ///< Signal emitted when input starts + Toolkit::TextInput::InputSignalV2 mInputFinishedSignalV2; ///< Signal emitted when input ends + Toolkit::TextInput::StyleChangedSignalV2 mStyleChangedSignalV2; ///< Signal emitted when style changes. + Toolkit::TextInput::MaxInputCharactersReachedSignalV2 mMaxInputCharactersReachedSignalV2; ///< Signal emitted when max input characters is reached. + Toolkit::TextInput::InputSignalV2 mCutAndPasteToolBarDisplayedV2; ///< Signal emitted when toolbar displayed + Toolkit::TextInput::InputTextExceedBoundariesSignalV2 mInputTextExceedBoundariesSignalV2; ///< Signal emitted when input text exceeds the boundaries of the text-input. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::TextInput& GetImpl(Toolkit::TextInput& textInput) +{ + DALI_ASSERT_ALWAYS(textInput); + + Dali::RefObject& handle = textInput.GetImplementation(); + + return static_cast(handle); +} + +inline const Toolkit::Internal::TextInput& GetImpl(const Toolkit::TextInput& textInput) +{ + DALI_ASSERT_ALWAYS(textInput); + + const Dali::RefObject& handle = textInput.GetImplementation(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_INPUT_H__ diff --git a/dali-toolkit/internal/controls/text-input/text-input-popup-impl.cpp b/dali-toolkit/internal/controls/text-input/text-input-popup-impl.cpp new file mode 100644 index 0000000..7d9bf42 --- /dev/null +++ b/dali-toolkit/internal/controls/text-input/text-input-popup-impl.cpp @@ -0,0 +1,561 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace std; +using namespace Dali; + +namespace { + +// Popup: Background +const char* DEFAULT_PANEL_BACKGROUND = DALI_IMAGE_DIR "cutCopyPastePopup_bg.png"; + +// Popup: Divider +const char* DEFAULT_PANEL_BUTTON_DIVIDER = DALI_IMAGE_DIR "copypanelLine.png"; + +/* Functionality in place to have the end buttons using different images to inner button. + * Supply a centre image and then a left and right image, the centre image can have straight ends while + * the left image can be rounded on the left and straight on the right, the right image can be straight on the left and rounded on the right. + */ + +// Popup: Left Pressed Highlight +const char* DEFAULT_BUTTON_HIGHLIGHT_LEFT( DALI_IMAGE_DIR "00_popup_button_pressed.png" ); +const Vector4 DEFAULT_BUTTON_HIGHLIGHT_LEFT_BORDER( 6.0f, 9.0f, 6.0f, 9.0f ); + +// Popup: Center Pressed Highlight +const char* DEFAULT_BUTTON_HIGHLIGHT_CENTER( DALI_IMAGE_DIR "00_popup_button_pressed.png" ); +const Vector4 DEFAULT_BUTTON_HIGHLIGHT_CENTER_BORDER( 6.0f, 9.0f, 6.0f, 9.0f ); + +// Popup: Right Pressed Highlight +const char* DEFAULT_BUTTON_HIGHLIGHT_RIGHT( DALI_IMAGE_DIR "00_popup_button_pressed.png" ); +const Vector4 DEFAULT_BUTTON_HIGHLIGHT_RIGHT_BORDER( 6.0f, 9.0f, 6.0f, 9.0f ); + +// Popup: Tails +const char* DEFAULT_POPUP_TAIL_BOTTOM( DALI_IMAGE_DIR "00_popup_bubble_tail_bottom.png" ); + +// Popup: Vertical Constraint +// TODO: Remove - this should come from application - it is not possible to get the +// height of the indicator actor from Dali-Toolkit. +const Vector2 DEFAULT_POPUP_INDICATOR_OFFSET(0.0f, 60.0f); + +const Vector4 BACKGROUND_IMAGE_BORDER( 22.0f, 20.0f, 29.0f, 27.0f ); +const Vector2 BACKGROUND_IMAGE_SIZE( 50.0f, 54.0f ); +const Vector3 POPUP_TEXT_OFFSET( 12.0f, 10.0f, 0.0f ); +const Vector3 POPUP_TEXT_ENLARGE( 12.0f, 28.0f, 0.0f ); +const Vector3 POPUP_MINIMUM_SIZE( 128.0f, 124.0f, 0.0f ); + +const Vector3 BUTTON_TEXT_ENLARGE( 32.0f, 0.0f, 0.0f ); +const Vector3 BUTTON_TEXT_MINIMUM_SIZE( 128.0f, 126.0f, 0.0f ); +const Vector3 BUTTON_TEXT_MAXIMUM_SIZE( 196.0f, 126.0f, 0.0f ); +const Vector3 TEXT_LABEL_MAX_SIZE( 160.0f, 30.0f, 0.0f ); + +const float DIVIDER_WIDTH(2.0f); ///< Width of each button divider +const float DIVIDER_MARGIN(10.0f); ///< Top/Bottom Margin between divider and edge of popup. + +const float DEFAULT_UI_FONT_SIZE(7.0f); ///< Standard font size for Text-Input's UI + +const float HIDE_POPUP_ANIMATION_DURATION(0.2f); ///< Duration of popup hide animation in seconds. +const float SHOW_POPUP_ANIMATION_DURATION(0.2f); ///< Duration of popup show animation in seconds. + +const Vector2 DEFAULT_ICON_SIZE( 45.0f, 45.0f ); ///< Default icon size for image in options +const float TEXT_POSITION_OFFSET( -19.0f ); ///< Default offset for text label +const float ICON_POSITION_OFFSET( 19.0f ); ///< Default offset for icon + +// TODO: This should be based on the content for example: +// 1. For selection: should be above top of highlighted selection, or below bottom of highlighted selection + end handle. +// 2. For cursor: should be above top of cursor, or below bottom of cursor + grab handle. +const std::string POPUP_ALTERNATIVE_OFFSET("popup-alternative-offset"); ///< Alternative offset property for confinenment constraint. + + +/** + * Confine Actor to boundaries of reference actor (e.g. Parent) + * Actor bounds (top-left position + size) are confined to reference Actor's + * bounds. + */ +struct ConfinementConstraint +{ + /** + * Confinement constraint constructor. + * @param[in] topLeftMargin (optional) Top-Left margins (defaults to 0.0f, 0.0f) + * @param[in] bottomRightMargin (optional) Bottom-Right margins (defaults to 0.0f, 0.0f) + * @param[in] flipVertical (optional) whether to flip Actor to the other side if near edge, and by + * how much (defaults to 0.0f i.e. no flip) + */ + ConfinementConstraint(Vector2 topLeftMargin = Vector2::ZERO, Vector2 bottomRightMargin = Vector2::ZERO, bool flipHorizontal = false, bool flipVertical = false) + : mMinIndent(topLeftMargin), + mMaxIndent(bottomRightMargin), + mFlipHorizontal(flipHorizontal), + mFlipVertical(flipVertical) + { + } + + Vector3 operator()(const Vector3& constPosition, + const PropertyInput& sizeProperty, + const PropertyInput& parentOriginProperty, + const PropertyInput& anchorPointProperty, + const PropertyInput& referenceSizeProperty, + const PropertyInput& alternativeOffsetProperty) + { + const Vector3& size = sizeProperty.GetVector3(); + const Vector3& origin = parentOriginProperty.GetVector3(); + const Vector3& anchor = anchorPointProperty.GetVector3(); + const Vector3& referenceSize = referenceSizeProperty.GetVector3(); + const Vector2& alternativeOffset = alternativeOffsetProperty.GetVector2(); + + Vector3 newPosition(constPosition); + + // Get actual position of Actor relative to parent's Top-Left. + Vector3 position(constPosition + origin * referenceSize); + + // if top-left corner is outside of Top-Left bounds, then push back in screen. + Vector3 corner(position - size * anchor - mMinIndent); + + if(mFlipHorizontal && corner.x < 0.0f) + { + corner.x = 0.0f; + newPosition.x += size.width + alternativeOffset.width; + } + + if(mFlipVertical && corner.y < 0.0f) + { + corner.y = 0.0f; + newPosition.y += size.height + alternativeOffset.height; + } + + newPosition.x -= std::min(corner.x, 0.0f); + newPosition.y -= std::min(corner.y, 0.0f); + + // if bottom-right corner is outside of Bottom-Right bounds, then push back in screen. + corner += size - referenceSize + mMinIndent + mMaxIndent; + + if(mFlipHorizontal && corner.x > 0.0f) + { + corner.x = 0.0f; + newPosition.x -= size.width + alternativeOffset.width; + } + + if(mFlipVertical && corner.y > 0.0f) + { + corner.y = 0.0f; + newPosition.y -= size.height + alternativeOffset.height; + } + + newPosition.x -= std::max(corner.x, 0.0f); + newPosition.y -= std::max(corner.y, 0.0f); + + return newPosition; + } + + Vector3 mMinIndent; ///< Top-Left Margin + Vector3 mMaxIndent; ///< Bottom-Right Margin. + bool mFlipHorizontal; ///< Whether to flip actor's position if exceeds horizontal screen bounds + bool mFlipVertical; ///< Whether to flip actor's position if exceeds vertical screen bounds +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +const char* const TextInputPopup::SIGNAL_PRESSED = "pressed"; +const char* const TextInputPopup::SIGNAL_HIDE_FINISHED = "hide-finished"; +const char* const TextInputPopup::SIGNAL_SHOW_FINISHED = "show-finished"; + +TextInputPopup::TextInputPopup() +: mState(StateHidden), + mRootActor(Layer::New()), + mPressedSignal(), + mHideFinishedSignal(), + mShowFinishedSignal() +{ + mAlternativeOffsetProperty = mRootActor.RegisterProperty( POPUP_ALTERNATIVE_OFFSET, Vector2::ZERO ); + mRootActor.SetParentOrigin( ParentOrigin::CENTER ); + mRootActor.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + // constrain popup to size of parent. +} + +Actor TextInputPopup::Self() +{ + return mRootActor; +} + +void TextInputPopup::AddToStage() +{ + // TODO: Confinement constraint borders should be defined by the application. + // It should also not use the stage directly, instead it should add to parent container. + Stage::GetCurrent().Add(mRootActor); + + ApplyConfinementConstraint(); +} + +void TextInputPopup::ApplyConfinementConstraint() +{ + mRootActor.RemoveConstraints(); + Constraint constraint = Constraint::New( Actor::POSITION, + LocalSource( Actor::SIZE ), + LocalSource( Actor::PARENT_ORIGIN ), + LocalSource( Actor::ANCHOR_POINT ), + ParentSource( Actor::SIZE ), + LocalSource( mAlternativeOffsetProperty ), + ConfinementConstraint( DEFAULT_POPUP_INDICATOR_OFFSET, + Vector2::ZERO, + false, + true) ); + mRootActor.ApplyConstraint(constraint); +} + +void TextInputPopup::RemoveFromStage() +{ + Actor rootActor = Self(); + Stage::GetCurrent().Remove( rootActor ); +} + +void TextInputPopup::Clear() +{ + if ( mBackground ) + { + mRootActor.Remove( mBackground ); + mBackground.Reset(); + mButtonContainer.clear(); + mDividerContainer.clear(); + + RemoveFromStage(); + mRootActor.RemoveConstraints(); + + mState = StateHidden; + } +} + +Toolkit::TextView TextInputPopup::CreateOptionText( const MarkupProcessor::StyledTextArray& styledCaption ) +{ + Toolkit::TextView label = Toolkit::TextView::New( styledCaption ); + label.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); + label.SetWidthExceedPolicy( Toolkit::TextView::Fade ); + label.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + label.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + label.SetPosition( 0.0f, TEXT_POSITION_OFFSET ); + + return label; +} + +ImageActor TextInputPopup::CreateOptionIcon( Image iconImage ) +{ + ImageActor icon = ImageActor::New( iconImage ); + + icon.SetSize( DEFAULT_ICON_SIZE ); + icon.SetParentOrigin( ParentOrigin::TOP_CENTER ); + icon.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + icon.SetPosition( 0.0f, ICON_POSITION_OFFSET ); + + return icon; +} + +void TextInputPopup::CreatePopUpBackground() +{ + // Create background-panel if not already created (required if we have at least one option) + if ( !mBackground ) + { + Image backgroundImage = Image::New( DEFAULT_PANEL_BACKGROUND ); + + mBackground = ImageActor::New( backgroundImage ); + // Expand background from bottom-center of root actor. + mBackground.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + mBackground.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + mBackground.SetStyle( ImageActor::STYLE_NINE_PATCH ); + + mBackground.SetNinePatchBorder( Vector4(13.0f, 13.0f, 13.0f, 13.0f) ); + + Self().Add( mBackground ); + mContentSize = POPUP_TEXT_OFFSET; + + Hide(false); + AddToStage(); + + // Add Tail too. + Image tailImage = Image::New( DEFAULT_POPUP_TAIL_BOTTOM ); + + mTail = ImageActor::New( tailImage ); + mTail.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + mTail.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + mBackground.Add( mTail ); + // TODO: Make tail visible, and positioned in relation to original intended position of popup (i.e. before constrained effects) + mTail.SetVisible(false); + } +} + +void TextInputPopup::CreateDivider() +{ + if(mButtonContainer.size() > 0) + { + Image dividerImage = Image::New( DEFAULT_PANEL_BUTTON_DIVIDER ); + ImageActor divider = ImageActor::New( dividerImage ); + divider.SetParentOrigin( ParentOrigin::TOP_LEFT ); + divider.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + divider.SetPosition( Vector3( mContentSize.width, POPUP_TEXT_OFFSET.y + 5.0f, 0.0f ) ); + // Keep track of all the dividers. As their height's need to be updated to the max. of all + // buttons currently added. + mDividerContainer.push_back(divider); + + mBackground.Add( divider ); + mContentSize.width += DIVIDER_WIDTH; + } +} + +ImageActor TextInputPopup::CreatePressedBackground( const Vector3 requiredSize, const bool finalFlag ) +{ + std::string pressedImageFilename; + Vector4 pressedImageBorder; + Vector2 pressedImageSize; + + if(mButtonContainer.size() == 0) // LEFT + { + pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_LEFT; + pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_LEFT_BORDER; + } + else if(!finalFlag) // CENTER + { + pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_CENTER; + pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_CENTER_BORDER; + } + else // RIGHT + { + pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_RIGHT; + pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_RIGHT_BORDER; + } + + Image pressedImage = Image::New( pressedImageFilename ); + ImageActor pressedImageBg = ImageActor::New( pressedImage ); + pressedImageBg.SetStyle( ImageActor::STYLE_NINE_PATCH ); + pressedImageBg.SetNinePatchBorder( pressedImageBorder ); + pressedImageBg.SetSize ( requiredSize ); + pressedImageBg.SetParentOrigin( ParentOrigin::CENTER ); + pressedImageBg.SetAnchorPoint( AnchorPoint::CENTER ); + + return pressedImageBg; +} + +void TextInputPopup::AddOption(const std::string& name, const std::string& caption, const Image iconImage, bool finalOption) +{ + CreatePopUpBackground(); + + CreateDivider(); + + // Create a Button with Text, Icon and highlight when pressed + + Toolkit::PushButton button = Toolkit::PushButton::New(); + button.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); + button.SetName( name ); + + // Create container for text and icon when not pressed + Actor iconTextContainer = Actor::New(); + iconTextContainer.SetParentOrigin( ParentOrigin::TOP_LEFT ); + iconTextContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + // 1. Add text. + TextStyle style; + style.SetFontPointSize( PointSize( DEFAULT_UI_FONT_SIZE ) ); + MarkupProcessor::StyledTextArray styledCaption; + styledCaption.push_back( MarkupProcessor::StyledText( Text( caption ), style ) ); + Toolkit::TextView label = CreateOptionText( styledCaption ); + label.SetName( name ); + + iconTextContainer.Add( label ); + + // Get natural size of text and then constrain it to bounds. + const Vector3 textSize = label.GetNaturalSize(); + const Vector3 constrainedTextSize = Min( textSize, TEXT_LABEL_MAX_SIZE ); + Vector3 buttonSize( Max(constrainedTextSize + BUTTON_TEXT_ENLARGE, BUTTON_TEXT_MINIMUM_SIZE) ); + buttonSize = ( Min(buttonSize, BUTTON_TEXT_MAXIMUM_SIZE) ); + label.SetSize( Min( buttonSize + BUTTON_TEXT_ENLARGE, constrainedTextSize ) ); + + button.SetParentOrigin( ParentOrigin::TOP_LEFT ); + button.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + button.SetSize( buttonSize ); + button.SetPosition( Vector3( mContentSize.width, POPUP_TEXT_OFFSET.y, 0.0f ) ); + + // 2. Add icon + ImageActor icon = CreateOptionIcon( iconImage ); + + iconTextContainer.Add( icon ); + + // 3. Add highlight - Pressed state in Pushbutton needs a new image which means creating the text and icon again but including a highlight this time. + ImageActor pressedImageBg = CreatePressedBackground( buttonSize, finalOption ); + + Actor iconPressedTextContainer = Actor::New(); + iconPressedTextContainer.SetDrawMode( DrawMode::OVERLAY ); + + Toolkit::TextView pressedLabel = CreateOptionText( styledCaption ); + pressedLabel.SetSize( Min( buttonSize, TEXT_LABEL_MAX_SIZE ) ); + ImageActor pressedIcon = CreateOptionIcon( iconImage ); + + iconPressedTextContainer.Add( pressedImageBg ); + iconPressedTextContainer.Add( pressedLabel ); + iconPressedTextContainer.Add( pressedIcon ); + + // Set Pressed button Image + iconPressedTextContainer.SetSize( buttonSize ); + button.SetPressedImage( iconPressedTextContainer ); + + // Set Normal button Image + iconTextContainer.SetSize( buttonSize ); + button.SetButtonImage( iconTextContainer ); + mBackground.Add( button ); + + // Update content size (represents size of all content i.e. from top-left of first button, to bottom-right of last button) + mContentSize.width += buttonSize.width; + mContentSize.height = std::max(mContentSize.height, buttonSize.height); + mButtonContainer.push_back(button); + + // resize all dividers based on the height content (i.e. max of all button heights) + const float dividerHeight = mContentSize.height - DIVIDER_MARGIN; + for(ActorIter i = mDividerContainer.begin(); i != mDividerContainer.end(); ++i) + { + i->SetSize( DIVIDER_WIDTH, dividerHeight ); + } + + Vector3 popupSize( Max(mContentSize + POPUP_TEXT_ENLARGE, POPUP_MINIMUM_SIZE) ); + + mBackground.SetSize( popupSize ); + // Make Root Actor reflect the size of its content + mRootActor.SetSize( popupSize ); + mTail.SetPosition(Vector3(0.0f, -20.0f, 0.0f)); + + button.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed ); +} + +void TextInputPopup::Hide(bool animate) +{ + if(mBackground) + { + if(mAnimation) + { + mAnimation.Clear(); + mAnimation.Reset(); + } + + if(animate) + { + mAnimation = Animation::New( HIDE_POPUP_ANIMATION_DURATION ); + mAnimation.AnimateTo( Property(mBackground, Actor::SCALE), Vector3::ZERO, AlphaFunctions::EaseOut ); + mAnimation.AnimateTo( Property(mBackground, Actor::COLOR_ALPHA), 0.0f, AlphaFunctions::EaseOut ); + mAnimation.Play(); + + mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnHideFinished ); + mState = StateHiding; + } + else + { + mBackground.SetProperty(Actor::SCALE, Vector3::ZERO); + mBackground.SetProperty(Actor::COLOR_ALPHA, 0.0f); + mState = StateHidden; + } + } +} + +void TextInputPopup::Show(bool animate) +{ + if(mBackground) + { + if(mAnimation) + { + mAnimation.Clear(); + mAnimation.Reset(); + } + + if(animate) + { + mAnimation = Animation::New( SHOW_POPUP_ANIMATION_DURATION ); + mAnimation.AnimateTo( Property(mBackground, Actor::SCALE), Vector3::ONE, AlphaFunctions::EaseOut ); + mAnimation.AnimateTo( Property(mBackground, Actor::COLOR_ALPHA), 1.0f, AlphaFunctions::EaseOut ); + mAnimation.Play(); + + mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnShowFinished ); + mState = StateShowing; + } + else + { + mBackground.SetProperty(Actor::SCALE, Vector3::ONE); + mBackground.SetProperty(Actor::COLOR_ALPHA, 1.0f); + mState = StateShown; + } + } +} + +void TextInputPopup::SetAlternativeOffset(Vector2 offset) +{ + mRootActor.SetProperty( mAlternativeOffsetProperty, offset ); + ApplyConfinementConstraint(); +} + +TextInputPopup::State TextInputPopup::GetState(void) const +{ + return mState; +} + +Actor TextInputPopup::GetRootActor() const +{ + return mRootActor; +} + +bool TextInputPopup::OnButtonPressed( Toolkit::Button button ) +{ + mPressedSignal.Emit( button ); + return false; +} + +void TextInputPopup::OnHideFinished(Animation& source) +{ + source.FinishedSignal().Disconnect( this, &TextInputPopup::OnHideFinished ); + Clear(); + mState = StateHidden; + mHideFinishedSignal.Emit( *this ); +} + +void TextInputPopup::OnShowFinished(Animation& source) +{ + source.FinishedSignal().Disconnect( this, &TextInputPopup::OnShowFinished ); + mState = StateShown; + mShowFinishedSignal.Emit( *this ); +} + +TextInputPopup::PressedSignalV2& TextInputPopup::PressedSignal() +{ + return mPressedSignal; +} + +TextInputPopup::HideFinishedSignalV2& TextInputPopup::HideFinishedSignal() +{ + return mHideFinishedSignal; +} + +TextInputPopup::ShowFinishedSignalV2& TextInputPopup::ShowFinishedSignal() +{ + return mShowFinishedSignal; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-input/text-input-popup-impl.h b/dali-toolkit/internal/controls/text-input/text-input-popup-impl.h new file mode 100644 index 0000000..1f58905 --- /dev/null +++ b/dali-toolkit/internal/controls/text-input/text-input-popup-impl.h @@ -0,0 +1,234 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_INPUT_POPUP_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_INPUT_POPUP_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Button; + +namespace Internal +{ + +class TextInputPopup : public ConnectionTracker +{ + +public: + + enum State + { + StateHidden, + StateHiding, + StateShowing, + StateShown + }; + + // Signal names + static const char* const SIGNAL_PRESSED; + static const char* const SIGNAL_HIDE_FINISHED; + static const char* const SIGNAL_SHOW_FINISHED; + + // Popup Button Pressed + typedef SignalV2< bool( Toolkit::Button ) > PressedSignalV2; + + // Popup Hide Finished + typedef SignalV2< void( TextInputPopup& ) > HideFinishedSignalV2; + + // Popup Show Finished + typedef SignalV2< void( TextInputPopup& ) > ShowFinishedSignalV2; + + /** + * Signal emitted when the button is touched. + */ + PressedSignalV2& PressedSignal(); + + /** + * Signal emitted when popup is completely hidden + * @note Only occurs after a Show() call with animation enabled. + */ + HideFinishedSignalV2& HideFinishedSignal(); + + /** + * Signal emitted when popup is completely shown + * @note Only occurs after a Hide() call with animation enabled. + */ + ShowFinishedSignalV2& ShowFinishedSignal(); + +public: + + /** + * Default constructor + * Creates an empty popup base actor (no content i.e. invisible) + */ + TextInputPopup(); + + /** + * @return The root actor of for this popup is returned. + */ + Actor Self(); + + /** + * Clears popup options (popup no longer exists) + */ + void Clear(); + + /** + * Create the label + * @param[in] styledCaption The text to be displayed + * @return the newly created label + */ + Toolkit::TextView CreateOptionText( const MarkupProcessor::StyledTextArray& styledCaption ); + + /** + * Create the label + * @param[in] iconImage the image to be used + * @return the newly created Image actor to be used as the icon + */ + ImageActor CreateOptionIcon( Image iconImage ); + + /** + * Creates and sets up the popup background + */ + void CreatePopUpBackground(); + + /** + * Create divider if multiple options + */ + void CreateDivider(); + + /** + * Create a background to be used when button pressed + * @param[in] requiredSize size Image actor should be + * @param[in] finalFlag flag to be set if option is the final one. + * @return Returns an Image Actor to be used a pressed background + */ + ImageActor CreatePressedBackground( const Vector3 requiredSize, const bool finalFlag ); + + /** + * Adds a popup option. + * @note Creates popup frame if not already created. + * @param[in] name The unique name for this option. + * @param[in] caption The caption (label) for this option + * @param[in] iconImage Image to displayed with text. + * @param[in] finalOption Flag to indicate that this is the final option. + * (set to true on the last option you add) + */ + void AddOption(const std::string& name, const std::string& caption, const Image iconImage, bool finalOption); + + /** + * Hides the popup + * @param[in] animate (optional) whether to animate popup to hide state over time (i.e. tween). + */ + void Hide(bool animate = true); + + /** + * Shows the popup + * @param[in] animate (optional) whether to animate popup to show state over time (i.e. tween). + */ + void Show(bool animate = true); + + /** + * Sets Alternative offset property. + * The alternative offset property is how much to move in the horizontal and vertical + * axes when the popup goes out of the screen on the left/right sides or top/bottom sides. + * @param[in] offset Vector holding the left/right offset (x) and top/bottom offset (y) + */ + void SetAlternativeOffset(Vector2 offset); + + /** + * Returns the current state of the popup. + * @return The state of the popup see enum State + */ + State GetState(void) const; + + /** + * Get the root actor which the buttons are added to. + * @return the root actor + */ + Actor GetRootActor() const; + +private: + + /** + * Adds popup to the stage (ideally on a separate top-most layer and as an overlay) + */ + void AddToStage(); + + /** + * Applies constraint to keep Popup in view within the desired area. + */ + void ApplyConfinementConstraint(); + + /** + * Removes popup from the stage. + */ + void RemoveFromStage(); + + /** + * Called when a button is pressed in the popup + * @param[in] button The button pressed. + */ + bool OnButtonPressed( Toolkit::Button button ); + + /** + * Invoked upon popup Hide animation completing. + * @note Only called for animating hide, not called for instantaneous (animate = false) + * @param[in] source The animation which completed. + */ + void OnHideFinished(Animation& source); + + /** + * Invoked upon popup Show animation completing. + * @note Only called for animating show, not called for instantaneous (animate = false) + * @param[in] source The animation which completed. + */ + void OnShowFinished(Animation& source); + +private: + + State mState; ///< Popup State. + Actor mRootActor; ///< The actor which all popup content is added to (i.e. panel and buttons) + Property::Index mAlternativeOffsetProperty; ///< Property [Vector3] how much to offset the popup if it goes out of the screen + ImageActor mBackground; ///< The background popup panel + ImageActor mTail; ///< The tail for the popup + Vector3 mContentSize; ///< Size of Content (i.e. Buttons) + ActorContainer mButtonContainer; ///< List of buttons added to popup. + ActorContainer mDividerContainer; ///< List of dividers added to popup. + Animation mAnimation; ///< Popup Hide/Show animation. + + PressedSignalV2 mPressedSignal; ///< Signal emitted when a button within the popup is pressed. + HideFinishedSignalV2 mHideFinishedSignal; ///< Signal emitted when popup is completely hidden + ShowFinishedSignalV2 mShowFinishedSignal; ///< Signal emitted when popup is completely shown + +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ diff --git a/dali-toolkit/internal/controls/text-view/relayout-utilities.cpp b/dali-toolkit/internal/controls/text-view/relayout-utilities.cpp new file mode 100644 index 0000000..56f20c6 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/relayout-utilities.cpp @@ -0,0 +1,1958 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "relayout-utilities.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include "text-view-line-processor.h" +#include "text-view-word-processor.h" +#include "text-view-processor-helper-functions.h" +#include "text-view-processor-dbg.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewRelayout +{ + +const float MINIMUM_FADE_BOUNDARY = 0.05f; // When the fade boundary is the same as the text-view boundary, this constant reduces it in order to avoid a zero division. + +RelayoutParameters::RelayoutParameters() +: mPositionOffset(), + mLineSize(), + mWordSize(), + mCharacterSize(), + mIndices(), + mCharacterGlobalIndex( 0u ), + mIsFirstCharacter( false ), + mIsFirstCharacterOfWord( false ), + mIsNewLine( false ), + mIsNewLineCharacter( false ), + mIsWhiteSpace( false ), + mIsVisible( false ) +{ +} + +RelayoutParameters::~RelayoutParameters() +{ +} + +FadeParameters::FadeParameters() +: mRightFadeBoundary( 0.f ), + mRightFadeThreshold( 0.f ), + mRightFadeBoundaryOffset( 0.f ), + mRightFadeThresholdOffset( 0.f ), + mRightAlphaCoeficients(), + mLeftFadeBoundary( 0.f ), + mLeftFadeThreshold( 0.f ), + mLeftFadeBoundaryOffset( 0.f ), + mLeftFadeThresholdOffset( 0.f ), + mLeftAlphaCoeficients(), + mTopFadeBoundary( 0.f ), + mTopFadeThreshold( 0.f ), + mTopFadeBoundaryOffset( 0.f ), + mTopFadeThresholdOffset( 0.f ), + mTopAlphaCoeficients(), + mBottomFadeBoundary( 0.f ), + mBottomFadeThreshold( 0.f ), + mBottomFadeBoundaryOffset( 0.f ), + mBottomFadeThresholdOffset( 0.f ), + mBottomAlphaCoeficients(), + mIsPartiallyVisible( false ) +{ +} + +FadeParameters::~FadeParameters() +{ +} + +EllipsizeParameters::EllipsizeParameters() +: mPosition(), + mLineDescender( 0.f ), + mLineWidth( 0.f ), + mEllipsizeBoundary(), + mFirstIndex( 0u ), + mLastIndex( 0u ), + mEllipsizeLine( false ), + mIsLineWidthFullyVisible( false ), + mIsLineHeightFullyVisible( false ), + mIsNextLineFullyVisibleHeight( false ), + mCreateEllipsizedTextActors( false ), + mLineFits( false ), + mWordFits( false ) +{ +} + +EllipsizeParameters::~EllipsizeParameters() +{ +} + +UnderlineInfo::UnderlineInfo() +: mMaxHeight( 0.f ), + mMaxThickness( 0.f ), + mPosition( 0.f ) +{ +} + +UnderlineInfo::~UnderlineInfo() +{ +} + +TextUnderlineStatus::TextUnderlineStatus() +: mUnderlineInfo(), + mCharacterGlobalIndex( 0u ), + mLineGlobalIndex( 0u ), + mCurrentUnderlineStatus( false ) +{ +} + +TextUnderlineStatus::~TextUnderlineStatus() +{ +} + +SubLineLayoutInfo::SubLineLayoutInfo() +: mLineLength( 0.f ), + mMaxCharHeight( 0.f ), + mMaxAscender( 0.f ) +{ +} + +SubLineLayoutInfo::~SubLineLayoutInfo() +{ +} + +/** + * Whether the given text-actor exceeds the left or the right boundary of the text-view. + * + * @param[in] position The position of the text-actor. + * @param[in] size The size of the text-actor. + * @param[in] parantSize The size of the text-view. + * + * @return \e true if the text-actor exceeds the left or the right boundary of the text-view. + */ +bool IsExceedingWidth( const Vector3& position, const Size& size, const Size& parentSize ) +{ + return ( ( position.x < 0.f ) || + ( position.x + size.width > parentSize.width ) ); +} + +/** + * Whether the given text-actor exceeds the top or the bottom boundary of the text-view. + * + * @param[in] position The position of the text-actor. + * @param[in] size The size of the text-actor. + * @param[in] parantSize The size of the text-view. + * + * @return \e true if the text-actor exceeds the top or the bottom boundary of the text-view. + */ +bool IsExceedingHeight( const Vector3& position, const Size& size, const Size& parentSize ) +{ + return ( ( position.y > parentSize.height ) || + ( position.y < size.height ) ); +} + +/** + * Calculates the line length adding the new word or character width. + * + * It also returns the length of white spaces if they are at the end of the line. + * + * @param[in] isWhiteSpace Whether the word is a white space. + * @param[in] width The width of the character or word. + * @param[in] parentWidth The parent width. + * @param[out] found Whether the sum of the new character or word is exceding the parent's width. + * @param[out] lineLength The length of the portion of line which doesn't exceed the parant's width + * @param[out] endWhiteSpaceLength The length of white spaces which are at the end of the line. + */ +void CalculateLineLength( const bool isWhiteSpace, const float width, const float parentWidth, bool& found, float& lineLength, float& endWhiteSpaceLength ) +{ + if( lineLength + width > parentWidth ) + { + found = true; + lineLength -= endWhiteSpaceLength; + } + else + { + lineLength += width; + + if( isWhiteSpace ) + { + endWhiteSpaceLength += width; + } + else + { + endWhiteSpaceLength = 0.f; + } + } +} + +struct CurrentTextActorInfo +{ + TextActor textActor; + Text text; + Vector3 position; + Size size; + Vector4 color; + Vector4 gradientColor; + Vector2 startPoint; + Vector2 endPoint; +}; + +void SetVisualParameters( CurrentTextActorInfo& currentTextActorInfo, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData, + const float lineHeight ) +{ + currentTextActorInfo.textActor.SetTextColor( currentTextActorInfo.color ); + currentTextActorInfo.textActor.SetGradientColor( currentTextActorInfo.gradientColor ); + currentTextActorInfo.textActor.SetGradientStartPoint( currentTextActorInfo.startPoint ); + currentTextActorInfo.textActor.SetGradientEndPoint( currentTextActorInfo.endPoint ); + + // The italics offset is used in the offscreen rendering. When text is in italics, it may exceed the text-view's boundary + // due to the trick used to implement it. + const Radian& italicsAngle = currentTextActorInfo.textActor.GetItalicsAngle(); + const float italicsOffset = lineHeight * std::tan( italicsAngle ); + relayoutData.mTextLayoutInfo.mMaxItalicsOffset = std::max( relayoutData.mTextLayoutInfo.mMaxItalicsOffset, italicsOffset ); + + // Sets the sort modifier value. + currentTextActorInfo.textActor.SetSortModifier( visualParameters.mSortModifier ); + + // Enables or disables the blending. + currentTextActorInfo.textActor.SetBlendMode( !visualParameters.mSnapshotModeEnabled ? BlendingMode::ON : BlendingMode::OFF ); +} + +void CalculateSubLineLayout( const float parentWidth, + const TextViewProcessor::TextInfoIndices& indices, + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo, + const HorizontalWrapType splitPolicy, + const float shrinkFactor, + SubLineLayoutInfo& subLineInfo ) +{ + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + + float endWhiteSpaceLength = 0.f; + + std::size_t wordIndex = indices.mWordIndex; + std::size_t characterIndex = indices.mCharacterIndex; + float lineOffset = 0.f; + bool found = false; + bool isFirstCharacter = true; + for( TextViewProcessor::WordGroupLayoutInfoContainer::const_iterator wordGroupIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex, + wordGroupEndIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + ( wordGroupIt != wordGroupEndIt ) && !found; + ++wordGroupIt ) + { + const TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *wordGroupIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::const_iterator wordIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordIndex, + wordEndIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + ( wordIt != wordEndIt ) && !found; + ++wordIt ) + { + const TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordIt ); + + const bool isWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType; + + bool splitByCharacter = false; + + switch( splitPolicy ) + { + case WrapByCharacter: + { + splitByCharacter = true; + break; + } + case WrapByWord: + case WrapByLine: // Fall through + { + splitByCharacter = false; + break; + } + case WrapByWordAndSplit: + { + splitByCharacter = ( wordLayoutInfo.mSize.width * shrinkFactor > parentWidth ); + break; + } + case WrapByLineAndSplit: + { + if( ( 0 != characterIndex ) || + ( ( 0 == characterIndex ) && ( lineOffset + wordLayoutInfo.mSize.width * shrinkFactor > parentWidth ) ) ) + { + splitByCharacter = true; + } + else + { + lineOffset += wordLayoutInfo.mSize.width * shrinkFactor; + splitByCharacter = false; + } + } + } + + if( splitByCharacter ) + { + for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator charIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + characterIndex, + charEndIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + ( charIt != charEndIt ) && !found; + ++charIt ) + { + const TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *charIt ); + CalculateLineLength( isWhiteSpace, characterLayoutInfo.mSize.width * shrinkFactor, parentWidth, found, subLineInfo.mLineLength, endWhiteSpaceLength ); + if( !found || isFirstCharacter ) + { + subLineInfo.mMaxCharHeight = std::max( subLineInfo.mMaxCharHeight, characterLayoutInfo.mSize.height ); + subLineInfo.mMaxAscender = std::max( subLineInfo.mMaxAscender, characterLayoutInfo.mAscender ); + } + + // All characters for word 'wordIndex' have been processed. + // Next word need to process all characters, so the characterIndex is reset to 0. + characterIndex = 0; + isFirstCharacter = false; + } + + lineOffset += subLineInfo.mLineLength; + } + else + { + CalculateLineLength( isWhiteSpace, wordLayoutInfo.mSize.width * shrinkFactor, parentWidth, found, subLineInfo.mLineLength, endWhiteSpaceLength ); + if( !found || isFirstCharacter ) + { + subLineInfo.mMaxCharHeight = std::max( subLineInfo.mMaxCharHeight, wordLayoutInfo.mSize.height ); + subLineInfo.mMaxAscender = std::max( subLineInfo.mMaxAscender, wordLayoutInfo.mAscender ); + } + isFirstCharacter = false; + } + } + + // All words for group 'groupIndex' have been processed. + // Next group need to process all words, so the wordIndex is reset to 0. + wordIndex = 0; + } + + subLineInfo.mMaxCharHeight *= shrinkFactor; + subLineInfo.mMaxAscender *= shrinkFactor; +} + +float CalculateXoffset( const Toolkit::Alignment::Type horizontalTextAlignment, const float parentWidth, const float wholeTextWidth ) +{ + float xOffset( 0.f ); + switch( horizontalTextAlignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + // nothing to do. + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + xOffset = 0.5f * ( parentWidth - wholeTextWidth ); + break; + } + case Toolkit::Alignment::HorizontalRight: + { + xOffset = parentWidth - wholeTextWidth; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateXoffset: Wrong horizontal text alignment. Did you set a vertical one?" ); + } + } + + return xOffset; +} + +float CalculateYoffset( const Toolkit::Alignment::Type verticalTextAlignment, const float parentHeight, const float wholeTextHeight ) +{ + float yOffset( 0.f ); + switch( verticalTextAlignment ) + { + case Toolkit::Alignment::VerticalTop: + { + // nothing to do. + break; + } + case Toolkit::Alignment::VerticalCenter: + { + yOffset = 0.5f * ( parentHeight - wholeTextHeight ); + break; + } + case Toolkit::Alignment::VerticalBottom: + { + yOffset = parentHeight - wholeTextHeight; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateXoffset: Wrong vertical text alignment. Did you set an horizontal one?" ); + } + } + + return yOffset; +} + +float CalculateJustificationOffset( const Toolkit::TextView::LineJustification justification, const float wholeTextWidth, const float lineLength ) +{ + float offset = 0.f; + switch( justification ) + { + case Toolkit::TextView::Left: + { + offset = 0.f; + break; + } + case Toolkit::TextView::Center: + { + offset = 0.5f * ( wholeTextWidth - lineLength ); + break; + } + case Toolkit::TextView::Right: + { + offset = wholeTextWidth - lineLength; + break; + } + case Toolkit::TextView::Justified: + { + offset = 0.f; + break; + } + } + + return offset; +} + +bool IsVisible( const Vector3& position, const Size& size, const Size& parentSize, VisibilityTestType type ) +{ + bool visible = false; + + switch( type ) + { + case FULLY_VISIBLE: + { + // Whether the text-actor is fully inside the boundaries of the text-view. + visible = ( ( position.x >= 0.f ) && ( position.x + size.width <= parentSize.width ) && + ( position.y >= size.height ) && ( position.y <= parentSize.height ) ); + break; + } + case FULLY_VISIBLE_WIDTH: + { + // Whether the text-actor is between the left and right boundaries of the text-view. + visible = ( ( position.x >= 0.f ) && ( position.x + size.width <= parentSize.width ) ); + break; + } + case FULLY_VISIBLE_HEIGHT: + { + // Whether the text-actor is between the top and bottom boundaries of the text-view. + visible = ( ( position.y >= size.height ) && ( position.y <= parentSize.height ) ); + break; + } + case PARTIALLY_VISIBLE: + { + // Whether the text-actor is partially inside the boundaries of the text-view. + visible = ( ( position.x < parentSize.width ) && + ( position.x + size.width > 0.f ) && + ( position.y > 0.f ) && + ( position.y - size.height < parentSize.height ) ); + break; + } + case PARTIALLY_VISIBLE_WIDTH: + { + // Whether the text-actor is partially inside the area defined by the left and the right boundaries of the text-view. + // It may not be partially inside the text-view. + visible = ( ( position.x < parentSize.width ) && + ( position.x + size.width > 0.f ) ); + break; + } + case PARTIALLY_VISIBLE_HEIGHT: + { + // Whether the text-actor is partially inside the area defined by the top and the bottom boundaries of the text-view. + // It may not be partially inside the text-view. + visible = ( ( position.y > 0.f ) && + ( position.y - size.height < parentSize.height ) ); + break; + } + } + + return visible; +} + +Vector2 CalculateRectParameters( const Vector2& p0, const Vector2& p1 ) +{ + const float gradient = ( p1.y - p0.y ) / ( p1.x - p0.x ); + + return Vector2( gradient, p0.y - gradient * p0.x ); +} + +void UpdateAlignment( const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // Calculates an offset to align the whole text within the text-view's boundary accordingly with the set alignment and justification options. + // The offset could be negative if the whole text is bigger than the boundary of the text-view. + + // If the exceed policy is ellipsize at the end, negative offsets are not wanted. + // In that case, it will align the line to the left and/or top, and ellipsize the end. + const bool ellipsizeAlignToLeft = ( layoutParameters.mExceedPolicy == TextView::EllipsizeEndOriginal ) || ( layoutParameters.mExceedPolicy == TextView::EllipsizeEnd ); + const bool ellipsizeAlignToTop = ( layoutParameters.mExceedPolicy == TextView::EllipsizeEnd ); + + RelayoutParameters relayoutParameters; + + // Calculates the vertical and horizontal offsets. + const float textHorizontalOffset = CalculateXoffset( layoutParameters.mHorizontalAlignment, relayoutData.mTextViewSize.width, relayoutData.mTextSizeForRelayoutOption.width ); + const float textVerticalOffset = CalculateYoffset( layoutParameters.mVerticalAlignment, relayoutData.mTextViewSize.height, relayoutData.mTextSizeForRelayoutOption.height ); + + std::size_t lineJustificationIndex = 0; // Index to the first position of the vector which stores all line justification info. + std::size_t infoTableCharacterIndex = 0; + + relayoutParameters.mIndices.mLineIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + relayoutParameters.mIndices.mGroupIndex = 0; + float justificationOffset = 0.f; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + relayoutParameters.mIndices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + relayoutParameters.mIndices.mCharacterIndex = 0; + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex, ++infoTableCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + // Calculate line justification offset. + if( lineJustificationIndex < relayoutData.mLineJustificationInfo.size() ) + { + const TextView::LineJustificationInfo lineJustificationInfo( *( relayoutData.mLineJustificationInfo.begin() + lineJustificationIndex ) ); + + if( relayoutParameters.mIndices == lineJustificationInfo.mIndices ) + { + justificationOffset = CalculateJustificationOffset( layoutParameters.mLineJustification, relayoutData.mTextSizeForRelayoutOption.width, lineJustificationInfo.mLineLength ); + ++lineJustificationIndex; // increase the index to point the next position in the vector. + } + } + + // Deletes the offsets if the exceed policies are EllipsizeEnd. + const float horizontalOffset = textHorizontalOffset + justificationOffset; + characterLayoutInfo.mOffset.x = ( ellipsizeAlignToLeft && ( horizontalOffset < 0.f ) ) ? 0.f : horizontalOffset; + characterLayoutInfo.mOffset.y = ( ellipsizeAlignToTop && ( textVerticalOffset < 0.f ) ) ? 0.f : textVerticalOffset; + + // Updates the size and position table for text-input with the alignment offset. + Vector3 positionOffset( characterLayoutInfo.mPosition ); + + std::vector::iterator infoTableIt = relayoutData.mCharacterLayoutInfoTable.begin() + infoTableCharacterIndex; + Toolkit::TextView::CharacterLayoutInfo& characterTableInfo( *infoTableIt ); + + characterTableInfo.mPosition.x = positionOffset.x + characterLayoutInfo.mOffset.x; + characterTableInfo.mPosition.y = positionOffset.y + characterLayoutInfo.mOffset.y; + + positionOffset.x += characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor; + } // end characters + } // end words + } // end group of words + } // end lines +} + +void CalculateBearing( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + TextView::RelayoutData& relayoutData ) +{ + // No bearing used. + // + // gggggggggg + // gggggggggg + // gggg gggg + // gggg gggg + // gggg gggg + // gggg gggg + // gggg gggg + // gggg gggg + // ggggg gggggggggg bb ggggg + // gg gg gggggggggg bb gg gg + // gg gg gggg bb gg gg + // gg gg gggg bb gg gg + // ggggg gg gggg bbbbbbb ggggg + // gg gg gggg bb bb gg + // g gg gggggggggg bb bb g gg + // ggggg gggggggggg bbbbbbb ggggg + // + // Bearing used. + // + // gggggggggg + // gggggggggg + // gggg gggg bb + // gggg gggg bb + // gggg gggg bb + // ggggg gggg gggg bb ggggg + // gg gg gggg gggg bbbbbbb gg gg + // gg gg gggg gggg bb bb gg gg + // gg gg gggggggggg bb bb gg gg + // ggggg gggggggggg bbbbbbb ggggg + // gg gggg gg + // g gg gggg g gg + // ggggg gg gggg ggggg + // gg gggg + // gggggggggg + // gggggggggg + + const Toolkit::TextView::LineLayoutInfo& lineInfo( *( relayoutData.mLines.end() - 1 ) ); + const float bearingOffset = ( lineInfo.mSize.height - lineInfo.mAscender ) - ( characterLayoutInfo.mSize.height - characterLayoutInfo.mAscender ); + + characterLayoutInfo.mPosition.y -= bearingOffset * relayoutData.mShrinkFactor; +} + +void UpdateLayoutInfoTable( Vector4& minMaxXY, + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo, + TextViewProcessor::WordLayoutInfo& wordLayoutInfo, + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + RelayoutParameters& relayoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // updates min and max position to calculate the text size for multiline policies. + minMaxXY.x = std::min( minMaxXY.x, characterLayoutInfo.mPosition.x ); + minMaxXY.z = std::max( minMaxXY.z, characterLayoutInfo.mPosition.x + characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor ); + + minMaxXY.y = std::min( minMaxXY.y, characterLayoutInfo.mPosition.y - characterLayoutInfo.mSize.height * relayoutData.mShrinkFactor ); + minMaxXY.w = std::max( minMaxXY.w, characterLayoutInfo.mPosition.y ); + + // Adds layout info to be retrieved by external controls or applications. + Vector3 positionOffset( characterLayoutInfo.mPosition ); + + const float descender = characterLayoutInfo.mSize.height - characterLayoutInfo.mAscender; + + const Toolkit::TextView::CharacterLayoutInfo characterLayoutTableInfo( Size( characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor, + characterLayoutInfo.mHeight * relayoutData.mShrinkFactor ), + positionOffset, + ( TextViewProcessor::LineSeparator == wordLayoutInfo.mType ), + ( TextViewProcessor::RTL == wordGroupLayoutInfo.mDirection ), + true, + descender ); + + relayoutData.mCharacterLayoutInfoTable.push_back( characterLayoutTableInfo ); + + positionOffset.x += characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor; +} + +void CalculateVisibilityForFade( const Internal::TextView::LayoutParameters& layoutParameters, + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + RelayoutParameters& relayoutParameters, + FadeParameters& fadeParameters, + TextView::RelayoutData& relayoutData ) +{ + if( ( TextView::Fade != layoutParameters.mExceedPolicy ) && + ( TextView::SplitFade != layoutParameters.mExceedPolicy ) && + ( TextView::FadeOriginal != layoutParameters.mExceedPolicy ) && + ( TextView::OriginalFade != layoutParameters.mExceedPolicy ) ) + { + // nothing to fade + return; + } + + // Calculates visibility of a text-actor according the exceed policies. + + // position + alignment offset. + const Vector3 position( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x, + characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y, + characterLayoutInfo.mPosition.z ); + + // Whether the text actor is fully, partially or non visible (according exceed policies). + switch( layoutParameters.mExceedPolicy ) + { + case TextView::Fade: + { + // All text-actors which are not completely inside the text-view's boundaries are set as non visible. + // All text-actors which are partially inside the text-view's boundaries are set as partially visible. + if( !IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE ) ) + { + relayoutParameters.mIsVisible = false; + if( IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + PARTIALLY_VISIBLE ) ) + { + fadeParameters.mIsPartiallyVisible = true; + + // Checks if a text-actor is exceeding more than one boundary as this case is not supported. + if( IsExceedingWidth( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize ) && + IsExceedingHeight( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize ) ) + { + // Combination not fully supported by text-view. + // Need to check if text-actor really supports this combination. + fadeParameters.mIsPartiallyVisible = false; + } + } + } + break; + } + case TextView::FadeOriginal: + { + // All text-actors which are not completely between the left and right text-view's boundaries are set as non visible. + // All text-actors which are partially inside the text-view's boundaries are set as partially visible. + if( !IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE_WIDTH ) ) + { + relayoutParameters.mIsVisible = false; + if( IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + PARTIALLY_VISIBLE_WIDTH ) ) + { + fadeParameters.mIsPartiallyVisible = true; + } + } + break; + } + case TextView::OriginalFade: + case TextView::SplitFade: // Fallthrough + { + // All text-actors which are not completely between the top and bottom text-view's boundaries are set as non visible. + // All text-actors which are partially inside the text-view's boundaries are set as partially visible. + if( !IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE_HEIGHT ) ) + { + relayoutParameters.mIsVisible = false; + if( IsVisible( position, + characterLayoutInfo.mSize, + relayoutData.mTextViewSize, + PARTIALLY_VISIBLE_HEIGHT ) ) + { + fadeParameters.mIsPartiallyVisible = true; + } + } + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateVisibilityForFade. Wrong exceed policies." ) + break; + } + } + + if( relayoutParameters.mIsVisible || fadeParameters.mIsPartiallyVisible ) + { + characterLayoutInfo.mIsVisible = true; + + const Size size = characterLayoutInfo.mSize * relayoutData.mShrinkFactor; + const float characterPositionPlusWidth = position.x + size.width; + const float characterPositionMinusHeight = position.y - size.height; + + // Calculates which edges need to be faded-out. + bool rightFadeOut = false; + bool leftFadeOut = false; + bool bottomFadeOut = false; + bool topFadeOut = false; + + switch( layoutParameters.mExceedPolicy ) + { + case TextView::Fade: + { + // All text-actors exceeding any of the boundaries will be faded-out. + rightFadeOut = ( characterPositionPlusWidth > fadeParameters.mRightFadeThreshold ); + leftFadeOut = ( position.x < fadeParameters.mLeftFadeThreshold ); + bottomFadeOut = ( position.y > fadeParameters.mBottomFadeThreshold ); + topFadeOut = ( characterPositionMinusHeight < fadeParameters.mTopFadeThreshold ); + break; + } + case TextView::FadeOriginal: + { + // Only text-actors exceeding the left or the right boundaries will be faded-out. + rightFadeOut = ( characterPositionPlusWidth > fadeParameters.mRightFadeThreshold ); + leftFadeOut = ( position.x < fadeParameters.mLeftFadeThreshold ); + break; + } + case TextView::SplitFade: + case TextView::OriginalFade: //Fallthrough + { + // Only text-actors exceeding the top or the bottom boundaries will be faded-out. + bottomFadeOut = ( position.y > fadeParameters.mBottomFadeThreshold ); + topFadeOut = ( characterPositionMinusHeight < fadeParameters.mTopFadeThreshold ); + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateVisibilityForFade. Wrong exceed policies." ); + break; + } + } + + // Calculates gradient parameters for a text-actor. + Vector4 gradientColor = Vector4::ZERO; + Vector2 startPoint = Vector2::ZERO; + Vector2 endPoint = Vector2::ZERO; + + if( !( rightFadeOut && leftFadeOut ) ) + { + // Current implementation can't set gradient parameters for a text-actor exceeding at the same time the left and the right boundaries. + if( rightFadeOut ) + { + gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor(); + + // Calculates gradient coeficients. + characterLayoutInfo.mColorAlpha = gradientColor.a * std::min( 1.f, fadeParameters.mRightAlphaCoeficients.x * position.x + fadeParameters.mRightAlphaCoeficients.y ); + gradientColor.a *= std::max( 0.f, fadeParameters.mRightAlphaCoeficients.x * characterPositionPlusWidth + fadeParameters.mRightAlphaCoeficients.y ); + + startPoint = Vector2( std::max( 0.f, ( fadeParameters.mRightFadeThresholdOffset - position.x ) / size.width ), 0.5f ); + endPoint = Vector2( std::min( 1.f, ( relayoutData.mTextViewSize.width - position.x ) / size.width ), 0.5f ); + } + else if( leftFadeOut ) + { + gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor(); + + // Calculates gradient coeficients. + characterLayoutInfo.mColorAlpha = std::min( 1.f, fadeParameters.mLeftAlphaCoeficients.x * characterPositionPlusWidth + fadeParameters.mLeftAlphaCoeficients.y ); + gradientColor.a *= gradientColor.a * std::max( 0.f, fadeParameters.mLeftAlphaCoeficients.x * position.x + fadeParameters.mLeftAlphaCoeficients.y ); + + startPoint = Vector2( std::max( 0.f, ( fadeParameters.mLeftFadeThresholdOffset - position.x ) / size.width ), 0.5f ); + endPoint = Vector2( std::min( 1.f, -position.x / size.width ), 0.5f ); + } + } + + if( !( bottomFadeOut && topFadeOut ) ) + { + // Current implementation can't set gradient parameters for a text-actor exceeding at the same time the top and the bottom boundaries. + if( bottomFadeOut ) + { + gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor(); + + // Calculates gradient coeficients. + characterLayoutInfo.mColorAlpha = gradientColor.a * std::min( 1.f, fadeParameters.mBottomAlphaCoeficients.x * characterPositionMinusHeight + fadeParameters.mBottomAlphaCoeficients.y ); + gradientColor.a *= std::max( 0.f, fadeParameters.mBottomAlphaCoeficients.x * position.y + fadeParameters.mBottomAlphaCoeficients.y ); + + startPoint = Vector2( 0.5f, std::max( 0.f, ( fadeParameters.mBottomFadeThresholdOffset - characterPositionMinusHeight ) / size.height ) ); + endPoint = Vector2( 0.5f, std::min( 1.f, ( relayoutData.mTextViewSize.height - characterPositionMinusHeight ) / size.height ) ); + } + else if( topFadeOut ) + { + gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor(); + + // Calculates gradient coeficients. + characterLayoutInfo.mColorAlpha *= gradientColor.a * std::min( 1.f, fadeParameters.mTopAlphaCoeficients.x * position.y + fadeParameters.mTopAlphaCoeficients.y ); + gradientColor.a *= std::max( 0.f, fadeParameters.mTopAlphaCoeficients.x * characterPositionMinusHeight + fadeParameters.mTopAlphaCoeficients.y ); + + startPoint = Vector2( 0.5f, std::max( 0.f, ( fadeParameters.mTopFadeThresholdOffset - characterPositionMinusHeight ) / size.height ) ); + endPoint = Vector2( 0.5f, std::min( 1.f, -characterPositionMinusHeight / size.height ) ); + } + } + + characterLayoutInfo.mGradientColor = gradientColor; + characterLayoutInfo.mStartPoint = startPoint; + characterLayoutInfo.mEndPoint = endPoint; + } + else + { + characterLayoutInfo.mIsVisible = false; + } +} + +bool CalculateVisibilityForEllipsizeEndOriginal( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + const EllipsizeParameters& ellipsizeParameters ) +{ + bool isPartiallyVisible = false; + + if( !IsVisible( ellipsizeParameters.mPosition, + characterLayoutInfo.mSize, + ellipsizeParameters.mEllipsizeBoundary, + FULLY_VISIBLE_WIDTH ) ) + { + // The character doesn't fit in the text-view's width. + characterLayoutInfo.mIsVisible = false; + + // Checks if the character is partially visible (it's cut by the boundary) + isPartiallyVisible = IsVisible( ellipsizeParameters.mPosition, + characterLayoutInfo.mSize, + ellipsizeParameters.mEllipsizeBoundary, + PARTIALLY_VISIBLE_WIDTH ); + } + else + { + // The character fits in the text-view's width. Set it to visible. + characterLayoutInfo.mIsVisible = true; + } + + return isPartiallyVisible; +} + +bool CalculateVisibilityForEllipsizeEnd( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + const EllipsizeParameters& ellipsizeParameters ) +{ + bool isPartiallyVisible = false; + + if( !IsVisible( ellipsizeParameters.mPosition, + characterLayoutInfo.mSize, + ellipsizeParameters.mEllipsizeBoundary, + FULLY_VISIBLE ) ) + { + // The character is not fully visible. Needs to check if it's partially visible. + characterLayoutInfo.mIsVisible = false; + + // Checks if the character doesn't cut the bottom edge of the text-view. + const bool fullyVisibleHeight = IsVisible( ellipsizeParameters.mPosition, + characterLayoutInfo.mSize, + ellipsizeParameters.mEllipsizeBoundary, + FULLY_VISIBLE_HEIGHT ); + + // Checks if the character cuts the right edge of the text-view. + const bool partiallyVisibleWidth = IsVisible( ellipsizeParameters.mPosition, + characterLayoutInfo.mSize, + ellipsizeParameters.mEllipsizeBoundary, + PARTIALLY_VISIBLE_WIDTH ); + + // Character will be ellipsized if it cuts the right edge of the text-view but fits completely in the text-view's height. + isPartiallyVisible = ( fullyVisibleHeight && partiallyVisibleWidth ); + } + else + { + // The character fits in the boundary of the text-view. Set it to visible. + characterLayoutInfo.mIsVisible = true; + } + + return isPartiallyVisible; +} + +void CalculateVisibilityForEllipsize( const Internal::TextView::LayoutParameters& layoutParameters, + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ) +{ + // Calculates visibility for EllipsizeEnd exceed policies. + + // It defines a boundary on the right side of the text-view by substracting the ellipsize-text's size (...) to the text-view's size. + // If a character is cut by this boundary and the whole line (if the multi-line policy is split-by-new-line-char) + // or the whole word (if the multi-line policy is split-by-word) doesn't fit in the text-view's width, then it's replaced by the ellipsize-text. + + // Position of the character used to do the visibility test. + ellipsizeParameters.mPosition = Vector3( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x, + characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y, + characterLayoutInfo.mPosition.z ); + + // Text will be ellipsized if a character is partially visible (it's cut by the boundary defined in the right side of the text-view). + bool isPartiallyVisible = false; + + // Checks if the whole line or the whole word fits in the text-view's width accordingly with the multiline policy. + const bool fitsInWidth = ( Toolkit::TextView::SplitByNewLineChar == layoutParameters.mMultilinePolicy ) ? ellipsizeParameters.mLineFits: ellipsizeParameters.mWordFits; + + // Will only ellipsize the text if it cuts the right vertical edge and it doesn't fit in the text-view's width. + if( fitsInWidth ) + { + // The line or word fits completely inside the text-view's width. Nothing else to do. + characterLayoutInfo.mIsVisible = true; + } + else + { + // The line or word doesn't fit in the text-view's width. + + // Calculates visibility for each type of ellipsize policies. + switch( layoutParameters.mExceedPolicy ) + { + case TextView::EllipsizeEndOriginal: + { + // Ellipsizes the text if it doesn't fit in the width but it doesn't ellipsize if the text doesn't fit in the height. + + isPartiallyVisible = CalculateVisibilityForEllipsizeEndOriginal( characterLayoutInfo, + ellipsizeParameters ); + + break; + } + case TextView::EllipsizeEnd: + { + // Ellipsizes the text if it doesn't fit in the width and fully fits in the text-view's height. + + isPartiallyVisible = CalculateVisibilityForEllipsizeEnd( characterLayoutInfo, + ellipsizeParameters ); + + break; + } + default: + { + DALI_ASSERT_DEBUG( !"TextViewRelayout::CalculateVisibilityForEllipsize. Wrong exceed value." ); + break; + } + } + } + + // If the current character is not fully visible but is partially visible, it is cut by the boundary of the text-view. + // In that case, the charater needs to be replaced by the ellipsize text. + ellipsizeParameters.mCreateEllipsizedTextActors = ( !characterLayoutInfo.mIsVisible && isPartiallyVisible ); +} + +void CreateEllipsizeTextActor( const EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ) +{ + // The default ellipsize text is '...' and all dots have the same style. However, a differernt ellipsize text could be set and it can have characters with differernt styles. + // The code bellow creates the text-actors needed for the ellipsize text. + + // Set ellipsize's position by the end of visible text. + Vector3 ellipsizePosition = ellipsizeParameters.mPosition; + // Stores current ellipsize text. + Text ellipsizeText; + // Stores current ellipsize style. + TextStyle ellipsizeStyle; + // Stores the current size. + Size ellipsizeSize; + + float bearingOffset = 0.f; + + // Create ellipsize text-actor. + for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator ellipsizeCharacterLayoutIt = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mCharactersLayoutInfo.begin(), + endEllipsizeCharacterLayoutIt = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mCharactersLayoutInfo.end(); + ellipsizeCharacterLayoutIt != endEllipsizeCharacterLayoutIt; + ++ellipsizeCharacterLayoutIt ) + { + const TextViewProcessor::CharacterLayoutInfo& ellipsizeCharacterLayoutInfo( *ellipsizeCharacterLayoutIt ); + + if( ellipsizeStyle != ellipsizeCharacterLayoutInfo.mStyledText.mStyle ) + { + // The style is different, so a new text-actor is needed. + if( !ellipsizeText.IsEmpty() ) + { + // It only creates a text-actor if there is any text. + TextActor ellipsizeTextActor = CreateTextActor( ellipsizeText, ellipsizeStyle, relayoutData.mTextActorCache ); + ellipsizeTextActor.SetSize( ellipsizeSize ); + ellipsizeTextActor.SetPosition( Vector3( ellipsizePosition.x, ellipsizePosition.y - bearingOffset, ellipsizePosition.z ) ); + + // Updates the position for the next text-actor. + ellipsizePosition.x += ellipsizeSize.width; + + // Adds the text-actor to the list. + relayoutData.mEllipsizedTextActors.push_back( ellipsizeTextActor ); + } + + // Resets the current ellipsize info. + ellipsizeText = ellipsizeCharacterLayoutInfo.mStyledText.mText; + ellipsizeStyle = ellipsizeCharacterLayoutInfo.mStyledText.mStyle; + ellipsizeSize = ellipsizeCharacterLayoutInfo.mSize; + + bearingOffset = ( ellipsizeParameters.mLineDescender - ( ellipsizeCharacterLayoutInfo.mSize.height - ellipsizeCharacterLayoutInfo.mAscender ) ) * relayoutData.mShrinkFactor; + } + else + { + // Updates text and size with the new character. + ellipsizeText.Append( ellipsizeCharacterLayoutInfo.mStyledText.mText ); + TextViewProcessor::UpdateSize( ellipsizeSize, ellipsizeCharacterLayoutInfo.mSize ); + } + + } + + if( !ellipsizeText.IsEmpty() ) + { + // Creates the last text-actor. + TextActor ellipsizeTextActor = CreateTextActor( ellipsizeText, ellipsizeStyle, relayoutData.mTextActorCache ); + ellipsizeTextActor.SetSize( ellipsizeSize ); + ellipsizeTextActor.SetPosition( Vector3( ellipsizePosition.x, ellipsizePosition.y - bearingOffset, ellipsizePosition.z ) ); + + // Adds the text-actor to the list. + relayoutData.mEllipsizedTextActors.push_back( ellipsizeTextActor ); + } +} + +void EllipsizeLine( const TextView::LayoutParameters& layoutParameters, + EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ) +{ + // Traverses the text layout info from the first character of the laid out line + // to the last one setting to each character its visibility. If needed, it adds the ellipsize text (...). + + // Indices to the first character of the laid out line. + TextViewProcessor::TextInfoIndices firstIndices; + TextViewProcessor::GetIndicesFromGlobalCharacterIndex( ellipsizeParameters.mFirstIndex, + relayoutData.mTextLayoutInfo, + firstIndices ); + + // Indices to the last character of the laid out line. + TextViewProcessor::TextInfoIndices lastIndices; + TextViewProcessor::GetIndicesFromGlobalCharacterIndex( ellipsizeParameters.mLastIndex, + relayoutData.mTextLayoutInfo, + lastIndices ); + + // Defines a boundary by substracting the ellipsize-text's width to the text-view's width. + // This is the boundary used to check if a character have to be ellipsized. + ellipsizeParameters.mEllipsizeBoundary = relayoutData.mTextViewSize; + ellipsizeParameters.mEllipsizeBoundary.width -= relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mSize.width; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + firstIndices.mLineIndex, + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + lastIndices.mLineIndex + 1; + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + ellipsizeParameters.mLineFits = ellipsizeParameters.mIsLineWidthFullyVisible && ellipsizeParameters.mIsLineHeightFullyVisible && ellipsizeParameters.mIsNextLineFullyVisibleHeight; + + if( !ellipsizeParameters.mIsNextLineFullyVisibleHeight ) + { + ellipsizeParameters.mEllipsizeBoundary.width = ellipsizeParameters.mLineWidth; + } + + bool firstGroup = true; + bool lastGroup = false; + std::size_t groupCount = 0; + + bool firstWord = true; + bool lastWord = false; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + firstIndices.mGroupIndex, + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + lastIndices.mGroupIndex + 1; + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++groupCount ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + if( groupCount == lastIndices.mGroupIndex - firstIndices.mGroupIndex ) + { + lastGroup = true; + } + + std::size_t wordCount = 0; + const std::size_t firstWordIndex = firstGroup ? firstIndices.mWordIndex : 0u; + const std::size_t lastWordIndex = lastGroup ? lastIndices.mWordIndex : wordGroupLayoutInfo.mWordsLayoutInfo.size() - 1; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + firstWordIndex, + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + lastWordIndex + 1; + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++wordCount ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + if( lastGroup && ( wordCount == lastIndices.mWordIndex - firstWordIndex ) ) + { + lastWord = true; + } + + const std::size_t firstCharacterIndex = firstWord ? firstIndices.mCharacterIndex : 0u; + const std::size_t lastCharacterIndex = lastWord ? lastIndices.mCharacterIndex : wordLayoutInfo.mCharactersLayoutInfo.size() - 1; + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + firstCharacterIndex, + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + lastCharacterIndex + 1; + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + if( ellipsizeParameters.mEllipsizeLine ) + { + // Calculates the character visibility and whether it needs to be replace by ellipsized text. + CalculateVisibilityForEllipsize( layoutParameters, + characterLayoutInfo, + ellipsizeParameters, + relayoutData ); + + if( ellipsizeParameters.mCreateEllipsizedTextActors ) + { + // Create ellipsize text-actors if the character needs to be replaced. + CreateEllipsizeTextActor( ellipsizeParameters, + relayoutData ); + } + } + else + { + if( TextView::EllipsizeEnd == layoutParameters.mExceedPolicy ) + { + if( !ellipsizeParameters.mIsLineHeightFullyVisible ) + { + // Make characters invisible. + characterLayoutInfo.mIsVisible = false; + } + } + } + } // end characters + firstWord = false; + } // end words + firstGroup = false; + } // end groups + } // end lines +} + +void SetTextVisible( TextView::RelayoutData& relayoutData ) +{ + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + characterLayoutInfo.mIsVisible = true; + characterLayoutInfo.mGradientColor = Vector4::ZERO; + characterLayoutInfo.mStartPoint = Vector2::ZERO; + characterLayoutInfo.mEndPoint = Vector2::ZERO; + } // end characters + } // end words + } // end group of words + } // end lines + + // Updates the visibility for text-input.. + for( std::vector::iterator it = relayoutData.mCharacterLayoutInfoTable.begin(), + endIt = relayoutData.mCharacterLayoutInfoTable.end(); + it != endIt; + ++it ) + { + Toolkit::TextView::CharacterLayoutInfo& characterLayoutInfo( *it ); + + characterLayoutInfo.mIsVisible = true; + } +} + +void UpdateVisibilityForFade( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + RelayoutParameters relayoutParameters; + FadeParameters fadeParameters; + + // Calculates the fade thresholds (from where the text starts to fade out). If any of the fade boundaries is zero, it sets a very small value just to avoid a zero division. + fadeParameters.mRightFadeBoundary = static_cast( visualParameters.mFadeBoundary.mRight ); + fadeParameters.mRightFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mRight > 0 ? fadeParameters.mRightFadeBoundary : MINIMUM_FADE_BOUNDARY ); + fadeParameters.mRightFadeThreshold = relayoutData.mTextViewSize.width - fadeParameters.mRightFadeBoundary; + fadeParameters.mRightFadeThresholdOffset = relayoutData.mTextViewSize.width - fadeParameters.mRightFadeBoundaryOffset; + fadeParameters.mLeftFadeBoundary = static_cast( visualParameters.mFadeBoundary.mLeft ); + fadeParameters.mLeftFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mLeft > 0 ? fadeParameters.mLeftFadeBoundary : MINIMUM_FADE_BOUNDARY ); + fadeParameters.mLeftFadeThreshold = fadeParameters.mLeftFadeBoundary; + fadeParameters.mLeftFadeThresholdOffset = fadeParameters.mLeftFadeBoundaryOffset; + fadeParameters.mTopFadeBoundary = static_cast( visualParameters.mFadeBoundary.mTop ); + fadeParameters.mTopFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mTop > 0 ? fadeParameters.mTopFadeBoundary : MINIMUM_FADE_BOUNDARY ); + fadeParameters.mTopFadeThreshold = fadeParameters.mTopFadeBoundary; + fadeParameters.mTopFadeThresholdOffset = fadeParameters.mTopFadeBoundaryOffset; + fadeParameters.mBottomFadeBoundary = static_cast( visualParameters.mFadeBoundary.mBottom ); + fadeParameters.mBottomFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mBottom > 0 ? fadeParameters.mBottomFadeBoundary : MINIMUM_FADE_BOUNDARY ); + fadeParameters.mBottomFadeThreshold = relayoutData.mTextViewSize.height - fadeParameters.mBottomFadeBoundary; + fadeParameters.mBottomFadeThresholdOffset = relayoutData.mTextViewSize.height - fadeParameters.mBottomFadeBoundaryOffset; + + // Calculates the fade out rect coeficients for the right, left, top and bottom sides of the text-view. + fadeParameters.mRightAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mRightFadeThresholdOffset, 1.f ), Vector2( relayoutData.mTextViewSize.width, 0.f ) ); + fadeParameters.mLeftAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mLeftFadeThresholdOffset, 1.f ), Vector2( 0.f, 0.f ) ); + fadeParameters.mTopAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mTopFadeThresholdOffset, 1.f ), Vector2( 0.f, 0.f ) ); + fadeParameters.mBottomAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mBottomFadeThresholdOffset, 1.f ), Vector2( relayoutData.mTextViewSize.height, 0.f ) ); + + // Traverses all groups of characters and calculates the visibility. + + std::size_t infoTableCharacterIndex = 0; + + relayoutParameters.mIndices.mLineIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + relayoutParameters.mIndices.mGroupIndex = 0; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + relayoutParameters.mIndices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + relayoutParameters.mIsFirstCharacterOfWord = true; + relayoutParameters.mWordSize = wordLayoutInfo.mSize; + relayoutParameters.mIndices.mCharacterIndex = 0; + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex, ++infoTableCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + relayoutParameters.mIsVisible = true; + fadeParameters.mIsPartiallyVisible = false; + + // Calculates the visibility for the current group of characters. + CalculateVisibilityForFade( layoutParameters, + characterLayoutInfo, + relayoutParameters, + fadeParameters, + relayoutData ); + + // Updates the visibility for text-input.. + std::vector::iterator it = relayoutData.mCharacterLayoutInfoTable.begin() + infoTableCharacterIndex; + + Toolkit::TextView::CharacterLayoutInfo& characterLayoutTableInfo( *it ); + + characterLayoutTableInfo.mIsVisible = relayoutParameters.mIsVisible; + + relayoutParameters.mIsFirstCharacterOfWord = false; + } // end group of character + } // end words + } // end group of words + } // end lines +} + +void UpdateVisibilityForEllipsize( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + // Traverses the laid-out lines and checks which ones doesn't fit in the text-view's boundary. + for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineInfoIt = relayoutData.mLines.begin(), endLineInfoIt = relayoutData.mLines.end(); + lineInfoIt != endLineInfoIt; + ++lineInfoIt ) + { + const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineInfoIt ); + + // To check if a laid-out line fits in the text-view's boundary, + // get the position of the first character is needed and do the test + // with the laid-out line size. + + // An bearing offset may have been applied to the first character so it's needed to + // get the start position of the line. + + // Some parameters used in the CalculateVisibilityForEllipsize() function. + EllipsizeParameters ellipsizeParameters; + + // Retrieves the first index and the last index of the line. + ellipsizeParameters.mFirstIndex = lineInfo.mCharacterGlobalIndex; + ellipsizeParameters.mLastIndex = 0; + if( ( lineInfoIt + 1 ) != endLineInfoIt ) + { + const Toolkit::TextView::LineLayoutInfo& nextLineInfo( *( lineInfoIt + 1 ) ); + ellipsizeParameters.mLastIndex = nextLineInfo.mCharacterGlobalIndex - 1; + } + else + { + ellipsizeParameters.mLastIndex = relayoutData.mCharacterLayoutInfoTable.size() - 1; + } + + // Retrieves the first character of the line and build the position of the line with the bearing. + const Toolkit::TextView::CharacterLayoutInfo& characterInfo = *( relayoutData.mCharacterLayoutInfoTable.begin() + ellipsizeParameters.mFirstIndex ); + + // Calculates the bearing offset applied to the first character. + const float bearingOffset = ( lineInfo.mSize.height - lineInfo.mAscender ) - characterInfo.mDescender; + + // Build the position of the line by removing the bearing offset from the first character's position. + const Vector3 position( characterInfo.mPosition.x, + characterInfo.mPosition.y + bearingOffset, + characterInfo.mPosition.z ); + + // Checks if the line needs to be ellipsized, + ellipsizeParameters.mIsLineWidthFullyVisible = IsVisible( position, + lineInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE_WIDTH ); + + // If the exceed policy is EllipsizeEndOriginal it's enough to check + // if the line fits in the width. + ellipsizeParameters.mEllipsizeLine = !ellipsizeParameters.mIsLineWidthFullyVisible; + + // If the exceed policy is EllipsizeEnd, it's needed to check if the next line exceeds the text-view's height. + // If the next line exceeds the text-view height then it's going to be invisible and current line needs to be ellipsized. + ellipsizeParameters.mIsLineHeightFullyVisible = true; + ellipsizeParameters.mIsNextLineFullyVisibleHeight = true; + if( TextView::EllipsizeEnd == layoutParameters.mExceedPolicy ) + { + // Need to check if there is lines which doesn't fit in the height. + + ellipsizeParameters.mIsLineHeightFullyVisible = IsVisible( position, + lineInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE_HEIGHT ); + + ellipsizeParameters.mEllipsizeLine = ellipsizeParameters.mEllipsizeLine && ellipsizeParameters.mIsLineHeightFullyVisible; + + if( ellipsizeParameters.mIsLineHeightFullyVisible && !ellipsizeParameters.mEllipsizeLine ) + { + // Current line is not ellipsized. + // Need to check if there is a next line and if it's not visible. If there is, current line needs to be ellipsized. + Toolkit::TextView::LineLayoutInfoContainer::const_iterator nextLineInfoIt = lineInfoIt + 1; + if( nextLineInfoIt != endLineInfoIt ) + { + // Retrives the position of the first character of the line and remove + // the bearing offset to build to build the position of the line. + const Toolkit::TextView::LineLayoutInfo& nextLineInfo( *nextLineInfoIt ); + const Toolkit::TextView::CharacterLayoutInfo& characterInfo = *( relayoutData.mCharacterLayoutInfoTable.begin() + nextLineInfo.mCharacterGlobalIndex ); + + const float bearingOffset = ( ( lineInfo.mSize.height - lineInfo.mAscender ) - characterInfo.mDescender ) * relayoutData.mShrinkFactor; + + const Vector3 position( characterInfo.mPosition.x, + characterInfo.mPosition.y + bearingOffset, + characterInfo.mPosition.z ); + + ellipsizeParameters.mIsNextLineFullyVisibleHeight = IsVisible( position, + nextLineInfo.mSize, + relayoutData.mTextViewSize, + FULLY_VISIBLE_HEIGHT ); + + // If the next line is not visible, current line have to be ellipsized. + ellipsizeParameters.mEllipsizeLine = !ellipsizeParameters.mIsNextLineFullyVisibleHeight; + } + } + } + + if( !ellipsizeParameters.mIsNextLineFullyVisibleHeight ) + { + ellipsizeParameters.mLineWidth = position.x + lineInfo.mSize.width - relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mSize.width; + } + + // Sets the line descender. + ellipsizeParameters.mLineDescender = lineInfo.mSize.height - lineInfo.mAscender; + + // At this point, ellipsizeLine distinguish if a piece of line have to be ellipsized or not. + EllipsizeLine( layoutParameters, ellipsizeParameters, relayoutData ); + } +} + +void UpdateVisibility( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + switch( layoutParameters.mExceedPolicy ) + { + case TextView::FadeOriginal: + case TextView::OriginalFade: + case TextView::Fade: + case TextView::SplitFade: // Fall through + { + UpdateVisibilityForFade( layoutParameters, + visualParameters, + relayoutData ); + break; + } + case TextView::EllipsizeEndOriginal: + case TextView::EllipsizeEnd: // Fall through + { + // Set first all characters to visible as UpdateVisibilityForEllipsize() doesn't traverse all of them. + SetTextVisible( relayoutData ); + + UpdateVisibilityForEllipsize( layoutParameters, + visualParameters, + relayoutData ); + break; + } + default: + { + SetTextVisible( relayoutData ); + break; + } + } +} + +void UpdateTextActorInfo( const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + CurrentTextActorInfo currentTextActorInfo; + + // Traverses the text-actor and layout info data structures. + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + if( characterLayoutInfo.mTextActor ) + { + // There is a new text-actor. Set text and everything to the previous one. + if( currentTextActorInfo.textActor ) + { + currentTextActorInfo.textActor.SetText( currentTextActorInfo.text ); + currentTextActorInfo.textActor.SetPosition( currentTextActorInfo.position ); + currentTextActorInfo.textActor.SetSize( currentTextActorInfo.size ); + + SetVisualParameters( currentTextActorInfo, + visualParameters, + relayoutData, + lineLayoutInfo.mSize.height ); + } + + currentTextActorInfo.text = characterLayoutInfo.mStyledText.mText; + currentTextActorInfo.position = Vector3( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x, + characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y, + characterLayoutInfo.mPosition.z ); + currentTextActorInfo.size = characterLayoutInfo.mSize * relayoutData.mShrinkFactor; + + currentTextActorInfo.color = characterLayoutInfo.mStyledText.mStyle.GetTextColor(); + currentTextActorInfo.color.a = characterLayoutInfo.mColorAlpha; + + currentTextActorInfo.gradientColor = characterLayoutInfo.mGradientColor; + currentTextActorInfo.startPoint = characterLayoutInfo.mStartPoint; + currentTextActorInfo.endPoint = characterLayoutInfo.mEndPoint; + + // Update the current text-actor. + currentTextActorInfo.textActor = characterLayoutInfo.mTextActor; + } + else + { + // If this character layout has no text-actor is because this character has the same style than previous one. + // Add the character to the current text-actor and update the size. + if( characterLayoutInfo.mIsVisible && ( TextViewProcessor::LineSeparator != wordLayoutInfo.mType ) ) + { + currentTextActorInfo.text.Append( characterLayoutInfo.mStyledText.mText ); + + currentTextActorInfo.position.y = std::min( currentTextActorInfo.position.y, ( characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y ) ); + currentTextActorInfo.size.width += characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor; + currentTextActorInfo.size.height = std::max( currentTextActorInfo.size.height, characterLayoutInfo.mSize.height * relayoutData.mShrinkFactor ); + } + } + } // end characters + } // end words + + if( !currentTextActorInfo.text.IsEmpty() ) + { + if( currentTextActorInfo.textActor ) + { + currentTextActorInfo.textActor.SetText( currentTextActorInfo.text ); + currentTextActorInfo.textActor.SetPosition( currentTextActorInfo.position ); + currentTextActorInfo.textActor.SetSize( currentTextActorInfo.size ); + + SetVisualParameters( currentTextActorInfo, + visualParameters, + relayoutData, + lineLayoutInfo.mSize.height ); + } + } + } //end groups of words + } // end lines + + for( std::vector::iterator it = relayoutData.mEllipsizedTextActors.begin(), + endIt = relayoutData.mEllipsizedTextActors.end(); + it != endIt; + ++it ) + { + TextActor textActor = ( *it ); + + textActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + textActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT ); + + // Sets the sort modifier value. + textActor.SetSortModifier( visualParameters.mSortModifier ); + + // Enables or disables the blending. + textActor.SetBlendMode( !visualParameters.mSnapshotModeEnabled ? BlendingMode::ON : BlendingMode::OFF ); + } +} + +void CalculateUnderlineInfo( TextView::RelayoutData& relayoutData, TextViewRelayout::TextUnderlineStatus& textUnderlineStatus ) +{ + // Traverse the whole text to find all groups of consecutive underlined characters in the same laid-out line. + // + // Note that relayoutData.mTextLayoutInfo contains layout info per line but these lines are the result of split the whole text every time a '\n' is found. + // According with the layout option, one of this lines could be laid-out in more than one. + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + TextViewProcessor::LineLayoutInfo& line( *lineIt ); + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt ) + { + TextViewProcessor::WordGroupLayoutInfo& group( *groupIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt ) + { + TextViewProcessor::WordLayoutInfo& word( *wordIt ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterGroup( *characterIt ); + + // Check if current character is the first of a new laid-out line + const bool isNewLine = ( textUnderlineStatus.mLineGlobalIndex < relayoutData.mLines.size() ) && + ( textUnderlineStatus.mCharacterGlobalIndex == ( *( relayoutData.mLines.begin() + textUnderlineStatus.mLineGlobalIndex ) ).mCharacterGlobalIndex ); + if( isNewLine ) + { + ++textUnderlineStatus.mLineGlobalIndex; // If it's a new line, point to the next one. + } + + if( characterGroup.mStyledText.mStyle.GetUnderline() ) + { + if( !textUnderlineStatus.mCurrentUnderlineStatus || // Current character is underlined but previous one it wasn't. + isNewLine ) // Current character is underlined and is the first of current laid-out line. + { + // Create a new underline info for the current underlined characters. + UnderlineInfo underlineInfo; + underlineInfo.mMaxHeight = characterGroup.mSize.height; + underlineInfo.mMaxThickness = characterGroup.mUnderlineThickness; + underlineInfo.mPosition = characterGroup.mUnderlinePosition; + + textUnderlineStatus.mUnderlineInfo.push_back( underlineInfo ); + + textUnderlineStatus.mCurrentUnderlineStatus = true; // Set the current text is underlined. + } + else + { + // Retrieve last underline info and update it if current underline thickness is bigger. + UnderlineInfo& underlineInfo( *( textUnderlineStatus.mUnderlineInfo.end() - 1 ) ); + + underlineInfo.mMaxHeight = std::max( underlineInfo.mMaxHeight, characterGroup.mSize.height ); + + if( characterGroup.mUnderlineThickness > underlineInfo.mMaxThickness ) + { + underlineInfo.mMaxThickness = characterGroup.mUnderlineThickness; + underlineInfo.mPosition = characterGroup.mUnderlinePosition; + } + } + } + else + { + textUnderlineStatus.mCurrentUnderlineStatus = false; + } + + ++textUnderlineStatus.mCharacterGlobalIndex; + } // end group of characters. + } // end words. + } // end group of words. + } // end lines. +} + +void SetUnderlineInfo( TextView::RelayoutData& relayoutData ) +{ + // Stores for each group of consecutive underlined text in each laid-out line its maximum thicknes, its position of that thickness and the maximum character's height. + TextViewRelayout::TextUnderlineStatus textUnderlineStatus; + + // Traverse the whole text to find all groups of consecutive underlined characters in the same laid-out line. + CalculateUnderlineInfo( relayoutData, textUnderlineStatus ); + + if( textUnderlineStatus.mUnderlineInfo.empty() ) + { + // There is no underlined text. Just exit. + return; + } + + // At this point textUnderlineStatus.mUnderlineInfo has for each group of consecutive underlined characters their maximum thickness, position and maximum height. + // Traverse the whole text and set the previously stored underline info in the text style. + + std::vector::const_iterator underlineInfoIt = textUnderlineStatus.mUnderlineInfo.begin(); + std::vector::const_iterator underlineInfoEndIt = textUnderlineStatus.mUnderlineInfo.end(); + + UnderlineInfo underlineInfo; + + if( underlineInfoIt < underlineInfoEndIt ) + { + underlineInfo = ( *underlineInfoIt ); + } + + // Whether current text is underlined. + textUnderlineStatus.mCurrentUnderlineStatus = false; + textUnderlineStatus.mCharacterGlobalIndex = 0; + textUnderlineStatus.mLineGlobalIndex = 0; + + float currentLineHeight = 0.f; + float currentLineAscender = 0.f; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + TextViewProcessor::LineLayoutInfo& line( *lineIt ); + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt ) + { + TextViewProcessor::WordGroupLayoutInfo& group( *groupIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt ) + { + TextViewProcessor::WordLayoutInfo& word( *wordIt ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterGroup( *characterIt ); + + // Check if current character is the first of a new laid-out line + + bool isNewLine = false; + + if( textUnderlineStatus.mLineGlobalIndex < relayoutData.mLines.size() ) + { + const Toolkit::TextView::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mLines.begin() + textUnderlineStatus.mLineGlobalIndex ) ); + isNewLine = ( textUnderlineStatus.mCharacterGlobalIndex == lineLayoutInfo.mCharacterGlobalIndex ); + + if( isNewLine ) + { + currentLineHeight = lineLayoutInfo.mSize.height; + currentLineAscender = lineLayoutInfo.mAscender; + ++textUnderlineStatus.mLineGlobalIndex; // If it's a new line, point to the next one. + } + } + + if( characterGroup.mStyledText.mStyle.GetUnderline() ) + { + if( textUnderlineStatus.mCurrentUnderlineStatus ) + { + if( isNewLine ) + { + // Retrieves the thickness and position for the next piece of underlined text. + if( underlineInfoIt < underlineInfoEndIt ) + { + ++underlineInfoIt; + if( underlineInfoIt < underlineInfoEndIt ) + { + underlineInfo = *underlineInfoIt; + } + } + } + } + + textUnderlineStatus.mCurrentUnderlineStatus = true; + + // Sets the underline's thickness. + characterGroup.mStyledText.mStyle.SetUnderlineThickness( underlineInfo.mMaxThickness ); + + // Before setting the position it needs to be adjusted to match the base line. + const float bearingOffset = ( currentLineHeight - currentLineAscender ) - ( characterGroup.mSize.height - characterGroup.mAscender ); + const float positionOffset = ( underlineInfo.mMaxHeight - characterGroup.mSize.height ) - bearingOffset; + + // Sets the underline's position. + characterGroup.mStyledText.mStyle.SetUnderlinePosition( underlineInfo.mPosition - positionOffset ); + + // Mark the group of characters to be set the new style into the text-actor. + characterGroup.mSetStyle = true; + } + else + { + if( textUnderlineStatus.mCurrentUnderlineStatus ) + { + textUnderlineStatus.mCurrentUnderlineStatus = false; + + // Retrieves the thickness and position for the next piece of underlined text. + if( underlineInfoIt < underlineInfoEndIt ) + { + ++underlineInfoIt; + if( underlineInfoIt < underlineInfoEndIt ) + { + underlineInfo = *underlineInfoIt; + } + } + } + } + + ++textUnderlineStatus.mCharacterGlobalIndex; + } // end of group of characters. + } // end of word. + } // end of group of words. + } // end of lines. +} + +void RemoveTextActors( Actor textView, + const std::vector& textActors ) +{ + // Removes previously inserted text-actors. + // The SplitByNewLineChar::Relayout(), SplitByWord::Relayout() and SplitByChar::Relayout() functions add + // text-actors to the text-view. A handle to these text-actors are stored and passed to this function + // in order to remove 'only' text-actors added by these functions. + // Any other actor added by a programmer or application won't be removed. + + for( std::vector::const_reverse_iterator it = textActors.rbegin(), endIt = textActors.rend(); it != endIt; ++it ) + { + textView.Remove( *it ); + } +} + +void InsertToTextView( const TextView::RelayoutOperationMask relayoutOperationMask, + Actor textView, + TextView::RelayoutData& relayoutData ) +{ + const bool insertToTextView = relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW; + const bool insertToTextActorList = relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST; + + // Add text-actors to the text-view. + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + if( characterLayoutInfo.mIsVisible && characterLayoutInfo.mTextActor ) // White spaces and '\n' characters doesn't have a text-actor. + { + //Add to the text-view. + if( insertToTextView ) + { + textView.Add( characterLayoutInfo.mTextActor ); + } + if( insertToTextActorList ) + { + relayoutData.mTextActors.push_back( characterLayoutInfo.mTextActor ); + } + } + } // end group of character + } // end words + } // end group of words + } // end lines + + for( std::vector::iterator it = relayoutData.mEllipsizedTextActors.begin(), + endIt = relayoutData.mEllipsizedTextActors.end(); + it != endIt; + ++it ) + { + TextActor textActor = ( *it ); + + //Add to the text-view. + if( insertToTextView ) + { + textView.Add( textActor ); + } + if( insertToTextActorList ) + { + relayoutData.mTextActors.push_back( textActor ); + } + } + relayoutData.mEllipsizedTextActors.clear(); +} + +TextActor CreateTextActor( const Text& text, const TextStyle& style, TextActorCache& cache ) +{ + TextActor textActor = cache.RetrieveTextActor(); + + if( textActor ) + { + // Update the text-actor. + textActor.SetText( text ); + textActor.SetTextStyle( style ); + } + else + { + // The text-actor cache is empty. Create a new one. + textActor = TextActor::New( text, style, false, true ); + } + + return textActor; +} + +} // namespace TextViewRelayout + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/relayout-utilities.h b/dali-toolkit/internal/controls/text-view/relayout-utilities.h new file mode 100644 index 0000000..ee0cc96 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/relayout-utilities.h @@ -0,0 +1,501 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_RELAYOUT_UTILITIES_H__ +#define __DALI_TOOLKIT_INTERNAL_RELAYOUT_UTILITIES_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +//Forward declarations. +class TextActorCache; + +namespace TextViewRelayout +{ + +extern const float MINIMUM_FADE_BOUNDARY; // When the fade boundary is the same as the text-view boundary, this constant reduces it in order to avoid a zero division. + +/** + * Define the type of line wrap. + */ +enum HorizontalWrapType +{ + WrapByCharacter, ///< Wrap a line per character (It may split a word in two). + WrapByWord, ///< Wrap a line by word. + WrapByWordAndSplit, ///< Wrap the line by word and split a word if it doesn't fit. + WrapByLine, ///< Wrap the line when a \n is found. + WrapByLineAndSplit ///< Wrap the line when a \n is found and split if it doesn't fit. +}; + +/** + * Different types of visibility tests (text-actor - text-view). + */ +enum VisibilityTestType +{ + FULLY_VISIBLE, ///< The text-actor is completely inside the text-view. + FULLY_VISIBLE_WIDTH, ///< The text-actor is completely between the right and the left boundaries of the text-view. + FULLY_VISIBLE_HEIGHT, ///< The text-actor is completely between the top and the bottom boundaries of the text-view. + PARTIALLY_VISIBLE, ///< The text-actor is partially inside the text-view. + PARTIALLY_VISIBLE_WIDTH, ///< The text-actor is partially inside the width of the text-view. It may be completely above and bellow the top and bottom boundaries of the text-view. + PARTIALLY_VISIBLE_HEIGHT ///< The text-actor is partially inside the height of the text-view. It may be completely on the left and on the right the left and right boundaries of the text-view. +}; + +/** + * Temporary parameters used in the relayout process. + */ +struct RelayoutParameters +{ + /** + * Default constructor. + * + * Initializes all members to their defaults. + */ + RelayoutParameters(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~RelayoutParameters(); + + Vector3 mPositionOffset; ///< Offset (position.x + size.width, position.y, position.z) of the previous text-actor. + Size mLineSize; ///< Current line's size. + Size mWordSize; ///< Current word's size. + Size mCharacterSize; ///< Current character's size. + TextViewProcessor::TextInfoIndices mIndices; ///< Current indices to line, group of words, word and character. + std::size_t mCharacterGlobalIndex; ///< Index to a single character within the whole text. + bool mIsFirstCharacter:1; ///< Whether is the first character of the whole text. + bool mIsFirstCharacterOfWord:1; ///< Whether is the first character of the word. + bool mIsNewLine:1; ///< Whether the current character is the first character of a new line. + bool mIsNewLineCharacter:1; ///< Whether the current character is a new line character. + bool mIsWhiteSpace:1; ///< Whether the current character is a white space. + bool mIsVisible:1; ///< Whether the current character is visible. +}; + +/** + * Parameters used to calculate the gradient of text-actors when fading is enabled. + */ +struct FadeParameters +{ + /** + * Default constructor. + * + * Initializes all members to their defaults. + */ + FadeParameters(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~FadeParameters(); + + float mRightFadeBoundary; ///< Distance from the right edge of the text-view to the right edge of the fade boundary. + float mRightFadeThreshold; ///< Point from where fade out starts (by right edge). + float mRightFadeBoundaryOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + float mRightFadeThresholdOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + Vector2 mRightAlphaCoeficients; ///< The fade out rect coeficients for the right side of the text-view. + float mLeftFadeBoundary; ///< Distance from the left edge of the text-view to the left edge of the fade boundary. + float mLeftFadeThreshold; ///< Point from where fade out starts (by left edge). + float mLeftFadeBoundaryOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + float mLeftFadeThresholdOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + Vector2 mLeftAlphaCoeficients; ///< The fade out rect coeficients for the left side of the text-view. + float mTopFadeBoundary; ///< Distance from the top edge of the text-view to the top edge of the fade boundary. + float mTopFadeThreshold; ///< Point from where fade out starts (by top edge). + float mTopFadeBoundaryOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + float mTopFadeThresholdOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + Vector2 mTopAlphaCoeficients; ///< The fade out rect coeficients for the top side of the text-view. + float mBottomFadeBoundary; ///< Distance from the bottom edge of the text-view to the bottom edge of the fade boundary. + float mBottomFadeThreshold; ///< Point from where fade out starts (by bottom edge). + float mBottomFadeBoundaryOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + float mBottomFadeThresholdOffset; ///< Same as above plus an offset if the value is zero. Used to avoid a zero division. + Vector2 mBottomAlphaCoeficients; ///< The fade out rect coeficients for the bottom side of the text-view. + bool mIsPartiallyVisible:1; ///< Whether the current character is partially visible. +}; + +/** + * Parameters used to calculate the ellipsize. + */ +struct EllipsizeParameters +{ + /** + * Default constructor. + * + * Initializes all members to their defaults. + */ + EllipsizeParameters(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~EllipsizeParameters(); + + Vector3 mPosition; ///< Position of the first character of the ellipsize text. + float mLineDescender; ///< Distance from the base line to the bottom. + float mLineWidth; ///< Current laid out line's width. + Size mEllipsizeBoundary; ///< Where to start to ellipsize a line. + std::size_t mFirstIndex; ///< Global index within the whole text of the first character of the laid out line. + std::size_t mLastIndex; ///< Global index within the whole text of the last character of the laid out line. + bool mEllipsizeLine:1; ///< Whether current line must be ellipsized. + bool mIsLineWidthFullyVisible:1; ///< Whether current line fits in text-view's width. + bool mIsLineHeightFullyVisible:1; ///< Whether current line fits in text-view's height. + bool mIsNextLineFullyVisibleHeight:1; ///< Whether next line fits in text-view's height. + bool mCreateEllipsizedTextActors:1; ///< Whether to create text-actors for the ellipsized text. + bool mLineFits:1; ///< Whether the current line fits in the boundary of the text-view. + bool mWordFits:1; ///< Whether the current word fits in the boundary of the text-view. +}; + +/** + * Stores underline info for a group of consecutive characters in the same laid out line. + */ +struct UnderlineInfo +{ + /** + * Default constructor. + * + * Initializes all members to their defaults. + */ + UnderlineInfo(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~UnderlineInfo(); + + float mMaxHeight; ///< The maximum height. + float mMaxThickness; ///< The maximum underline's thickness. + float mPosition; ///< The underline's position of the character with the maximum underline's thickness. +}; + +/** + * Stores underline info for each group of consecutive underlined characters. + * It also stores some status used when traversing the whole text. + */ +struct TextUnderlineStatus +{ + /** + * Default constructor. + * + * Initializes each member to its default. + */ + TextUnderlineStatus(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~TextUnderlineStatus(); + + std::vector mUnderlineInfo; ///< Underline info for each group of consecutive underlined characters. + std::size_t mCharacterGlobalIndex; ///< Global index (within the whole text) to current character. + std::size_t mLineGlobalIndex; ///< Index to current laid out line. It takes into account the current layout configuration (is not the number of \n) + bool mCurrentUnderlineStatus:1; ///< Whether current character is underlined. +}; + +/** + * Stores layout information of the piece of a line. + */ +struct SubLineLayoutInfo +{ + /** + * Default constructor. + * + * Initializes each member to its default. + */ + SubLineLayoutInfo(); + + /** + * Empty destructor. + * + * @note Added to increase coverage. + */ + ~SubLineLayoutInfo(); + + float mLineLength; ///< The length of the portion of the line which fits on the text-view width. + float mMaxCharHeight; ///< The maximum height of all characters of the portion of line which fits on the text-view width. + float mMaxAscender; ///< The maximum ascender of all characters of the portion of line which fits on the text-view width. +}; + +/** + * Calculates the layout info of the portion of the line which fits on the text-view width. + * + * @param[in] parentWidth Text-view width + * @param[in] indices Indices to the word group, word and character. + * @param[in] lineLayoutInfo Layout info for the line. + * @param[in] splitPolicy Whether a line is wraped by word, by character or by word and character. + * @param[in] shrinkFactor Shrink factor used. + * @param[out] layoutInfo Layout information of the part of the line which fits in the text-view width. + */ +void CalculateSubLineLayout( float parentWidth, + const TextViewProcessor::TextInfoIndices& indices, + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo, + HorizontalWrapType splitPolicy, + float shrinkFactor, + SubLineLayoutInfo& layoutInfo ); + +/** + * Calculates the \e x offset position for the whole text. + * + * @param[in] horizontalTextAlignment The horizontal alignment type. + * @param[in] parentWidth The parent's width. + * @param[in] wholeTextWidth The whole text's width. + * @return The \e x position offset. + */ +float CalculateXoffset( Toolkit::Alignment::Type horizontalTextAlignment, float parentWidth, float wholeTextWidth ); + +/** + * Calculates the \e y offset position for the whole text. + * + * @param[in] verticalTextAlignment The vertical alignment type. + * @param[in] parentHeight The parent's height. + * @param[in] wholeTextHeight The whole text's height. + * @return The \e y position offset. + */ +float CalculateYoffset( Toolkit::Alignment::Type verticalTextAlignment, float parentHeight, float wholeTextHeight ); + +/** + * Calculates the \e x offset position for one line. + * + * @param[in] justification The line justification type. + * @param[in] wholeTextWidth The whole text's width. + * @param[in] lineLength The line's length. + * @return The \e x position offset. + * + */ +float CalculateJustificationOffset( Toolkit::TextView::LineJustification justification, float wholeTextWidth, float lineLength ); + +/** + * Whether text-actor is visible for Fade and Ellipsize exceed policies. + * + * It does different visibility tests according the given parameter \e type. @see VisibilityTestType. + * + * @param[in] position Position of the character. + * @param[in] size Size of the character. + * @param[in] parentSize Parent's size. + * @param[in] type One of FULLY_VISIBLE, PARTIALLY_VISIBLE, PARTIALLY_VISIBLE_WIDTH or PARTIALLI_VISIBLE_HEIGHT. + * @return \e true if is visible. + */ +bool IsVisible( const Vector3& position, const Size& size, const Size& parentSize, VisibilityTestType type ); + +/** + * Calculates the coeficients of the rect equation for the two given points. + * + * @param[in] p0 A point of the rect. + * @param[in] p1 Another point of the rect. + * + * @return The \e gradient is returned in the \e x member and the \e constant \e term is returned in the \e y value. + */ +Vector2 CalculateRectParameters( const Vector2& p0, const Vector2& p1 ); + +/** + * Aligns the whole text within the text-view. + * + * @param[in] layoutParameters The layout parameters. + * @param[in,out] relayoutData The text-view's data structures. + */ +void UpdateAlignment( const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Calculates the bearing for the given character. + * + * @param[in,out] characterLayoutInfo Character layout info. + * @param[in,out] relayoutData The text-view's data structures. + */ +void CalculateBearing( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + TextView::RelayoutData& relayoutData ); + +/** + * Updates the character's layout info table. + * + * This table is used to pass the size, the position and other layout info to other controls/actors. + * + * @param[in,out] minMaxXY The boundary box of the whole text. + * @param[in,out] characterLayoutInfo Character layout info. + * @param[in,out] relayoutData The text-view's data structures. + */ +void UpdateLayoutInfoTable( Vector4& minMaxXY, + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo, + TextViewProcessor::WordLayoutInfo& wordLayoutInfo, + TextViewProcessor::CharacterLayoutInfo& characterGroupLayoutInfo, + RelayoutParameters& relayoutParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Calculates the text-actor visibility and fade parameters. + * + * @param[in] layoutParameters The layout parameters. + * @param[in] characterLayoutInfo Character layout info. + * @param[in,out] relayoutParameters Temporary layout parameters. + * @param[in,out] fadeParameters Temporary fade parameters. + * @param[in,out] relayoutData The text-view's data structures. + */ +void CalculateVisibilityForFade( const Internal::TextView::LayoutParameters& layoutParameters, + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + RelayoutParameters& relayoutParameters, + FadeParameters& fadeParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Calculates the text-actor visibility and creates eliipsize text-actors. + * + * @param[in] layoutParameters The layout parameters. + * @param[in] characterLayoutInfo Character layout info. + * @param[in,out] ellipsizeParameters Temporary ellipsize parameters. + * @param[in,out] relayoutData The text-view's data structures. + */ +void CalculateVisibilityForEllipsize( const Internal::TextView::LayoutParameters& layoutParameters, + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo, + EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Creates the actors needed for the ellipsized text. + * + * @param[in,out] ellipsizeParameters Temporary ellipsize parameters. + * @param[in,out] relayoutData The text-view's data structures. + */ +void CreateEllipsizeTextActor( const EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ); + +/** + * + */ +void EllipsizeLine( const TextView::LayoutParameters& layoutParameters, + EllipsizeParameters& ellipsizeParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Traverse all text data structure setting its visibility to true. + * + * @param[in,out] relayoutData The text-view's data structures. + */ +void SetTextVisible( TextView::RelayoutData& relayoutData ); + +/** + * Calculates the visibility and fade parameters. + * + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in,out] relayoutData The text-view's data structures. + */ +void UpdateVisibilityForFade( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); +/** + * Calculates the visibility for text ellipsize. + * + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in,out] relayoutData The text-view's data structures. + */ +void UpdateVisibilityForEllipsize( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); +/** + * Calculates the visibility and fade parameters. + * + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in,out] relayoutData The text-view's data structures. + */ +void UpdateVisibility( const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Traverse all text updating text-actor handles with new size, position, ... + * + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info. + */ +void UpdateTextActorInfo( const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Traverses the whole text and for each piece of underlined text, + * it calculates the maximum thickness and the position of that particular piece of underlined text. + * + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + */ +void CalculateUnderlineInfo( TextView::RelayoutData& relayoutData ); + +/** + * Traverses the whole text and for each piece of underlined text, + * it sets the previously calculated maximum thickness and the position of that particular piece of underlined text. + * + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + */ +void SetUnderlineInfo( TextView::RelayoutData& relayoutData ); + +/** + * Remove text-actor from the text-view. + * + * @param[in,out] textView The text-view. + * @param[in] textActors text-actors to be removed from the text-view. + */ +void RemoveTextActors( Actor textView, + const std::vector& textActors ); + +/** + * Inserts the text-actors into the text-view and/or the text-actor's list. + * + * @param[in] relayoutOperationMask Whether the text-actors should be added into the text-view, the list of text-actors or in both. + * @param[in,out] textView The text-view. + * @param[in,out] relayoutData The text-view's data structures. + */ +void InsertToTextView( TextView::RelayoutOperationMask relayoutOperationMask, + Actor textView, + TextView::RelayoutData& relayoutData ); + +/** + * Retrieves a new text-actor from the cache of text-actors or creating a new one if it's empty. + * + * @param[in] text The text-actor's text. + * @param[in] style The text-actor's style. + * @param[in] cache The cache of text-actors. + */ +TextActor CreateTextActor( const Text& text, const TextStyle& style, TextActorCache& cache ); + +} // namespace TextViewRelayout + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_RELAYOUT_UTILITIES_H__ diff --git a/dali-toolkit/internal/controls/text-view/split-by-char-policies.cpp b/dali-toolkit/internal/controls/text-view/split-by-char-policies.cpp new file mode 100644 index 0000000..819d87e --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-char-policies.cpp @@ -0,0 +1,296 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "split-by-char-policies.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include "relayout-utilities.h" +#include "text-view-processor.h" +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByChar +{ + +namespace +{ + +Vector3 NoShrinkWhenExceedPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x ); + const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y ); + + if( ( relayoutParameters.mIsNewLine || + relayoutParameters.mIsFirstCharacter || + ( wordOffset + relayoutParameters.mCharacterSize.width > relayoutData.mTextViewSize.width ) ) ) + { + if( !relayoutParameters.mIsNewLine && + ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) ) + { + // Current character is a white space. Don't want to move a white space to the next line. + // These white spaces are placed just in the edge. + return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f ); + } + else + { + // Calculate the line length and the max character height for the current line. + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) ); + + TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width, + relayoutParameters.mIndices, + lineLayoutInfo, + TextViewRelayout::WrapByCharacter, + 1.f, // Shrink factor + subLineInfo ); + + // Stores some info to calculate the line justification in a post-process. + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = subLineInfo.mLineLength; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + + return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f ); + } + } + else + { + return Vector3( wordOffset, previousPositionY, 0.f ); + } +} + +void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + TextViewRelayout::RelayoutParameters relayoutParameters; + + // clear + relayoutData.mCharacterLayoutInfoTable.clear(); + relayoutData.mLines.clear(); + relayoutData.mTextSizeForRelayoutOption = Size(); + + // Calculate the text size for split by char. + Vector4 minMaxXY( std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() ); + + relayoutData.mShrinkFactor = 1.f; // Shrink factor used when the exceed policy contains ShrinkToFit + + relayoutParameters.mPositionOffset = Vector3::ZERO; + relayoutParameters.mIsFirstCharacter = true; + relayoutParameters.mIndices.mLineIndex = 0; + relayoutParameters.mCharacterGlobalIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + relayoutParameters.mIsNewLine = true; + relayoutParameters.mLineSize = lineLayoutInfo.mSize; + relayoutParameters.mIndices.mGroupIndex = 0; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + relayoutParameters.mIndices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType; + relayoutParameters.mIsNewLineCharacter = TextViewProcessor::LineSeparator == wordLayoutInfo.mType; + + relayoutParameters.mIsFirstCharacterOfWord = true; + relayoutParameters.mWordSize = wordLayoutInfo.mSize; + relayoutParameters.mIndices.mCharacterIndex = 0; + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + relayoutParameters.mCharacterSize = characterLayoutInfo.mSize; + + switch( layoutParameters.mExceedPolicy ) + { + case TextView::OriginalShrink: + case TextView::SplitOriginal: + case TextView::SplitFade: + case TextView::SplitShrink: + case TextView::ShrinkOriginal: + case TextView::ShrinkFade: + case TextView::Shrink: + case TextView::EllipsizeEndOriginal: + case TextView::EllipsizeEnd: // Fall Through + { + DALI_LOG_WARNING( "SplitByChar::CalculateSizeAndPosition() policy not implemented.\n" ); + break; + } + case TextView::OriginalFade: + case TextView::FadeOriginal: + case TextView::Original: + case TextView::Fade: // Fall Through + { + characterLayoutInfo.mPosition = NoShrinkWhenExceedPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f ); + break; + } + default: + { + DALI_LOG_WARNING( "SplitByChar::CalculateSizeAndPosition() policy combination not possible.\n" ); + } + } + + // Get last line info and calculate the bearing (used to align glyphs with the baseline). + TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData ); + + // updates min and max position to calculate the text size for split by char. + TextViewRelayout::UpdateLayoutInfoTable( minMaxXY, + wordGroupLayoutInfo, + wordLayoutInfo, + characterLayoutInfo, + relayoutParameters, + relayoutData ); + + ++relayoutParameters.mCharacterGlobalIndex; + relayoutParameters.mIsFirstCharacter = false; + relayoutParameters.mIsNewLine = false; + } // end characters + } // end words + } // end group of words + } // end lines + + if( relayoutData.mCharacterLayoutInfoTable.empty() ) + { + relayoutData.mTextSizeForRelayoutOption = Size(); + } + else + { + relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x; + relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y; + } + + // Check if the last character is a new line character. In that case the height should be added. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + if( lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) // if it's empty, it means the last character is a new line character. + { + relayoutData.mTextSizeForRelayoutOption.height += lineLayoutInfo.mSize.height; + } + } +} + +} // namespace + +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION ) + { + relayoutData.mLineJustificationInfo.clear(); + CalculateSizeAndPosition( layoutParameters, + relayoutData ); + + TextViewRelayout::SetUnderlineInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT ) + { + TextViewRelayout::UpdateAlignment( layoutParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY ) + { + TextViewRelayout::UpdateVisibility( layoutParameters, + visualParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS ) + { + TextViewProcessor::InitializeTextActorInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE ) + { + TextViewRelayout::UpdateTextActorInfo( visualParameters, + relayoutData ); + } + + if( ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW ) || + ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ) ) + { + TextViewRelayout::InsertToTextView( relayoutOperationMask, + textView, + relayoutData ); + } +} + +} // namespace SplitByChar + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/split-by-char-policies.h b/dali-toolkit/internal/controls/text-view/split-by-char-policies.h new file mode 100644 index 0000000..5a09de8 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-char-policies.h @@ -0,0 +1,59 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SPLIT_BY_CHAR_POLICIES_H__ +#define __DALI_TOOLKIT_INTERNAL_SPLIT_BY_CHAR_POLICIES_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByChar +{ + +/** + * Sets text-actor's size and position accordingly with the given text-view's size and layout parameters. + * Visible text-actors are added to the text-view. Non visible actors are not added. + * + * @param[in] textView The handle to the text-view actor. + * @param[in] relayoutOperationMask Mask which defines which operations need to be done in the relayout process. + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in] relayoutData The text-view's data structures which are modified by this function. + */ +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); + +} // namespace SplitByChar + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SPLIT_BY_CHAR_POLICIES_H__ diff --git a/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.cpp b/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.cpp new file mode 100644 index 0000000..b1b55de --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.cpp @@ -0,0 +1,352 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "split-by-new-line-char-policies.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include "relayout-utilities.h" +#include "text-view-processor.h" +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByNewLineChar +{ + +namespace +{ + +Vector3 SplitPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x ); + const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y ); + + if( relayoutParameters.mIsNewLine || + relayoutParameters.mIsFirstCharacter || + ( wordOffset + relayoutParameters.mCharacterSize.width > relayoutData.mTextViewSize.width ) ) + { + if( !relayoutParameters.mIsNewLine && + ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) ) + { + // Current character is a white space. Don't want to move a white space to the next line. + // These white spaces are placed just in the edge. + return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f ); + } + else + { + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) ); + + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width, + relayoutParameters.mIndices, + lineLayoutInfo, + TextViewRelayout::WrapByLineAndSplit, + 1.f, // Shrink factor + subLineInfo ); + + // Stores some info to calculate the line justification in a post-process. + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = subLineInfo.mLineLength; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + + return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f ); + } + } + else + { + return Vector3( wordOffset, previousPositionY, 0.f ); + } +} + +void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // clear + relayoutData.mCharacterLayoutInfoTable.clear(); + relayoutData.mLines.clear(); + relayoutData.mTextSizeForRelayoutOption = Size(); + relayoutData.mShrinkFactor = 1.f; + + // Calculates the text size for split by char. + Vector4 minMaxXY( std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() ); + + TextViewRelayout::RelayoutParameters relayoutParameters; + + if( TextView::ShrinkOriginal == layoutParameters.mExceedPolicy ) + { + if( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width ) + { + relayoutData.mShrinkFactor = relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width; + } + } + else if( TextView::Shrink == layoutParameters.mExceedPolicy ) + { + if( ( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width ) || + ( relayoutData.mTextLayoutInfo.mWholeTextSize.height > relayoutData.mTextViewSize.height ) ) + { + relayoutData.mShrinkFactor = std::min( relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width, + relayoutData.mTextViewSize.height / relayoutData.mTextLayoutInfo.mWholeTextSize.height ); + } + } + + relayoutParameters.mIsFirstCharacter = true; + relayoutParameters.mIndices.mLineIndex = 0; + relayoutParameters.mPositionOffset = Vector3::ZERO; + relayoutParameters.mCharacterGlobalIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + relayoutParameters.mLineSize = lineLayoutInfo.mSize * relayoutData.mShrinkFactor; + + relayoutParameters.mIsNewLine = true; + relayoutParameters.mIndices.mGroupIndex = 0; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + relayoutParameters.mIndices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType; + relayoutParameters.mIsNewLineCharacter = TextViewProcessor::LineSeparator == wordLayoutInfo.mType; + + relayoutParameters.mIsFirstCharacterOfWord = true; + relayoutParameters.mWordSize = wordLayoutInfo.mSize; + relayoutParameters.mIndices.mCharacterIndex = 0; + + if( relayoutParameters.mIsNewLine ) + { + // Stores some info to calculate the line justification in a post-process. + const bool isSplitOriginal = layoutParameters.mExceedPolicy == TextView::SplitOriginal; + + if( !isSplitOriginal ) + { + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = relayoutParameters.mLineSize.width; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + } + } + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterLayoutIt != endCharacterLayoutIt; + ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + relayoutParameters.mCharacterSize = characterLayoutInfo.mSize; + + relayoutParameters.mCharacterSize = characterLayoutInfo.mSize; + + switch( layoutParameters.mExceedPolicy ) + { + case TextView::OriginalShrink: + case TextView::SplitShrink: + case TextView::ShrinkFade: // Fall Through + { + DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() policy not implemented.\n" ); + break; + } + case TextView::Original: // Fall Through + case TextView::ShrinkOriginal: // Fall Through + case TextView::Shrink: // Fall Through + case TextView::OriginalFade: // Fall Through + case TextView::FadeOriginal: // Fall Through + case TextView::Fade: // Fall Through + case TextView::EllipsizeEndOriginal: // Fall Through + case TextView::EllipsizeEnd: // Fall Through + { + if( relayoutParameters.mIsNewLine ) + { + relayoutParameters.mPositionOffset.x = 0.f; + relayoutParameters.mPositionOffset.y += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor; + } + + characterLayoutInfo.mPosition = relayoutParameters.mPositionOffset; + + relayoutParameters.mPositionOffset.x += characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor; + + if( relayoutParameters.mIsNewLine || + relayoutParameters.mIsFirstCharacter ) + { + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = relayoutParameters.mLineSize; // Size of this piece of line. + lineInfo.mAscender = lineLayoutInfo.mAscender * relayoutData.mShrinkFactor; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + } + break; + } + case TextView::SplitOriginal: + case TextView::SplitFade: // Fall Through + { + characterLayoutInfo.mPosition = SplitPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f ); + break; + } + default: + { + DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() Layout configuration not possible.\n" ); + break; + } + } + + // Get last line info and calculate the bearing (used to align glyphs with the baseline). + TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData ); + + // updates min and max position to calculate the text size for split by new line char. + TextViewRelayout::UpdateLayoutInfoTable( minMaxXY, + wordGroupLayoutInfo, + wordLayoutInfo, + characterLayoutInfo, + relayoutParameters, + relayoutData ); + + ++relayoutParameters.mCharacterGlobalIndex; + relayoutParameters.mIsFirstCharacter = false; + relayoutParameters.mIsNewLine = false; + } // end characters + } // end words + } // end group of words + } // end lines + + if( relayoutData.mCharacterLayoutInfoTable.empty() ) + { + relayoutData.mTextSizeForRelayoutOption = Size(); + } + else + { + relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x; + relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y; + } + + // Check if the last character is a new line character. In that case the height should be added. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + if( lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) // if it's empty, it means the last character is a new line character. + { + relayoutData.mTextSizeForRelayoutOption.height += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor; + } + } +} + +} // namespace + +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION ) + { + relayoutData.mLineJustificationInfo.clear(); + CalculateSizeAndPosition( layoutParameters, + relayoutData ); + + TextViewRelayout::SetUnderlineInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT ) + { + TextViewRelayout::UpdateAlignment( layoutParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY ) + { + TextViewRelayout::UpdateVisibility( layoutParameters, + visualParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS ) + { + TextViewProcessor::InitializeTextActorInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE ) + { + TextViewRelayout::UpdateTextActorInfo( visualParameters, + relayoutData ); + } + + if( ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW ) || + ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ) ) + { + TextViewRelayout::InsertToTextView( relayoutOperationMask, + textView, + relayoutData ); + } +} + +} // namespace SplitByNewLineChar + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.h b/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.h new file mode 100644 index 0000000..ddaac96 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-new-line-char-policies.h @@ -0,0 +1,60 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SPLIT_BY_NEW_LINE_CHAR_POLICIES_H__ +#define __DALI_TOOLKIT_INTERNAL_SPLIT_BY_NEW_LINE_CHAR_POLICIES_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByNewLineChar +{ + +/** + * Sets text-actor's size and position accordingly with the given text-view's size and layout parameters. + * Visible text-actors are added to the text-view. Non visible actors are not added. + * + * @param[in] textView The handle to the text-view actor. + * @param[in] relayoutOperationMask Mask which defines which operations need to be done in the relayout process. + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in] relayoutData The text-view's data structures which are modified by this function. + */ +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); + +} // namespace SplitByNewLineChar + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SPLIT_BY_NEW_LINE_CHAR_POLICIES_H__ diff --git a/dali-toolkit/internal/controls/text-view/split-by-word-policies.cpp b/dali-toolkit/internal/controls/text-view/split-by-word-policies.cpp new file mode 100644 index 0000000..d6455a8 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-word-policies.cpp @@ -0,0 +1,695 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// FILE HEADER + +#include "split-by-word-policies.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include "relayout-utilities.h" +#include "text-view-processor.h" +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByWord +{ + +namespace +{ + +Vector3 OriginalPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x ); + const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y ); + + if( relayoutParameters.mIsNewLine || + relayoutParameters.mIsFirstCharacter || + ( relayoutParameters.mIsFirstCharacterOfWord && ( wordOffset + relayoutParameters.mWordSize.width > relayoutData.mTextViewSize.width ) ) ) + { + if( !relayoutParameters.mIsNewLine && + ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) ) + { + // Current character is a white space. Don't want to move a white space to the next line. + // These white spaces are placed just in the edge. + return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f ); + } + else + { + // Calculates the length of the portion of the line which doesn't exceed the text-view's width and the max character height for the current line. + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) ); + + TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width, + relayoutParameters.mIndices, + lineLayoutInfo, + TextViewRelayout::WrapByWord, + 1.f, // Shrink factor + subLineInfo ); + + if( subLineInfo.mLineLength < Math::MACHINE_EPSILON_1000 ) + { + // It may mean there is a word which is actually longer than the width of the text-view. + // In that case the length of this word is needed. + if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + const TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.begin() + relayoutParameters.mIndices.mGroupIndex ) ); + if( !wordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + const TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *( wordGroupLayoutInfo.mWordsLayoutInfo.begin() + relayoutParameters.mIndices.mWordIndex ) ); + subLineInfo.mLineLength = wordLayoutInfo.mSize.width; + } + } + } + + // Stores some info to calculate the line justification in a post-process. + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = subLineInfo.mLineLength; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + + return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f ); + } + } + else + { + return Vector3( wordOffset, previousPositionY, 0.f ); + } +} + +/** + * Calculates character position. + * @param[in] relayoutParameters Temporary layout parameters (previous size, previous position, ... ) + * @param[in] layoutParameters The layout parameters. + * @param[in] relayoutData The text-view's data structures. + * @return The character's position. + */ +Vector3 SplitWhenExceedPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x ); + const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y ); + + if( ( relayoutParameters.mIsNewLine || relayoutParameters.mIsFirstCharacter ) || + ( relayoutParameters.mIsFirstCharacterOfWord && ( wordOffset + relayoutParameters.mWordSize.width > relayoutData.mTextViewSize.width ) ) || + ( wordOffset + relayoutParameters.mCharacterSize.width > relayoutData.mTextViewSize.width ) ) + { + if( !relayoutParameters.mIsNewLine && + ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) ) + { + // Current character is a white space. Don't want to move a white space to the next line. + // These white spaces are placed just in the edge. + return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f ); + } + else + { + // Calculates the line length and the max character height for the current line. + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) ); + + TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width, + relayoutParameters.mIndices, + lineLayoutInfo, + TextViewRelayout::WrapByWordAndSplit, + 1.f, // Shrink factor. + subLineInfo ); + + // Stores some info to calculate the line justification in a post-process. + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = subLineInfo.mLineLength; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + + return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f ); + } + } + else + { + return Vector3( wordOffset, previousPositionY, 0.f ); + } +} + +/** + * Calculates character position. + * @param[in] relayoutParameters Temporary layout parameters (previous size, previous position, ... ) + * @param[in] layoutParameters The layout parameters. + * @param[in] relayoutData The text-view's data structures. + * @return The character's position. + */ +Vector3 ShrinkWidthWhenExceedPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x ); + const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y ); + const Size wordSize = relayoutParameters.mWordSize * relayoutData.mShrinkFactor; + + if( ( relayoutParameters.mIsNewLine || relayoutParameters.mIsFirstCharacter ) || // isNewLine is true when '\n' is found. + ( relayoutParameters.mIsFirstCharacterOfWord && ( wordOffset + wordSize.width > relayoutData.mTextViewSize.width ) ) ) // The word doesn't fit in the parent width. + { + if( !relayoutParameters.mIsNewLine && + ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) ) + { + // Current character is a white space. Don't want to move a white space to the next line. + // These white spaces are placed just in the edge. + return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f ); + } + else + { + // Calculates the line length and the max character height for the current line. + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) ); + + TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width, + relayoutParameters.mIndices, + lineLayoutInfo, + TextViewRelayout::WrapByWord, + relayoutData.mShrinkFactor, + subLineInfo ); + + // Stores some info to calculate the line justification in a post-process. + TextView::LineJustificationInfo justificationInfo; + + justificationInfo.mIndices = relayoutParameters.mIndices; + justificationInfo.mLineLength = subLineInfo.mLineLength; + + relayoutData.mLineJustificationInfo.push_back( justificationInfo ); + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + + return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset * relayoutData.mShrinkFactor, 0.f ); + } + } + else + { + return Vector3( wordOffset, previousPositionY, 0.f ); + } +} + +void CalculatePositionsForShrinkWhenExceed( TextView::RelayoutData& relayoutData, + const TextView::LayoutParameters& layoutParameters, + const float shrinkFactor, + float& newTextHeight ) +{ + const float parentWidth = relayoutData.mTextViewSize.width; + TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo; + + // Reset the text height. This value is returned in order to shrink further or not the text. + newTextHeight = 0.f; + + // Whether the first character is being processed. + bool isFirstChar = true; + + // Stores the size of the previous character. + Size previousSize; + // Stores the position of the previous character. + Vector3 previousPosition; + + // Reset the index of lines. + TextViewProcessor::TextInfoIndices indices; + + // Whether the last character of the whole text is a new line char. + // This information is used to increase or not the height of the whole text by one line. + // Increase the whole text's height by one line is useful i.e. in TextInput to place the cursor + // after pressing 'Enter' in the last line. + bool isLastCharacterNewLineChar = false; + // Stores the height of the last character. This height used to be added to the whole text height if + // isLastCharacterNewLineChar is true. + float lastCharHeight = 0.f; + + relayoutData.mLines.clear(); + std::size_t characterGlobalIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt, ++indices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineIt ); + + // The next character is in a new line. + bool isNewLine = true; + + // Reset the index of groups of words. + indices.mGroupIndex = 0; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), groupEndIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt, ++indices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupIt ); + + // Reset the index of words. + indices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), wordEndIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt, ++indices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordIt ); + + // Reset the index of the character. + indices.mCharacterIndex = 0; + + // Whether current character is the first of the word. + bool isFirstCharOfWord = true; + const float wordOffset = previousPosition.x + previousSize.width; + + isLastCharacterNewLineChar = ( TextViewProcessor::LineSeparator == wordLayoutInfo.mType ); + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator charIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), charEndIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + charIt != charEndIt; + ++charIt, ++indices.mCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *charIt ); + lastCharHeight = characterLayoutInfo.mSize.height * shrinkFactor; + + const float previousPositionY = isFirstChar ? 0.f : previousPosition.y; + + if( ( isNewLine || isFirstChar ) || + ( isFirstCharOfWord && ( wordOffset + wordLayoutInfo.mSize.width * shrinkFactor > parentWidth ) ) ) + { + isFirstChar = false; + const float minWidth = std::min( parentWidth, textLayoutInfo.mWholeTextSize.width ); + + // Calculates the line length and the max character height for the current line. + TextViewRelayout::SubLineLayoutInfo subLineInfo; + subLineInfo.mLineLength = 0.f; + subLineInfo.mMaxCharHeight = 0.f; + subLineInfo.mMaxAscender = 0.f; + TextViewRelayout::CalculateSubLineLayout( minWidth, + indices, + lineLayoutInfo, + TextViewRelayout::WrapByWord, + shrinkFactor, + subLineInfo ); + + float justificationOffset = TextViewRelayout::CalculateJustificationOffset( layoutParameters.mLineJustification, minWidth, subLineInfo.mLineLength ); + + characterLayoutInfo.mPosition = Vector3( justificationOffset, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset * shrinkFactor, 0.f ); + + newTextHeight += subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset * shrinkFactor; + + Toolkit::TextView::LineLayoutInfo lineInfo; + lineInfo.mCharacterGlobalIndex = characterGlobalIndex; // Index to the first character of the next line. + lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line. + lineInfo.mAscender = subLineInfo.mMaxAscender; // Ascender of this piece of line. + relayoutData.mLines.push_back( lineInfo ); + } + else + { + characterLayoutInfo.mPosition = previousPosition + Vector3( previousSize.width, 0.f, 0.f ); + } + + // Get last line info and calculate the bearing. + const Toolkit::TextView::LineLayoutInfo& lineInfo( *( relayoutData.mLines.end() - 1 ) ); + const float bearingOffset = ( ( lineInfo.mSize.height - lineInfo.mAscender ) - ( characterLayoutInfo.mSize.height - characterLayoutInfo.mAscender ) ) * shrinkFactor; + + previousSize = characterLayoutInfo.mSize * shrinkFactor; + previousPosition = characterLayoutInfo.mPosition; + characterLayoutInfo.mPosition.y -= bearingOffset; + isFirstCharOfWord = false; + isNewLine = false; + + ++characterGlobalIndex; + } + } + } + } + + if( isLastCharacterNewLineChar ) + { + newTextHeight += lastCharHeight + layoutParameters.mLineHeightOffset * shrinkFactor; + } +} + +float RelayoutForShrinkToFit( TextView::RelayoutData& relayoutData, + const TextView::LayoutParameters& layoutParameters ) +{ + const Size& textViewSize = relayoutData.mTextViewSize; + TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo; + + // First step is assure the longest word fits in the text view width. + float shrinkFactor = ( textLayoutInfo.mMaxWordWidth > textViewSize.width ? textViewSize.width / textLayoutInfo.mMaxWordWidth : 1.f ); + + // Out parameter. Will store the new text height after relayout the text. + float newTextHeight = 0.f; + + // Relayout the text for the given character's sizes. + CalculatePositionsForShrinkWhenExceed( relayoutData, + layoutParameters, + shrinkFactor, + newTextHeight ); + + if( newTextHeight > textViewSize.height ) + { + // After relayouting, the text exceeds the text view height. + // Find a new scale factor to fit all the text in the text view size is needed. + + // The next algorithm does some iterations to calculate an acceptable scale factor. + // Some magic numbers are defined. + + const float MIN_RATIO( 0.90f ); // The algorithm finishes if the ratio + const float MAX_RATIO( 1.00f ); // new_text_height / text_view_height is between this two values + const unsigned int MAX_ITERATIONS( 8 ); // or max_iteration is reached. + + float ratio = newTextHeight / textViewSize.height; + + float maxScaleFactor = shrinkFactor; // bigger scale factors than maxScaleFactor will produce a too big text. + float minScaleFactor = shrinkFactor * ( textViewSize.height / newTextHeight ); // smaller scale factors than minScaleFactor will produce a too small text. + + for( unsigned int iterations = 0; ( ( MIN_RATIO > ratio ) || ( ratio > MAX_RATIO ) ) && ( iterations < MAX_ITERATIONS ); ++iterations ) + { + // Calculates the new scale factor. + // The new scale factor is always between the min and max scale factors. + // If ratio < 1 it means the text is too small and a bigger scale factor is needed. In this case the algorithm selects a new scale factor close to + // minScaleFactor. Alternatively if the text is too big a new scale factor close to maxScaleFactor is selected. + // This allows the text shrink or grow smoothly. + shrinkFactor = minScaleFactor + ( ratio < 1.f ? 0.4f : 0.6f ) * ( maxScaleFactor - minScaleFactor ); + + CalculatePositionsForShrinkWhenExceed( relayoutData, // Relayout the text for the given character's sizes. + layoutParameters, + shrinkFactor, + newTextHeight ); + + // Calculates the new text size ratio. It allows update the min and max scale factors. + // If the ratio is not good enough a new scale factor between min and max could be used in next iteration. + ratio = newTextHeight / textViewSize.height; + if( ratio < 1.f ) + { + minScaleFactor = shrinkFactor; + } + else + { + maxScaleFactor = shrinkFactor; + } + } + + if( ratio > MAX_RATIO ) + { + // The algorithm didn't find an acceptable scale factor. + // In that case the text is shrunk to fit in the boundaries of the text view actor. + shrinkFactor = minScaleFactor; + + CalculatePositionsForShrinkWhenExceed( relayoutData, + layoutParameters, + shrinkFactor, + newTextHeight ); + } + } + + return shrinkFactor; +} + +void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + TextViewRelayout::RelayoutParameters relayoutParameters; + + // clear + relayoutData.mCharacterLayoutInfoTable.clear(); + relayoutData.mLines.clear(); + relayoutData.mTextSizeForRelayoutOption = Size(); + + // Calculates the text size for split by char. + Vector4 minMaxXY( std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() ); + + relayoutData.mShrinkFactor = 1.f; // Shrink factor used when the exceed policy contains ShrinkToFit + + if( TextView::Shrink== layoutParameters.mExceedPolicy ) + { + // Relays-out the text for the shrink to fit policy. + relayoutData.mShrinkFactor = RelayoutForShrinkToFit( relayoutData, layoutParameters ); + } + else if( TextView::ShrinkOriginal == layoutParameters.mExceedPolicy ) + { + relayoutData.mShrinkFactor = ( relayoutData.mTextLayoutInfo.mMaxWordWidth > relayoutData.mTextViewSize.width ? relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mMaxWordWidth : 1.f ); + } + + relayoutParameters.mPositionOffset = Vector3::ZERO; + relayoutParameters.mIsFirstCharacter = true; + relayoutParameters.mIndices.mLineIndex = 0; + relayoutParameters.mCharacterGlobalIndex = 0; + + for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), + endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineLayoutIt != endLineLayoutIt; + ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex ) + { + TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt ); + + relayoutParameters.mIsNewLine = true; + relayoutParameters.mLineSize = lineLayoutInfo.mSize; + relayoutParameters.mIndices.mGroupIndex = 0; + + for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + groupLayoutIt != endGroupLayoutIt; + ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex ) + { + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt ); + + relayoutParameters.mIndices.mWordIndex = 0; + + for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + wordLayoutIt != endWordLayoutIt; + ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex ) + { + TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt ); + relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType; + relayoutParameters.mIsNewLineCharacter = TextViewProcessor::LineSeparator == wordLayoutInfo.mType; + + relayoutParameters.mIsFirstCharacterOfWord = true; + relayoutParameters.mWordSize = wordLayoutInfo.mSize; + relayoutParameters.mIndices.mCharacterIndex = 0; + + for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), + endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + ( characterLayoutIt != endCharacterLayoutIt ); + ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex ) + { + TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt ); + + relayoutParameters.mCharacterSize = characterLayoutInfo.mSize; + + switch( layoutParameters.mExceedPolicy ) + { + case TextView::OriginalShrink: + case TextView::SplitShrink: + case TextView::ShrinkFade: + { + DALI_LOG_WARNING( "SplitByWord::CalculateSizeAndPosition() policy not implemented.\n" ); + break; + } + case TextView::Original: + case TextView::OriginalFade: + case TextView::FadeOriginal: + case TextView::Fade: + case TextView::EllipsizeEndOriginal: + case TextView::EllipsizeEnd: // Fall Through + { + characterLayoutInfo.mPosition = OriginalPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f ); + break; + } + case TextView::SplitOriginal: + { + characterLayoutInfo.mPosition = SplitWhenExceedPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f ); + break; + } + case TextView::SplitFade: + { + characterLayoutInfo.mPosition = SplitWhenExceedPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f ); + break; + } + case TextView::ShrinkOriginal: + { + characterLayoutInfo.mPosition = ShrinkWidthWhenExceedPosition( relayoutParameters, + layoutParameters, + relayoutData ); + + relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor, 0.f, 0.f ); + break; + } + case TextView::Shrink: + { + // Does nothing. All the job has been done in the RelayoutForShrinkToFit() function. + break; + } + default: + { + DALI_LOG_WARNING( "SplitByWord::CalculateSizeAndPosition() policy combination not possible.\n" ); + } + } + + // Get last line info and calculate the bearing (used to align glyphs with the baseline). + if( TextView::Shrink != layoutParameters.mExceedPolicy ) + { + TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData ); + } + + // updates min and max position to calculate the text size for split by word. + TextViewRelayout::UpdateLayoutInfoTable( minMaxXY, + wordGroupLayoutInfo, + wordLayoutInfo, + characterLayoutInfo, + relayoutParameters, + relayoutData ); + + ++relayoutParameters.mCharacterGlobalIndex; + relayoutParameters.mIsFirstCharacter = false; + relayoutParameters.mIsFirstCharacterOfWord = false; + relayoutParameters.mIsNewLine = false; + } // end characters + } // end words + } // end group of words + } // end lines + + if( relayoutData.mCharacterLayoutInfoTable.empty() ) + { + relayoutData.mTextSizeForRelayoutOption = Size(); + } + else + { + relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x; + relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y; + } + + // Check if the last character is a new line character. In that case the height should be added. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + if( lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) // if it's empty, it means the last character is a new line character. + { + relayoutData.mTextSizeForRelayoutOption.height += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor; + } + } +} + +} // namespace + +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ) +{ + if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION ) + { + relayoutData.mLineJustificationInfo.clear(); + CalculateSizeAndPosition( layoutParameters, + relayoutData ); + + TextViewRelayout::SetUnderlineInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT ) + { + TextViewRelayout::UpdateAlignment( layoutParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY ) + { + TextViewRelayout::UpdateVisibility( layoutParameters, + visualParameters, + relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS ) + { + TextViewProcessor::InitializeTextActorInfo( relayoutData ); + } + + if( relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE ) + { + TextViewRelayout::UpdateTextActorInfo( visualParameters, + relayoutData ); + } + + if( ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW ) || + ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ) ) + { + TextViewRelayout::InsertToTextView( relayoutOperationMask, + textView, + relayoutData ); + } +} + +} // namespace SplitByWord + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/split-by-word-policies.h b/dali-toolkit/internal/controls/text-view/split-by-word-policies.h new file mode 100644 index 0000000..b17984c --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/split-by-word-policies.h @@ -0,0 +1,59 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SPLIT_BY_WORD_POLICIES_H__ +#define __DALI_TOOLKIT_INTERNAL_SPLIT_BY_WORD_POLICIES_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace SplitByWord +{ + +/** + * Sets text-actor's size and position accordingly with the given text-view's size and layout parameters. + * Visible text-actors are added to the text-view. Non visible actors are not added. + * + * @param[in] textView The handle to the text-view actor. + * @param[in] relayoutOperationMask Mask which defines which operations need to be done in the relayout process. + * @param[in] layoutParameters The layout parameters. + * @param[in] visualParameters Some visual parameters (fade, sort modifier and blending). + * @param[in] relayoutData The text-view's data structures which are modified by this function. + */ +void Relayout( Actor textView, + TextView::RelayoutOperationMask relayoutOperationMask, + const TextView::LayoutParameters& layoutParameters, + const TextView::VisualParameters& visualParameters, + TextView::RelayoutData& relayoutData ); + +} // namespace SplitByWord + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SPLIT_BY_WORD_POLICIES_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-actor-cache.cpp b/dali-toolkit/internal/controls/text-view/text-actor-cache.cpp new file mode 100644 index 0000000..3395b42 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-actor-cache.cpp @@ -0,0 +1,71 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +TextActorCache::TextActorCache() +: mTextActors() +{ +} + +TextActorCache::~TextActorCache() +{ +} + +void TextActorCache::InsertTextActors( const std::vector& textActors ) +{ + mTextActors.insert( mTextActors.end(), textActors.rbegin(), textActors.rend() ); +} + +TextActor TextActorCache::RetrieveTextActor() +{ + // Text-actors are inserted in the order needed to retrieve always the last one. + + // Returns a non initialized handle if the cache is empty. + TextActor textActor; + + if( !mTextActors.empty() ) + { + textActor = mTextActors.back(); + mTextActors.pop_back(); + } + + return textActor; +} + +void TextActorCache::ClearTexts() +{ + for( std::vector::iterator it = mTextActors.begin(); it != mTextActors.end(); ++it ) + { + (*it).SetText(""); + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-actor-cache.h b/dali-toolkit/internal/controls/text-view/text-actor-cache.h new file mode 100644 index 0000000..4848610 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-actor-cache.h @@ -0,0 +1,82 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_ACTOR_CACHE_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_ACTOR_CACHE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * Stores text-actors to be reused. + * + * Is it asumed that the first text-actor of a group of text-actors added to the cache is the first one to be rehused. + */ +class TextActorCache +{ + +public: + /** + * Default constructor. + */ + TextActorCache(); + + /** + * Destructor. + */ + ~TextActorCache(); + + /** + * Inserts the given text-actors into the cache. + * + * First text-actor of the vector is the first one to be reused. + * + * @param[in] textActors The text-actors to be inserted into the cache. + */ + void InsertTextActors( const std::vector& textActors ); + + /** + * Retrieves a text-actor from the cache. + * + * @return A handle to a text-actor. It returns a non initialized handle if the cache has no text-actors. + */ + TextActor RetrieveTextActor(); + + /** + * Clears the text of the text-actor in the cache. + */ + void ClearTexts(); + +private: + std::vector mTextActors; ///< Stores cached text-actors. +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_ACTOR_CACHE_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-processor.cpp b/dali-toolkit/internal/controls/text-view/text-processor.cpp new file mode 100644 index 0000000..ecb2c74 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-processor.cpp @@ -0,0 +1,358 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextProcessor +{ + +void SplitInLines( const MarkupProcessor::StyledTextArray& text, + std::vector& lines ) +{ + MarkupProcessor::StyledTextArray line; + for( MarkupProcessor::StyledTextArray::const_iterator it = text.begin(), endIt = text.end(); it != endIt; ++it ) + { + const MarkupProcessor::StyledText& styledText( *it ); + + for( size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i ) + { + const Dali::Character character = styledText.mText[i]; + + if( character.IsNewLine() ) // LF + { + Text newText( character ); + MarkupProcessor::StyledText newStyledText( newText, styledText.mStyle ); + line.push_back( newStyledText ); + + lines.push_back( line ); + line.clear(); + } + else + { + Text newText( character ); + MarkupProcessor::StyledText newStyledText( newText, styledText.mStyle ); + line.push_back( newStyledText ); + } + } + } + + // This line could be empty if the last character of the previous line is a 'new line' character + // and is the last of the text. + lines.push_back( line ); +} + +void SplitInWords( const MarkupProcessor::StyledTextArray& line, + std::vector& words ) +{ + MarkupProcessor::StyledTextArray word; + for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it ) + { + const MarkupProcessor::StyledText& styledText( *it ); + const Dali::Character character = styledText.mText[0]; + + if( character.IsWhiteSpace() ) + { + // When a separator is found, the previous word is added to the list, + // then a new word is initialized and the separator is also added as a word. + if( !word.empty() ) + { + words.push_back( word ); + word.clear(); // initializes a new word. + } + + // Separator added as a word. + MarkupProcessor::StyledText separatorChar; + separatorChar.mText.Append( character ); + separatorChar.mStyle = styledText.mStyle; + + MarkupProcessor::StyledTextArray separatorWord; + separatorWord.push_back( separatorChar ); + + words.push_back( separatorWord ); + } + else + { + MarkupProcessor::StyledText styledChar; + styledChar.mStyle = styledText.mStyle; + styledChar.mText.Append( character ); + + // Add the character to the current word. + word.push_back( styledChar ); + } + } + + //Finally the last word need to be added. + if( !word.empty() ) + { + words.push_back( word ); + } +} + +bool BeginsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& styledText ) +{ + for( MarkupProcessor::StyledTextArray::const_iterator it = styledText.begin(), endIt = styledText.end(); it != endIt; ++it ) + { + const Text& text( (*it).mText ); + + for( size_t i = 0, length = text.GetLength(); i < length; ++i ) + { + Character::CharacterDirection direction = text[i].GetCharacterDirection(); + if( direction != Character::Neutral ) + { + return ( direction == Character::RightToLeft || direction == Character::RightToLeftWeak ); + } + } + } + + return false; +} + +bool BeginsRightToLeftCharacter( const Text& text ) +{ + for( size_t i = 0, length = text.GetLength(); i < length; ++i ) + { + Character::CharacterDirection direction = text[i].GetCharacterDirection(); + if( direction != Character::Neutral ) + { + return ( direction == Character::RightToLeft || direction == Character::RightToLeftWeak ); + } + } + + return false; +} + +bool ContainsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& styledText ) +{ + for( MarkupProcessor::StyledTextArray::const_iterator it = styledText.begin(), endIt = styledText.end(); it != endIt; ++it ) + { + const Text& text( (*it).mText ); + + for( size_t i = 0, length = text.GetLength(); i < length; ++i ) + { + Character::CharacterDirection direction = text[i].GetCharacterDirection(); + if( ( Character::RightToLeft == direction ) || ( Character::RightToLeftWeak == direction ) ) + { + return true; + } + } + } + + return false; +} + +bool ContainsRightToLeftCharacter( const Dali::Text& text ) +{ + for( size_t i = 0, length = text.GetLength(); i < length; ++i ) + { + Character::CharacterDirection direction = ( text[i] ).GetCharacterDirection(); + if( ( Character::RightToLeft == direction ) || ( Character::RightToLeftWeak == direction ) ) + { + return true; + } + } + + return false; +} + +void ConvertBidirectionalText( const MarkupProcessor::StyledTextArray& line, + std::vector& convertedText, + std::vector& logicalToVisualMap, + std::vector& visualToLogicalMap ) +{ + // Clean vectors first. This function doesn't use any previous value. + logicalToVisualMap.clear(); + visualToLogicalMap.clear(); + convertedText.clear(); + + if( line.empty() ) + { + // nothing to do if the line is empty. + return; + } + + // Get the plain text from the line to be reordered by the BiDirectional algorithm. + std::string textToBeConverted; + GetPlainString( line, textToBeConverted ); + + const std::size_t stringSize = textToBeConverted.size(); + + std::vector logicalStrBuffer; + std::vector visualStrBuffer; + // unicode length <= UTF-8 length in bytes (reserve one extra for terminator) + // pad these buffers with 0's, as it's unclear what fribidi_log2vis does w.r.t. + // the length of it's output content (appears the same as input content, and does + // not seem to generate bidi marks i.e. FRIBIDI_CHAR_LRM/FRIBIDI_CHAR_RLM) + logicalStrBuffer.resize( stringSize+1, 0 ); + visualStrBuffer.resize( stringSize+1, 0 ); + FriBidiChar *logicalStr( &logicalStrBuffer[0] ); + FriBidiChar *visualStr( &visualStrBuffer[0] ); + + // Convert UTF-8 string to unicode string + const std::size_t length = fribidi_charset_to_unicode( FRIBIDI_CHAR_SET_UTF8, textToBeConverted.c_str(), stringSize, logicalStr ); + + if( 0 == length ) + { + DALI_ASSERT_DEBUG( !"TextProcessor::ConvertBidirectionalText. Error when calling at fribidi_charset_to_unicode" ); + + return; + } + + logicalToVisualMap.resize( length ); + visualToLogicalMap.resize( length ); + + // Convert and reorder the string as specified by the Unicode Bidirectional Algorithm + FriBidiCharType baseDirection = FRIBIDI_TYPE_ON; + fribidi_boolean log2vis = fribidi_log2vis( logicalStr, length, &baseDirection, visualStr, &logicalToVisualMap[0], &visualToLogicalMap[0], NULL ); + + if(log2vis) + { + // Convert the unicode string back to the UTF-8 string + std::vector bidiTextConverted; + + bidiTextConverted.resize( length * 4 + 1 ); // Maximum bytes to represent one UTF-8 character is 6. + // Currently Dali doesn't support this UTF-8 extension. Dali only supports 'regular' UTF-8 which has a maximum of 4 bytes per character. + + fribidi_unicode_to_charset( FRIBIDI_CHAR_SET_UTF8, visualStr, length, &bidiTextConverted[0] ); + + textToBeConverted = &bidiTextConverted[0]; + + // After reorder the text, rebuild the text with the original styles is needed. + // To assign the original style is needed to use the characterLogicalToVisualMap table. + Text text( &bidiTextConverted[0] ); + + // Split the line in groups of words. + // Words are grouped if they can be displayed left to right or right to left. + // Add the correct styles for the characters after they are reordered. + + MarkupProcessor::StyledTextArray groupOfWords; + + Character::CharacterDirection previousDirection = ( BeginsRightToLeftCharacter( line ) ? Character::RightToLeft : Character::LeftToRight ); + for( size_t i = 0; i < length; ++i ) + { + const Character character( text[i] ); + + Character::CharacterDirection currentDirection = character.GetCharacterDirection(); + if( Character::Neutral == currentDirection ) + { + currentDirection = previousDirection; + } + + MarkupProcessor::StyledText styledText; + styledText.mText.Append( character ); + styledText.mStyle = line[visualToLogicalMap[i]].mStyle; + + if( currentDirection != previousDirection ) + { + if( !groupOfWords.empty() ) + { + convertedText.push_back( groupOfWords ); + groupOfWords.clear(); + } + } + + groupOfWords.push_back( styledText ); + + previousDirection = currentDirection; + } + + if( !groupOfWords.empty() ) + { + convertedText.push_back( groupOfWords ); + } + } +} + +bool IsWhiteSpace( const MarkupProcessor::StyledTextArray& text, size_t offset ) +{ + DALI_ASSERT_DEBUG( offset < text.size() ); + + // assume 1 Character per StyledText + return text[offset].mText[0].IsWhiteSpace(); +} + +void FindNearestWord( const MarkupProcessor::StyledTextArray& text, size_t offset, size_t& start, size_t& end) +{ + const size_t size(text.size()); + offset = std::min(offset, size-1); + + size_t i(offset); + size_t j(offset); + + // if currently looking at whitespace, then search left and right for non-whitespace. + if(IsWhiteSpace(text, offset)) + { + // scan left until non-white space / beginning of string. + while(i > 0 && IsWhiteSpace(text, i)) + { + i--; + } + + // scan right until non-white space / end of string. + while(j < size && IsWhiteSpace(text, j)) + { + j++; + } + } + + // check if r.h.s. word is closer than l.h.s. word + if( (j - offset) < // distance to closest right word < + (offset - i) ) // distance to closest left word + { + // point left and right markers on start of right word + i = j; + } + else + { + // point left and right markers on end of left word + j = i; + } + + // expand left and right markers to encompase entire word + while(i > 0 && !IsWhiteSpace(text, i-1)) + { + i--; + } + + while(j < size && !IsWhiteSpace(text, j)) + { + j++; + } + + start = i; + end = j; +} + +} // namespace TextProcessor + +} // namespace Internal + +} // namespace DaliToolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-processor.h b/dali-toolkit/internal/controls/text-view/text-processor.h new file mode 100644 index 0000000..269b8ce --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-processor.h @@ -0,0 +1,118 @@ +#ifndef __DALI_TOOLKIT_TEXT_PROCESSOR_H__ +#define __DALI_TOOLKIT_TEXT_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextProcessor +{ + +/** + * Splits the given text in lines. + * + * @note Assumes the StyledTextArray has 1 Character per Text element. (which is the case for text in TextInput, but + * not necessarily the case for text in TextView) + * + * @param [in] text The given text. + * @param [out] lines The text split in lines. + */ +void SplitInLines( const MarkupProcessor::StyledTextArray& text, + std::vector& lines ); + +/** + * Splits the given line in words. + * + * @note Assumes the StyledTextArray has 1 Character per Text element. (which is the case for text in TextInput, but + * not necessarily the case for text in TextView) + * + * @param [in] line The given line. + * @param [out] words The line split in words. + */ +void SplitInWords( const MarkupProcessor::StyledTextArray& line, + std::vector& words ); + +/** + * Whether the text begins with right-to-left (bidirectional) character. + * @param [in] text The given text. + * @return \e true if the text begins right-to-left character. + */ +bool BeginsRightToLeftCharacter( const Text& text ); + +/** + * @copydoc BeginsRightToLeftCharacter( const Text& text ) + */ +bool BeginsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& text ); + +/** + * Whether the text contains any right-to-left (bidirectional) character. + * @param [in] text The given text. + * @return \e true if the text contains right-to-left character. + */ +bool ContainsRightToLeftCharacter( const Text& text ); + +/** + * @copydoc ContainsRightToLeftCharacter( const Text& text ) + */ +bool ContainsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& text ); + +/** + * Convert the text as specified by the Unicode Bidirectional Algorithm. + * The text is converted only if it is bidirectional. + * @param[in] line The line of text to be converted. + * @param[out] convertedText The text converted. Text is grouped in chunks which only have one direction. + * @param[out] logicalToVisualMap The character position map from the logical input text to the visual output text. + * @param[out] visualToLogicalMap The character position map from the visual output text to the logical input text. + */ + void ConvertBidirectionalText( const MarkupProcessor::StyledTextArray& line, + std::vector& convertedText, + std::vector& logicalToVisualMap, + std::vector& visualToLogicalMap ); + +/** + * Finds the nearest word in a string to a specified + * offset (in Characters). + * + * @note Assumes the StyledTextArray has 1 Character per Text element. (which is the case for text in TextInput, but + * not necessarily the case for text in TextView) + * + * @param[in] text The text to search through. + * @param[in] offset The current offset position to begin search from. + * @param[out] start The start position of nearest word + * @param[out] end The end position of nearest word + */ +void FindNearestWord( const MarkupProcessor::StyledTextArray& text, size_t offset, size_t& start, size_t& end); + +} // namespace TextProcessor + +} // namespace Internal + +} // namespace DaliToolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TEXT_PROCESSOR_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-character-processor.cpp b/dali-toolkit/internal/controls/text-view/text-view-character-processor.cpp new file mode 100644 index 0000000..36df1e6 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-character-processor.cpp @@ -0,0 +1,113 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +///////////////////// +// Layout info. +///////////////////// + +CharacterLayoutInfo::CharacterLayoutInfo() +: mHeight( 0.f ), + mAdvance( 0.f ), + mBearing( 0.f ), + mPosition(), + mOffset(), + mSize(), + mAscender( 0.f ), + mUnderlineThickness( 0.f ), + mUnderlinePosition( 0.f ), + mTextActor(), + mStyledText(), + mColorAlpha( 1.f ), + mGradientColor(), + mStartPoint(), + mEndPoint(), + mIsVisible( true ), + mSetText( true ), + mSetStyle( true ) +{ +} + +CharacterLayoutInfo::CharacterLayoutInfo( const CharacterLayoutInfo& character ) +: mHeight( character.mHeight ), + mAdvance( character.mAdvance ), + mBearing( character.mBearing ), + mPosition( character.mPosition ), + mOffset( character.mOffset ), + mSize( character.mSize ), + mAscender( character.mAscender ), + mUnderlineThickness( character.mUnderlineThickness ), + mUnderlinePosition( character.mUnderlinePosition ), + mTextActor( character.mTextActor ), + mStyledText( character.mStyledText ), + mColorAlpha( character.mColorAlpha ), + mGradientColor( character.mGradientColor ), + mStartPoint( character.mStartPoint ), + mEndPoint( character.mEndPoint ), + mIsVisible( character.mIsVisible ), + mSetText( character.mSetText ), + mSetStyle( character.mSetStyle ) +{ +} + +CharacterLayoutInfo& CharacterLayoutInfo::operator=( const CharacterLayoutInfo& character ) +{ + mHeight = character.mHeight; + mAdvance = character.mAdvance; + mBearing = character.mBearing; + + mPosition = character.mPosition; + mOffset = character.mOffset; + mSize = character.mSize; + mAscender = character.mAscender; + mUnderlineThickness = character.mUnderlineThickness; + mUnderlinePosition = character.mUnderlinePosition; + + mStyledText = character.mStyledText; + mColorAlpha = character.mColorAlpha; + mGradientColor = character.mGradientColor; + mStartPoint = character.mStartPoint; + mEndPoint = character.mEndPoint; + mIsVisible = character.mIsVisible; + mSetText = character.mSetText; + mSetStyle = character.mSetStyle; + + mTextActor = character.mTextActor; + + return *this; +} + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-impl.cpp b/dali-toolkit/internal/controls/text-view/text-view-impl.cpp new file mode 100644 index 0000000..add65d2 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-impl.cpp @@ -0,0 +1,2087 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "text-view-impl.h" + +// INTERNAL INCLUDES +#include "split-by-new-line-char-policies.h" +#include "split-by-word-policies.h" +#include "split-by-char-policies.h" +#include "text-view-processor.h" +#include "text-view-word-processor.h" +#include "relayout-utilities.h" +#include "text-view-processor-dbg.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +// Currently on desktop machines 2k x 2k is the maximum frame buffer size, on target is 4k x 4k. +const float MAX_OFFSCREEN_RENDERING_SIZE = 2048.f; + +//Type Registration +BaseHandle Create() +{ + return Toolkit::TextView::New(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::TextView), typeid(Toolkit::Control), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextView::SIGNAL_TEXT_SCROLLED , &TextView::DoConnectSignal ); + +/** + * Whether the text-view-processor operation sets, inserts, replaces, removes text. + * + * @param[in] metadata The text-view-processor operation. + * + * @return \e true if the given text-view-processor operation is modifying the text. + */ +bool IsTextViewProcessorRelayoutOperation( const TextView::TextViewProcessorMetadata& metadata ) +{ + return ( ( metadata.mType == TextView::TextSet ) || + ( metadata.mType == TextView::TextInserted ) || + ( metadata.mType == TextView::TextReplaced ) || + ( metadata.mType == TextView::TextRemoved ) || + ( metadata.mType == TextView::NewStyle )); +} + +/** + * Whether the text-view-processor operation sets a new line height offset. + * + * @param[in] metadata The text-view-processor operation. + * + * @return \e true if the given text-view-processor operation sets a new line height offset. + */ +bool IsTextViewProcessorLineHeightOffsetOperation( const TextView::TextViewProcessorMetadata& metadata ) +{ + return ( metadata.mType == TextView::NewLineHeight ); +} + +/** + * Whether the text-view-processor operation sets a new style. + * + * @param[in] metadata The text-view-processor operation. + * + * @return \e true if the given text-view-processor operation sets a new style. + */ +bool IsTextViewProcessorNewStyleOperation( const TextView::TextViewProcessorMetadata& metadata ) +{ + return ( metadata.mType == TextView::NewStyle ); +} + +} // namespace + +TextView::TextViewProcessorMetadata::TextViewProcessorMetadata() +: mType( TextView::TextSet ), + mPosition( 0 ), + mNumberOfCharacters( 0 ), + mText() +{ +} + +Toolkit::TextView TextView::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr textView = new TextView(); + + // Pass ownership to CustomActor + Toolkit::TextView handle( *textView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + textView->Initialize(); + + // Enables by default the offscreen rendering. + textView->SetSnapshotModeEnabled( false ); /// @note Temporary disabled due to some issues with text quality and glyph loading. + + return handle; +} + +void TextView::SetText( const std::string& text ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + // Calls SetText() with the styled text array. + SetText( styledText ); +} + +void TextView::SetText( const MarkupProcessor::StyledTextArray& text ) +{ + // mTextViewProcessorOperations stores the InsertTextAt and RemoveTextFrom operations to transform the initial text to mCurrentStyledText. + // Once again, if a new text is set, any previous call to InsertTextAt or RemoveTextFrom can be discarted. + + std::vector::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorRelayoutOperation ); + mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() ); + + // Creates metadata with the Set operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::TextSet; + metadata.mText = text; + + // Store metadata. + mTextViewProcessorOperations.push_back( metadata ); + + // Updates current styled text. + mCurrentStyledText = text; + + // Request to be relaid out + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; +} + +void TextView::InsertTextAt( std::size_t position, const std::string& text ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + // Calls InsertTextAt() with the styled text array. + InsertTextAt( position, styledText ); +} + +void TextView::InsertTextAt( const std::size_t position, const MarkupProcessor::StyledTextArray& text ) +{ + // Creates metadata with the Insert operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::TextInserted; + metadata.mPosition = position; + metadata.mText = text; + + // Store metadata. + mTextViewProcessorOperations.push_back( metadata ); + + // Updates current styled text. + mCurrentStyledText.insert( mCurrentStyledText.begin() + position, text.begin(), text.end() ); + + // Request to be relaid out + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; +} + +void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const std::string& text ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( text, styledText ); + + // Calls ReplaceTextFromTo() with the styled text array. + ReplaceTextFromTo( position, numberOfCharacters, styledText ); +} + +void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text ) +{ + // Creates metadata with the Insert operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::TextReplaced; + metadata.mPosition = position; + metadata.mNumberOfCharacters = numberOfCharacters; + metadata.mText = text; + + // Store metadata. + mTextViewProcessorOperations.push_back( metadata ); + + // Updates current styled text. + MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position; + mCurrentStyledText.erase( it, it + numberOfCharacters ); + it = mCurrentStyledText.begin() + position; + mCurrentStyledText.insert( it, text.begin(), text.end() ); + + // Request to be relaid out + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; +} + +void TextView::RemoveTextFrom( const std::size_t position, const std::size_t numberOfCharacters ) +{ + // Creates metadata with the Remove operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::TextRemoved; + metadata.mPosition = position; + metadata.mNumberOfCharacters = numberOfCharacters; + + // Store metadata. + mTextViewProcessorOperations.push_back( metadata ); + + // Updates current styled text. + MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position; + mCurrentStyledText.erase( it, it + numberOfCharacters ); + + // Request to be relaid out + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; +} + +std::string TextView::GetText() const +{ + // Traverses the styled text array getting only the text. + // Note that for some languages a 'character' could be represented by more than one 'char' + + std::string text; + for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); it != endIt; ++it ) + { + text.append( (*it).mText.GetText() ); + } + + return text; +} + +void TextView::SetLineHeightOffset( const PointSize offset ) +{ + if( fabsf( mLayoutParameters.mLineHeightOffset - offset ) > Math::MACHINE_EPSILON_1000 ) + { + // Removes any previous operation which modifies the line height offset. + std::vector::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorLineHeightOffsetOperation ); + mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() ); + + // Creates metadata with the new line height operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::NewLineHeight; + + mTextViewProcessorOperations.push_back( metadata ); + + // Updates line height offset. + mLayoutParameters.mLineHeightOffset = offset; + + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_SIZE_POSITION | + RELAYOUT_ALIGNMENT | + RELAYOUT_VISIBILITY | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + } +} + +PointSize TextView::GetLineHeightOffset() const +{ + return PointSize( mLayoutParameters.mLineHeightOffset ); +} + +void TextView::SetStyleToCurrentText( const TextStyle& style, const TextStyle::Mask mask ) +{ + if( !mCurrentStyledText.empty() ) + { + const bool checkFontName = mask & TextStyle::FONT; + const bool checkFontSize = mask & TextStyle::SIZE; + const bool checkFontStyle = mask & TextStyle::STYLE; + + // Check first if metrics have changed. + bool metricsChanged = false; + for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); ( it != endIt ) && !metricsChanged; ++it ) + { + const MarkupProcessor::StyledText& styledText( *it ); + + metricsChanged = ( checkFontName && ( styledText.mStyle.GetFontName() != style.GetFontName() ) ) || + ( checkFontStyle && ( styledText.mStyle.GetFontStyle() != style.GetFontStyle() ) ) || + ( checkFontSize && ( fabsf( styledText.mStyle.GetFontPointSize() - style.GetFontPointSize() ) > Math::MACHINE_EPSILON_1000 ) ); + } + + if( metricsChanged ) + { + MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask ); + + // If metrics change, new text measurements are needed. + SetText( mCurrentStyledText ); + } + else + { + // Deletes any previous operation which sets a new style. + std::vector::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorNewStyleOperation ); + mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() ); + + // Creates metadata with the new style operation. + TextViewProcessorMetadata metadata; + metadata.mType = TextView::NewStyle; + + MarkupProcessor::StyledText text; + text.mStyle = style; + metadata.mText.push_back( text ); + metadata.mStyleMask = mask; + + mTextViewProcessorOperations.push_back( metadata ); + + MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask ); + + RelayoutRequest(); + + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_TEXT_ACTOR_UPDATE ); + } + } + } + + // Sets the new style to the ellipsize text + if( !mLayoutParameters.mEllipsizeText.empty() ) + { + for( MarkupProcessor::StyledTextArray::iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it ) + { + (*it).mStyle.Copy( style, mask ); + } + + SetEllipsizeText( mLayoutParameters.mEllipsizeText ); + } +} + +void TextView::SetTextAlignment( Toolkit::Alignment::Type align ) +{ + if( align != ( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment ) ) + { + Toolkit::Alignment::Type horizontalAlignment( ( align & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft : + ( align & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter : + ( align & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) ); + Toolkit::Alignment::Type verticalAlignment( ( align & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop : + ( align & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter : + ( align & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) ); + + mLayoutParameters.mHorizontalAlignment = horizontalAlignment; + mLayoutParameters.mVerticalAlignment = verticalAlignment; + + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_ALIGNMENT | + RELAYOUT_VISIBILITY ); + } + } +} + +Toolkit::Alignment::Type TextView::GetTextAlignment() const +{ + return static_cast( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment ); +} + +void TextView::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy ) +{ + if( policy != mLayoutParameters.mMultilinePolicy ) + { + mLayoutParameters.mMultilinePolicy = policy; + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; + + RelayoutRequest(); + } +} + +Toolkit::TextView::MultilinePolicy TextView::GetMultilinePolicy() const +{ + return mLayoutParameters.mMultilinePolicy; +} + +void TextView::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy ) +{ + // The layout info could be invalid depending on the current exceed policy and the new one. + // i.e. if the current policy is Split and the new one is ShrinkToFit then + // the layout info generated for each char is not needed. + if( policy != mLayoutParameters.mWidthExceedPolicy ) + { + mLayoutParameters.mWidthExceedPolicy = policy; + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; + + RelayoutRequest(); + } +} + +Toolkit::TextView::ExceedPolicy TextView::GetWidthExceedPolicy() const +{ + return mLayoutParameters.mWidthExceedPolicy; +} + +void TextView::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy ) +{ + if( policy != mLayoutParameters.mHeightExceedPolicy ) + { + mLayoutParameters.mHeightExceedPolicy = policy; + + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_SIZE_POSITION | + RELAYOUT_ALIGNMENT | + RELAYOUT_VISIBILITY | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + } +} + +Toolkit::TextView::ExceedPolicy TextView::GetHeightExceedPolicy() const +{ + return mLayoutParameters.mHeightExceedPolicy; +} + +void TextView::SetLineJustification( Toolkit::TextView::LineJustification justification ) +{ + if( justification != mLayoutParameters.mLineJustification ) + { + mLayoutParameters.mLineJustification = justification; + + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_SIZE_POSITION | + RELAYOUT_ALIGNMENT | + RELAYOUT_VISIBILITY | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + } +} + +Toolkit::TextView::LineJustification TextView::GetLineJustification() const +{ + return mLayoutParameters.mLineJustification; +} + +void TextView::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ) +{ + if( ( fadeBoundary.mLeft != mVisualParameters.mFadeBoundary.mLeft ) || + ( fadeBoundary.mRight != mVisualParameters.mFadeBoundary.mRight ) || + ( fadeBoundary.mTop != mVisualParameters.mFadeBoundary.mTop ) || + ( fadeBoundary.mBottom != mVisualParameters.mFadeBoundary.mBottom ) ) + { + mVisualParameters.mFadeBoundary = fadeBoundary; + + RelayoutRequest(); + + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_VISIBILITY | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + } +} + +const Toolkit::TextView::FadeBoundary& TextView::GetFadeBoundary() const +{ + return mVisualParameters.mFadeBoundary; +} + +void TextView::SetEllipsizeText( const std::string& ellipsizeText ) +{ + // Creates a styled text with the markup or plain string. + MarkupProcessor::StyledTextArray styledText; + MarkupProcessor::GetStyledTextArray( ellipsizeText, styledText ); + + SetEllipsizeText( styledText ); +} + +void TextView::SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText ) +{ + mLayoutParameters.mEllipsizeText = ellipsizeText; + + mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo(); + + TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText, + mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo ); + + // Request to be relaid out + RelayoutRequest(); + + mRelayoutOperations = RELAYOUT_ALL; +} + +std::string TextView::GetEllipsizeText() const +{ + std::string text; + for( MarkupProcessor::StyledTextArray::const_iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it ) + { + text.append( (*it).mText.GetText() ); + } + + return text; +} + +void TextView::GetTextLayoutInfo() +{ + const bool relayoutSizeAndPositionNeeded = mRelayoutOperations & RELAYOUT_SIZE_POSITION; + const bool relayoutAlignmentNeeded = mRelayoutOperations & RELAYOUT_ALIGNMENT; + const bool relayoutVisibilityNeeded = mRelayoutOperations & RELAYOUT_VISIBILITY; + + if( relayoutSizeAndPositionNeeded || relayoutAlignmentNeeded || relayoutVisibilityNeeded ) + { + Vector3 textViewSize = GetControlSize(); + + if( ( ( textViewSize.width < Math::MACHINE_EPSILON_1000 ) || + ( textViewSize.height < Math::MACHINE_EPSILON_1000 ) ) && + ( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) && + ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) && + ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) ) ) + { + // In case the control size is not set but the layout settings are the default (split by new line character and original exceed policies) + // the text natural size can be used. + textViewSize = GetNaturalSize(); + } + + if( ( textViewSize.width > Math::MACHINE_EPSILON_1000 ) && + ( textViewSize.height > Math::MACHINE_EPSILON_1000 ) ) + { + // Check if the text-view has text-actors. + const bool hasTextActors = !mRelayoutData.mTextActors.empty(); + + RelayoutOperationMask mask = NO_RELAYOUT; + if( relayoutSizeAndPositionNeeded ) + { + mask = static_cast( mask | RELAYOUT_SIZE_POSITION ); + } + if( relayoutAlignmentNeeded ) + { + mask = static_cast( mask | RELAYOUT_ALIGNMENT ); + } + if( relayoutVisibilityNeeded ) + { + mask = static_cast( mask | RELAYOUT_VISIBILITY ); + } + + if( hasTextActors ) + { + // Remove text-actors from the text-view as some text-operation like CreateTextInfo() + // add them to the text-actor cache. + TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors ); + mRelayoutData.mTextActors.clear(); + } + + // Relays-out but doesn't add text-actors to the text-view. + DoRelayOut( textViewSize.GetVectorXY(), mask ); + + if( hasTextActors ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW ); + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + + } + } +} + +void TextView::GetTextLayoutInfo( Toolkit::TextView::TextLayoutInfo& textLayoutInfo ) +{ + GetTextLayoutInfo(); + + textLayoutInfo.mCharacterLayoutInfoTable = mRelayoutData.mCharacterLayoutInfoTable; + textLayoutInfo.mLines = mRelayoutData.mLines; + + textLayoutInfo.mCharacterLogicalToVisualMap = mRelayoutData.mCharacterLogicalToVisualMap; + textLayoutInfo.mCharacterVisualToLogicalMap = mRelayoutData.mCharacterVisualToLogicalMap; + + textLayoutInfo.mTextSize = mRelayoutData.mTextSizeForRelayoutOption; + + textLayoutInfo.mScrollOffset = mVisualParameters.mCameraScrollPosition; +} + +void TextView::SetSortModifier( float depthOffset ) +{ + mVisualParameters.mSortModifier = depthOffset; + + for( std::vector::iterator it = mRelayoutData.mTextActors.begin(), endIt = mRelayoutData.mTextActors.end(); + it != endIt; + ++it ) + { + ( *it ).SetSortModifier( depthOffset ); + } + + if( mOffscreenImageActor ) + { + mOffscreenImageActor.SetSortModifier( depthOffset ); + } +} + +void TextView::SetSnapshotModeEnabled( bool enable ) +{ + if( enable != mVisualParameters.mSnapshotModeEnabled ) + { + // Remove first all text-actors + if( !mRelayoutData.mTextActors.empty() ) + { + TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors ); + } + + mVisualParameters.mSnapshotModeEnabled = enable; + if( !mLockPreviousSnapshotMode ) + { + // mPreviousSnapshotModeEnabled stores the snapshot mode value before SetScrollEnabled( true ) is + // called. However, if SetSnapshotModeEnabled() is called after SetScrollEnabled() then the stored value + // is updated. + // As SetSnapshotModeEnabled() is also called from SetScrollEnabled(), the mLockPreviousSnapshotMode prevents + // to smash the stored value. + mPreviousSnapshotModeEnabled = enable; + } + + if( mVisualParameters.mSnapshotModeEnabled ) + { + // Create a root actor and an image actor for offscreen rendering. + mOffscreenRootActor = Layer::New(); + mOffscreenImageActor = ImageActor::New(); + + mOffscreenRootActor.SetColorMode( USE_OWN_COLOR ); + mOffscreenRootActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION ); + mOffscreenRootActor.SetInheritRotation( false ); + mOffscreenRootActor.SetInheritScale( false ); + mOffscreenRootActor.SetDepthTestDisabled( true ); + + mOffscreenRootActor.SetPosition( 0.f, 0.f, 0.f ); + + mOffscreenImageActor.SetAnchorPoint( ParentOrigin::CENTER ); + mOffscreenImageActor.SetParentOrigin( ParentOrigin::CENTER ); + + Actor self = Self(); + self.Add( mOffscreenRootActor ); + self.Add( mOffscreenImageActor ); + } + else + { + Actor self = Self(); + + if( mOffscreenRootActor ) + { + self.Remove( mOffscreenRootActor ); + } + + if( mOffscreenImageActor ) + { + self.Remove( mOffscreenImageActor ); + } + + DestroyOffscreenRenderingResources(); + } + + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + RelayoutRequest(); + } +} + +bool TextView::IsSnapshotModeEnabled() const +{ + return mVisualParameters.mSnapshotModeEnabled; +} + +void TextView::SetScrollEnabled( const bool enable ) +{ + if( enable != mVisualParameters.mScrollEnabled ) + { + mVisualParameters.mScrollEnabled = enable; + + if( mVisualParameters.mScrollEnabled ) + { + // Offscreen rendering is needed to enable text scroll. + + // Stores previous value of the snapshot mode. + mPreviousSnapshotModeEnabled = IsSnapshotModeEnabled(); + + { + // SetSnapshotModeEnabled() modifies the mPreviousSnapshotModeEnabled just in case it's called after SetScrollEnabled(), + // this lock prevents to modify the mPreviousSnapshotModeEnabled when SetSnapshotModeEnabled() from this method. + Lock lock( mLockPreviousSnapshotMode ); + SetSnapshotModeEnabled( true ); + } + + // Creates the pan gesture detector and attach the text-view. + mPanGestureDetector = PanGestureDetector::New(); + mPanGestureDetector.DetectedSignal().Connect( this, &TextView::OnTextPan ); + mPanGestureDetector.Attach( Self() ); + } + else + { + // Removes the pan gesture detector. + if( mPanGestureDetector ) + { + mPanGestureDetector.Detach( Self() ); + mPanGestureDetector.DetectedSignal().Disconnect( this, &TextView::OnTextPan ); + mPanGestureDetector.Reset(); + } + + // Restores the previous state for snapshot mode. + SetSnapshotModeEnabled( mPreviousSnapshotModeEnabled ); + } + } +} + +bool TextView::IsScrollEnabled() const +{ + return mVisualParameters.mScrollEnabled; +} + +void TextView::SetScrollPosition( const Vector2& position ) +{ + if( position != mVisualParameters.mCameraScrollPosition ) + { + // Guard against destruction during signal emission + // Note that Emit() methods are called indirectly from within DoSetScrollPosition() + Toolkit::TextView handle( GetOwner() ); + + DoSetScrollPosition( position ); + + // Check if the new scroll position has been trimmed. + mVisualParameters.mScrollPositionTrimmed = ( position != mVisualParameters.mCameraScrollPosition ); + } +} + +const Vector2& TextView::GetScrollPosition() const +{ + return mVisualParameters.mCameraScrollPosition; +} + +bool TextView::IsScrollPositionTrimmed() const +{ + return mVisualParameters.mScrollPositionTrimmed; +} + +Toolkit::TextView::ScrolledSignalV2& TextView::ScrolledSignal() +{ + return mScrolledSignalV2; +} + +bool TextView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::TextView textView = Toolkit::TextView::DownCast(handle); + + if( Dali::Toolkit::TextView::SIGNAL_TEXT_SCROLLED == signalName ) + { + textView.ScrolledSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +TextView::LayoutParameters::LayoutParameters() +: mMultilinePolicy( Toolkit::TextView::SplitByNewLineChar ), + mWidthExceedPolicy( Toolkit::TextView::Original ), + mHeightExceedPolicy( Toolkit::TextView::Original ), + mHorizontalAlignment( Toolkit::Alignment::HorizontalCenter ), + mVerticalAlignment( Toolkit::Alignment::VerticalCenter ), + mLineJustification( Toolkit::TextView::Left ), + mLineHeightOffset( 0.f ), + mEllipsizeText() +{ + // Sets ellipsize text + MarkupProcessor::StyledTextArray styledEllipsize; + MarkupProcessor::GetStyledTextArray( std::string( "..." ), mEllipsizeText ); +} + +TextView::LayoutParameters::LayoutParameters( const Toolkit::TextView::MultilinePolicy multilinePolicy, + const Toolkit::TextView::ExceedPolicy widthExceedPolicy, + const Toolkit::TextView::ExceedPolicy heightExceedPolicy, + const Toolkit::Alignment::Type alignmentType, + const Toolkit::TextView::LineJustification lineJustification, + const float lineHeightOffset, + const std::string& ellipsizeText ) +: mMultilinePolicy( multilinePolicy ), + mWidthExceedPolicy( widthExceedPolicy ), + mHeightExceedPolicy( heightExceedPolicy ), + mHorizontalAlignment(), + mVerticalAlignment(), + mLineJustification( lineJustification ), + mLineHeightOffset( lineHeightOffset ), + mEllipsizeText() +{ + // Sets alignment + Toolkit::Alignment::Type horizontalAlignment( ( alignmentType & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft : + ( alignmentType & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter : + ( alignmentType & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) ); + Toolkit::Alignment::Type verticalAlignment( ( alignmentType & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop : + ( alignmentType & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter : + ( alignmentType & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) ); + + mHorizontalAlignment = horizontalAlignment; + mVerticalAlignment = verticalAlignment; + + // Sets ellipsize text + MarkupProcessor::StyledTextArray styledEllipsize; + MarkupProcessor::GetStyledTextArray( ellipsizeText, mEllipsizeText ); +} + +TextView::LayoutParameters::LayoutParameters( const TextView::LayoutParameters& layoutParameters ) +: mMultilinePolicy( layoutParameters.mMultilinePolicy ), + mWidthExceedPolicy( layoutParameters.mWidthExceedPolicy ), + mHeightExceedPolicy( layoutParameters.mHeightExceedPolicy ), + mHorizontalAlignment( layoutParameters.mHorizontalAlignment ), + mVerticalAlignment( layoutParameters.mVerticalAlignment ), + mLineJustification( layoutParameters.mLineJustification ), + mLineHeightOffset( layoutParameters.mLineHeightOffset ), + mEllipsizeText( layoutParameters.mEllipsizeText ) +{ +} + +TextView::LayoutParameters& TextView::LayoutParameters::operator=( const TextView::LayoutParameters& layoutParameters ) +{ + mMultilinePolicy = layoutParameters.mMultilinePolicy; + mWidthExceedPolicy = layoutParameters.mWidthExceedPolicy; + mHeightExceedPolicy = layoutParameters.mHeightExceedPolicy; + mHorizontalAlignment = layoutParameters.mHorizontalAlignment; + mVerticalAlignment = layoutParameters.mVerticalAlignment; + mLineJustification = layoutParameters.mLineJustification; + mLineHeightOffset = layoutParameters.mLineHeightOffset; + mEllipsizeText = layoutParameters.mEllipsizeText; + + return *this; +} + +TextView::VisualParameters::VisualParameters() +: mFadeBoundary(), + mSortModifier( 0.f ), + mCameraScrollPosition( 0.f, 0.f ), + mSnapshotModeEnabled( false ), + mScrollEnabled( false ), + mScrollPositionTrimmed( false ) +{ +} + +TextView::VisualParameters::VisualParameters( const VisualParameters& visualParameters ) +: mFadeBoundary( visualParameters.mFadeBoundary ), + mSortModifier( visualParameters.mSortModifier ), + mCameraScrollPosition( visualParameters.mCameraScrollPosition ), + mSnapshotModeEnabled( visualParameters.mSnapshotModeEnabled ), + mScrollEnabled( visualParameters.mScrollEnabled ), + mScrollPositionTrimmed( visualParameters.mScrollPositionTrimmed ) +{ +} + +TextView::VisualParameters& TextView::VisualParameters::operator=( const TextView::VisualParameters& visualParameters ) +{ + mFadeBoundary = visualParameters.mFadeBoundary; + mSortModifier = visualParameters.mSortModifier; + mCameraScrollPosition = visualParameters.mCameraScrollPosition; + mSnapshotModeEnabled = visualParameters.mSnapshotModeEnabled; + mScrollEnabled = visualParameters.mScrollEnabled; + mScrollPositionTrimmed = visualParameters.mScrollPositionTrimmed; + + return *this; +} + +TextView::RelayoutData::RelayoutData() +: mTextViewSize(), + mShrinkFactor( 1.f ), + mTextLayoutInfo(), + mCharacterLogicalToVisualMap(), + mCharacterVisualToLogicalMap(), + mTextActors(), + mCharacterLayoutInfoTable(), + mLines(), + mTextSizeForRelayoutOption() +{ +} + +TextView::RelayoutData::RelayoutData( const TextView::RelayoutData& relayoutData ) +: mTextViewSize( relayoutData.mTextViewSize ), + mShrinkFactor( relayoutData.mShrinkFactor ), + mTextLayoutInfo( relayoutData.mTextLayoutInfo ), + mCharacterLogicalToVisualMap( relayoutData.mCharacterLogicalToVisualMap ), + mCharacterVisualToLogicalMap( relayoutData.mCharacterVisualToLogicalMap ), + mTextActors( relayoutData.mTextActors ), + mCharacterLayoutInfoTable( relayoutData.mCharacterLayoutInfoTable ), + mLines( relayoutData.mLines ), + mTextSizeForRelayoutOption( relayoutData.mTextSizeForRelayoutOption ) +{ +} + +TextView::RelayoutData& TextView::RelayoutData::operator=( const TextView::RelayoutData& relayoutData ) +{ + mTextViewSize = relayoutData.mTextViewSize; + mShrinkFactor = relayoutData.mShrinkFactor; + mTextLayoutInfo = relayoutData.mTextLayoutInfo; + mCharacterLogicalToVisualMap = relayoutData.mCharacterLogicalToVisualMap; + mCharacterVisualToLogicalMap = relayoutData.mCharacterVisualToLogicalMap; + mTextActors = relayoutData.mTextActors; + mCharacterLayoutInfoTable = relayoutData.mCharacterLayoutInfoTable; + mLines = relayoutData.mLines; + mTextSizeForRelayoutOption = relayoutData.mTextSizeForRelayoutOption; + + return *this; +} + +TextView::TextView() +: ControlImpl( false ), // doesn't require touch events + mCurrentStyledText(), + mTextViewProcessorOperations(), + mLayoutParameters( Toolkit::TextView::SplitByNewLineChar, + Toolkit::TextView::Original, + Toolkit::TextView::Original, + static_cast( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ), + Toolkit::TextView::Left, + PointSize( 0.f ), + std::string( "..." ) ), + mVisualParameters(), + mRelayoutData(), + mRelayoutOperations( NO_RELAYOUT ), + mOffscreenRootActor(), + mOffscreenImageActor(), + mOffscreenCameraActor(), + mCurrentOffscreenSize(), + mFrameBufferImage(), + mRenderTask(), + mPanGestureDetector(), + mLockPreviousSnapshotMode( false ), + mPreviousSnapshotModeEnabled( false ) +{ + TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText, + mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo ); +} + +TextView::~TextView() +{ + // Destroys offscreen rendering resources. + DestroyOffscreenRenderingResources(); + + // Destroys scroll pan gesture detector. + if( mPanGestureDetector ) + { + mPanGestureDetector.Reset(); + } +} + +Vector3 TextView::GetNaturalSize() +{ + if( !mTextViewProcessorOperations.empty() ) + { + // There are SetText, Inserts or Removes to do. It means the current layout info is not updated. + + if( !mRelayoutData.mTextActors.empty() ) + { + // Remove text-actors from the text-view as some text-operation like CreateTextInfo() + // add them to the text-actor cache. + TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors ); + mRelayoutData.mTextActors.clear(); + + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW ); + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + + PerformTextViewProcessorOperations(); + } + + return Vector3( mRelayoutData.mTextLayoutInfo.mWholeTextSize.width, mRelayoutData.mTextLayoutInfo.mWholeTextSize.height, 0.f ); +} + +float TextView::GetHeightForWidth( float width ) +{ + float height = 0.f; + + if( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) && + ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) && + ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) ) + { + // If multiline and exceed policies are 'SplitByNewLineChar' and 'Original' is better get the height from the + // natural size. GetNaturalSize() for this configuration is faster than DoRelayOut(). + height = GetNaturalSize().height; + } + else + { + // Check if the given width is different than the current one. + const bool differentWidth = ( fabsf( width - mRelayoutData.mTextViewSize.width ) > Math::MACHINE_EPSILON_1000 ); + + // Check if the text-view has text-actors. + const bool hasTextActors = !mRelayoutData.mTextActors.empty(); + + // Check which layout operations need to be done. + const bool relayoutSizeAndPositionNeeded = ( mRelayoutOperations & RELAYOUT_SIZE_POSITION ) || differentWidth; + + if( relayoutSizeAndPositionNeeded ) + { + if( hasTextActors ) + { + // Remove text-actors from the text-view as some text-operation like CreateTextInfo() + // add them to the text-actor cache. + TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors ); + mRelayoutData.mTextActors.clear(); + } + + // Use the given width. + const Vector2 textViewSize( width, GetControlSize().height ); + + // Relays-out but doesn't add text-actors to the text-view. + DoRelayOut( textViewSize, RELAYOUT_SIZE_POSITION ); + } + + // Retrieve the text height after relayout the text. + height = mRelayoutData.mTextSizeForRelayoutOption.height; + + if( differentWidth ) + { + // Revert the relayout operation mask + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_SIZE_POSITION ); + } + + if( hasTextActors ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW ); + mRelayoutOperations = static_cast( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + + if( differentWidth || hasTextActors ) + { + RelayoutRequest(); + } + } + + return height; +} + +float TextView::GetWidthForHeight( float height ) +{ + // TODO: Needs implementing properly, for now just return the natural width. + return GetNaturalSize().width; +} + +void TextView::OnPropertySet( Property::Index index, Property::Value propertyValue ) +{ + if( index == mPropertyText ) + { + SetText(propertyValue.Get()); + } + else if( index == mPropertyMultilinePolicy ) + { + OnMultilinePolicyPropertySet(propertyValue); + } + else if( index == mPropertyWidthExceedPolicy ) + { + OnWidthExceedPolicyPropertySet(propertyValue); + } + else if( index == mPropertyHeightExceedPolicy ) + { + OnHeightExceedPolicyPropertySet(propertyValue); + } + else if( index == mPropertyLineJustification ) + { + OnLineJustificationPropertySet(propertyValue); + } + else if( ( index == mPropertyFadeBoundaryLeft ) || + ( index == mPropertyFadeBoundaryRight ) || + ( index == mPropertyFadeBoundaryTop ) || + ( index == mPropertyFadeBoundaryBottom ) ) + { + OnFadeBoundaryPropertySet( index, propertyValue ); + } + else if( index == mPropertyLineHeightOffset ) + { + Dali::PointSize pointSize( propertyValue.Get() ); + SetLineHeightOffset(pointSize); + } + else if ( ( index == mPropertyHorizontalAlignment ) || + ( index == mPropertyVerticalAlignment ) ) + { + OnAlignmentPropertySet( index, propertyValue ); + } +} + +void TextView::OnInitialize() +{ + Actor self = Self(); + + mPropertyText = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_TEXT, "", Property::READ_WRITE ); + + mPropertyMultilinePolicy = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_MULTILINE_POLICY, "SplitByNewLineChar", Property::READ_WRITE ); + + mPropertyWidthExceedPolicy = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_WIDTH_EXCEED_POLICY, "Original", Property::READ_WRITE ); + + mPropertyHeightExceedPolicy = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_HEIGHT_EXCEED_POLICY, "Original", Property::READ_WRITE ); + + mPropertyLineJustification = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_LINE_JUSTIFICATION, "Left", Property::READ_WRITE ); + + mPropertyFadeBoundaryLeft = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_FADE_BOUNDARY_LEFT, static_cast< int >( 0 ), Property::READ_WRITE ); + + mPropertyFadeBoundaryRight = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_FADE_BOUNDARY_RIGHT, static_cast< int >( 0 ), Property::READ_WRITE ); + + mPropertyFadeBoundaryTop = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_FADE_BOUNDARY_TOP, static_cast< int >( 0 ), Property::READ_WRITE ); + + mPropertyFadeBoundaryBottom = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_FADE_BOUNDARY_BOTTOM, static_cast< int >( 0 ), Property::READ_WRITE ); + + mPropertyLineHeightOffset = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_LINE_HEIGHT_OFFSET, 0.0f, Property::READ_WRITE ); + + mPropertyHorizontalAlignment = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_HORIZONTAL_ALIGNMENT, "HorizontalCenter", Property::READ_WRITE ); + + mPropertyVerticalAlignment = self.RegisterProperty( Dali::Toolkit::TextView::PROPERTY_VERTICAL_ALIGNMENT, "VerticalCenter", Property::READ_WRITE ); + +} + + +void TextView::OnStyleChange( StyleChange change ) +{ + mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo(); + TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText, + mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo ); + + SetText( mCurrentStyledText ); +} + +void TextView::OnControlSizeSet( const Vector3& size ) +{ + if( size.GetVectorXY() != mRelayoutData.mTextViewSize ) + { + // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values. + mRelayoutOperations = RELAYOUT_ALL; + + // Request to be relaid out + RelayoutRequest(); + } +} + +void TextView::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) ) + { + // Not worth to relayout if width or height is equal to zero. + return; + } + + if( size != mRelayoutData.mTextViewSize ) + { + // if new size is different than the prevoius one, set positions and maybe sizes of all text-actor is needed. + if( RELAYOUT_ALL != mRelayoutOperations ) + { + mRelayoutOperations = static_cast( mRelayoutOperations | + RELAYOUT_REMOVE_TEXT_ACTORS | + RELAYOUT_SIZE_POSITION | + RELAYOUT_ALIGNMENT | + RELAYOUT_VISIBILITY | + RELAYOUT_TEXT_ACTOR_UPDATE | + RELAYOUT_INSERT_TO_TEXT_VIEW | + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ); + } + } + + // Remove text-actors from text-view + if( !mRelayoutData.mTextActors.empty() && ( mRelayoutOperations & RELAYOUT_REMOVE_TEXT_ACTORS ) ) + { + TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors ); + mRelayoutData.mTextActors.clear(); + } + + if( NO_RELAYOUT != mRelayoutOperations ) + { + // Relays-out and add text-actors to the text-view. + DoRelayOut( size, mRelayoutOperations ); + ProcessSnapshot( size ); + } + + // Quite likely the texts of the text-actors are not going to be reused, so clear them. + mRelayoutData.mTextActorCache.ClearTexts(); +} + +void TextView::PerformTextViewProcessorOperations() +{ + // Traverse the relayout operation vector ... + + // Optimizes some operations. + OptimizeTextViewProcessorOperations(); + + for( std::vector::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it ) + { + const TextViewProcessorMetadata& relayoutMetadata( *it ); + + switch( relayoutMetadata.mType ) + { + case TextView::TextSet: + { + TextViewProcessor::CreateTextInfo( relayoutMetadata.mText, + mLayoutParameters, + mRelayoutData ); + break; + } + case TextView::TextInserted: + { + TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition, + relayoutMetadata.mText, + mLayoutParameters, + mRelayoutData ); + break; + } + case TextView::TextReplaced: + { + TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition, + relayoutMetadata.mNumberOfCharacters, + relayoutMetadata.mText, + mLayoutParameters, + mRelayoutData ); + break; + } + case TextView::TextRemoved: + { + TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition, + relayoutMetadata.mNumberOfCharacters, + mLayoutParameters, + mRelayoutData, + TextViewProcessor::CLEAR_TEXT ); // clears the text of the text-actors. + break; + } + case TextView::NewLineHeight: + { + TextViewProcessor::UpdateTextInfo( mLayoutParameters.mLineHeightOffset, + mRelayoutData.mTextLayoutInfo ); + break; + } + case TextView::NewStyle: + { + TextViewProcessor::UpdateTextInfo( ( *relayoutMetadata.mText.begin() ).mStyle, + relayoutMetadata.mStyleMask, + mRelayoutData ); + break; + } + } + } + + // Clear all operations when they are done. + mTextViewProcessorOperations.clear(); +} + +void TextView::OptimizeTextViewProcessorOperations() +{ + // TODO: check if some operation can be discarted. i.e. InsertTextAt( 0, "a" ); RemoveTextFrom( 0, 1 ); + + // At the moment it only replaces a 'remove 1 character' followed by 'insert 1 character' in the same position by a 'replace' operation. + // This sequence is used by text-input with predictive text. Change this two operations by a replace allows the text-view processor to + // use the cache without clearing the text-actors. + + std::vector textViewProcessorOperations; + + for( std::vector::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it ) + { + const TextViewProcessorMetadata& relayoutMetadata( *it ); + + switch( relayoutMetadata.mType ) + { + case TextView::TextRemoved: + { + bool optimizationDone = false; + + if( it + 1 != endIt ) + { + const TextViewProcessorMetadata& nextRelayoutMetadata( *( it + 1 ) ); + if( TextView::TextInserted == nextRelayoutMetadata.mType ) + { + if( relayoutMetadata.mPosition == nextRelayoutMetadata.mPosition ) + { + optimizationDone = true; + TextViewProcessorMetadata newRelayoutMetadata; + newRelayoutMetadata.mType = TextView::TextReplaced; + newRelayoutMetadata.mPosition = relayoutMetadata.mPosition; + newRelayoutMetadata.mNumberOfCharacters = relayoutMetadata.mNumberOfCharacters; + newRelayoutMetadata.mText = nextRelayoutMetadata.mText; + textViewProcessorOperations.push_back( newRelayoutMetadata ); + + // do not access the TextInserted operation in next iteration. + ++it; + } + } + } + + if( !optimizationDone ) + { + textViewProcessorOperations.push_back( relayoutMetadata ); + } + break; + } + default: + { + textViewProcessorOperations.push_back( relayoutMetadata ); + } + } + } + + mTextViewProcessorOperations = textViewProcessorOperations; +} + +void TextView::DoRelayOut( const Size& textViewSize, const RelayoutOperationMask relayoutOperationMask ) +{ + // Traverse the relayout operation vector. It fills the natural size, layout and text-actor data structures. + if( !mTextViewProcessorOperations.empty() ) + { + PerformTextViewProcessorOperations(); + } + + CombineExceedPolicies(); + + Actor rootActor; + if( mVisualParameters.mSnapshotModeEnabled ) + { + rootActor = mOffscreenRootActor; + } + else + { + rootActor = Self(); + } + + mRelayoutData.mTextViewSize = textViewSize; + switch( mLayoutParameters.mMultilinePolicy ) + { + case Toolkit::TextView::SplitByNewLineChar: // multiline policy == SplitByNewLineChar. + { + SplitByNewLineChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData ); + break; + } // SplitByNewLineChar + + case Toolkit::TextView::SplitByWord: // multiline policy == SplitByWord. + { + SplitByWord::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData ); + break; + } // SplitByWord + + case Toolkit::TextView::SplitByChar: // multiline policy == SplitByChar. + { + SplitByChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData ); + break; + } // SplitByChar + } // switch( mMultilinePolicy ) + + // Remove done operations from the mask. + mRelayoutOperations = static_cast( mRelayoutOperations & ~relayoutOperationMask ); +} + +void TextView::ProcessSnapshot( const Size& textViewSize ) +{ + if( mVisualParameters.mSnapshotModeEnabled ) + { + // If layout options change, it's needed generate a new image. + + if( mOffscreenRootActor ) + { + // Set the root actor visible. + // The root actor is set to non visible after the render task is processed. + mOffscreenRootActor.SetVisible( true ); + + // The offscreen root actor must have same size as text view. Otherwise, text alignment won't work. + mOffscreenRootActor.SetSize( textViewSize ); + } + + if( ( mRelayoutData.mTextSizeForRelayoutOption.width > Math::MACHINE_EPSILON_1000 ) && + ( mRelayoutData.mTextSizeForRelayoutOption.height > Math::MACHINE_EPSILON_1000 ) ) + { + // Set the image actor visible. + // The image actor is set to non visible if there is no text to render. + mOffscreenImageActor.SetVisible( true ); + + // Calculates the offscreen image's size. It takes into account different points: + // * If text has italics, add a small offset is needed in order to not to cut the text next to the right edge. + // * There is a maximum texture size the graphic subsystem can load on the memory. + // * If the scroll is enabled, the offscreen image's size is never bigger than the text-view's size. + + const Size offscreenSize( std::min( MAX_OFFSCREEN_RENDERING_SIZE, + mVisualParameters.mScrollEnabled ? textViewSize.width : std::max( mRelayoutData.mTextSizeForRelayoutOption.width, textViewSize.width ) + mRelayoutData.mTextLayoutInfo.mMaxItalicsOffset ), + std::min( MAX_OFFSCREEN_RENDERING_SIZE, + mVisualParameters.mScrollEnabled ? textViewSize.height : std::max( mRelayoutData.mTextSizeForRelayoutOption.height, textViewSize.height ) ) ); + + const bool sizeChanged = offscreenSize != mCurrentOffscreenSize; + + if( sizeChanged ) + { + // Creates a frame buffer for offscreen rendering when the size is negotiated. + mFrameBufferImage = FrameBufferImage::New( offscreenSize.width, + offscreenSize.height, + Pixel::RGBA8888 ); + + // Stores current text-view size to avoid create new Dali resources if text changes. + mCurrentOffscreenSize = offscreenSize; + + if( !mOffscreenCameraActor ) + { + // Creates a new camera actor. + mOffscreenCameraActor = CameraActor::New(); + mOffscreenCameraActor.SetParentOrigin( ParentOrigin::CENTER ); + mOffscreenCameraActor.SetAnchorPoint( AnchorPoint::CENTER ); + + mOffscreenCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Inherits position from the offscreen root actor. + + mOffscreenRootActor.Add( mOffscreenCameraActor ); // camera to shoot the offscreen text + + // Images are expected to be from top to bottom, but OpenGL buffers are bottom to top + mOffscreenCameraActor.SetInvertYAxis( false ); + } + + // Calculate camera parameters for current text size. + mOffscreenCameraActor.SetOrthographicProjection( offscreenSize ); + } + + if( mVisualParameters.mScrollEnabled ) + { + // Updates the offscreen camera position with the new scroll offset. + mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x ); + mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y ); + } + else + { + // Text's size could be bigger than text-view's size. In that case the camera must be aligned to cover the whole text. + AlignOffscreenCameraActor( textViewSize, offscreenSize ); + } + + if( !mRenderTask ) + { + // Creates a new render task. + mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask(); + + mRenderTask.SetSourceActor( mOffscreenRootActor ); + mRenderTask.SetInputEnabled( false ); + mRenderTask.SetClearColor( Color::TRANSPARENT ); + mRenderTask.SetClearEnabled( true ); + mRenderTask.SetExclusive( true ); + + // Connects the signal to the TextView::RenderTaskFinished method in order to make the root actor non visible when the render task is processed. + mRenderTask.FinishedSignal().Connect( this, &TextView::RenderTaskFinished ); + } + + if( sizeChanged ) + { + mRenderTask.SetCameraActor( mOffscreenCameraActor ); + mRenderTask.SetTargetFrameBuffer( mFrameBufferImage ); + } + + // Process the render task only once every time the text changes or the text-view's size canges. + mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE ); + } + else + { + // If there is no text just make any previous generated image invisible instead to process a render task with no text. + mOffscreenImageActor.SetVisible( false ); + } + } +} + +void TextView::AlignOffscreenCameraActor( const Size& textViewSize, const Size& offscreenSize ) +{ + float xPosition = 0.f; + float yPosition = 0.f; + Vector3 parentOrigin = ParentOrigin::CENTER; + Vector3 anchorPoint = AnchorPoint::CENTER; + + switch( mLayoutParameters.mHorizontalAlignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + xPosition = 0.5f * ( offscreenSize.width - textViewSize.width ); + parentOrigin.x = 0.f; + anchorPoint.x = 0.f; + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + // nothing to do. + break; + } + case Toolkit::Alignment::HorizontalRight: + { + xPosition = 0.5f * ( textViewSize.width - offscreenSize.width ); + parentOrigin.x = 1.f; + anchorPoint.x = 1.f; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." ) + } + } + + switch( mLayoutParameters.mVerticalAlignment ) + { + case Toolkit::Alignment::VerticalTop: + { + yPosition = 0.5f * ( offscreenSize.height - textViewSize.height ); + parentOrigin.y = 0.f; + anchorPoint.y = 0.f; + break; + } + case Toolkit::Alignment::VerticalCenter: + { + // nothing to do. + break; + } + case Toolkit::Alignment::VerticalBottom: + { + yPosition = 0.5f * ( textViewSize.height - offscreenSize.height ); + parentOrigin.y = 1.f; + anchorPoint.y = 1.f; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." ) + } + } + + mOffscreenCameraActor.SetX( xPosition ); + mOffscreenCameraActor.SetY( yPosition ); + + mOffscreenImageActor.SetParentOrigin( parentOrigin ); + mOffscreenImageActor.SetAnchorPoint( anchorPoint ); +} + +void TextView::RenderTaskFinished( Dali::RenderTask& renderTask ) +{ + // not to process the offscreen root actor by setting its visibility to false. + mOffscreenRootActor.SetVisible( false ); + + // Sets the new size and the new frame buffer to the image actor. + // Image actor must have same size as text. Otherwise text can be truncated. + mOffscreenImageActor.SetSize( mCurrentOffscreenSize ); + mOffscreenImageActor.SetImage( mFrameBufferImage ); +} + +void TextView::DestroyOffscreenRenderingResources() +{ + if( mRenderTask ) + { + mRenderTask.FinishedSignal().Disconnect( this, &TextView::RenderTaskFinished ); + + if( Stage::IsInstalled() ) + { + Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask ); + } + + mRenderTask.Reset(); + } + + // Remove and reset the root actor, image actor and camera actor as text-view is not rendering offscreen. + if( mOffscreenCameraActor ) + { + mOffscreenRootActor.Remove( mOffscreenCameraActor ); + + mOffscreenCameraActor.Reset(); + } + + if( mOffscreenRootActor ) + { + mOffscreenRootActor.Reset(); + } + + if( mOffscreenImageActor ) + { + mOffscreenImageActor.Reset(); + } + + mCurrentOffscreenSize = Size( 0.f, 0.f ); + + if( mFrameBufferImage ) + { + mFrameBufferImage.Reset(); + } +} + +void TextView::OnTextPan( Actor actor, PanGesture gesture ) +{ + if( 1u == gesture.numberOfTouches ) + { + DoSetScrollPosition( mVisualParameters.mCameraScrollPosition - gesture.displacement ); + } +} + +void TextView::TrimScrollPosition() +{ + const Vector3& textViewSize = GetControlSize(); + + // Before use the text's size, relayout the text is needed to get the actual text size. + GetTextLayoutInfo(); + + // Calculates the range within the text could be scrolled. (When the text is aligned in the center). + float maxHorizontalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.width - textViewSize.width ) ); + float maxVerticalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.height - textViewSize.height ) ); + float minHorizontalDisplacement = -maxHorizontalDisplacement; + float minVerticalDisplacement = -maxVerticalDisplacement; + + // Updates the range if the text is aligned on the right or left. + switch( mLayoutParameters.mHorizontalAlignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + maxHorizontalDisplacement *= 2.f; + minHorizontalDisplacement = 0.f; + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + // nothing to do. + break; + } + case Toolkit::Alignment::HorizontalRight: + { + maxHorizontalDisplacement = 0.f; + minHorizontalDisplacement *= 2.f; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." ) + } + } + + // Updates the range if the text is aligned on the top or bottom. + switch( mLayoutParameters.mVerticalAlignment ) + { + case Toolkit::Alignment::VerticalTop: + { + maxVerticalDisplacement *= 2.f; + minVerticalDisplacement = 0.f; + break; + } + case Toolkit::Alignment::VerticalCenter: + { + //nothing to do + break; + } + case Toolkit::Alignment::VerticalBottom: + { + maxVerticalDisplacement = 0.f; + minVerticalDisplacement *= 2.f; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." ) + } + } + + // Trims the scroll position to be within the range. + mVisualParameters.mCameraScrollPosition.x = std::min( maxHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x ); + mVisualParameters.mCameraScrollPosition.x = std::max( minHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x ); + + mVisualParameters.mCameraScrollPosition.y = std::min( maxVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y ); + mVisualParameters.mCameraScrollPosition.y = std::max( minVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y ); +} + +void TextView::DoSetScrollPosition( const Vector2& position ) +{ + // Stores old scroll position. + Vector2 delta( mVisualParameters.mCameraScrollPosition ); + + // Updates the scroll position + mVisualParameters.mCameraScrollPosition = position; + + // Ensures the text-view is covered with text. + TrimScrollPosition(); + + // Calculate the difference with the previous scroll position + delta.x = mVisualParameters.mCameraScrollPosition.x - delta.x; + delta.y = mVisualParameters.mCameraScrollPosition.y - delta.y; + + if( mOffscreenRootActor ) + { + // If there is a render-task it needs to be refreshed. Therefore text-actors need to be + // set to visible. + mOffscreenRootActor.SetVisible( true ); + } + + if( mOffscreenCameraActor ) + { + // Update the offscreen camera with the new scroll position. + mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x ); + mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y ); + } + + if( mRenderTask ) + { + // Refresh the render-task. + mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE ); + } + + // Emit the signal. + Toolkit::TextView handle( GetOwner() ); + mScrolledSignalV2.Emit( handle, delta ); +} + +void TextView::CombineExceedPolicies() +{ + // Calculates the combination of exceed policies. + + switch( mLayoutParameters.mWidthExceedPolicy ) + { + case Toolkit::TextView::Original: + { + switch( mLayoutParameters.mHeightExceedPolicy ) + { + case Toolkit::TextView::Original: + { + mLayoutParameters.mExceedPolicy = Original; + break; + } + case Toolkit::TextView::Fade: + { + mLayoutParameters.mExceedPolicy = OriginalFade; + break; + } + case Toolkit::TextView::ShrinkToFit: + { + mLayoutParameters.mExceedPolicy = OriginalShrink; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" ); + } + } + break; + } + case Toolkit::TextView::Split: + { + switch( mLayoutParameters.mHeightExceedPolicy ) + { + case Toolkit::TextView::Original: + { + mLayoutParameters.mExceedPolicy = SplitOriginal; + break; + } + case Toolkit::TextView::Fade: + { + mLayoutParameters.mExceedPolicy = SplitFade; + break; + } + case Toolkit::TextView::ShrinkToFit: + { + mLayoutParameters.mExceedPolicy = SplitShrink; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" ); + } + } + break; + } + case Toolkit::TextView::Fade: + { + switch( mLayoutParameters.mHeightExceedPolicy ) + { + case Toolkit::TextView::Original: + { + mLayoutParameters.mExceedPolicy = FadeOriginal; + break; + } + case Toolkit::TextView::Fade: + { + mLayoutParameters.mExceedPolicy = Fade; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" ); + } + } + break; + } + case Toolkit::TextView::ShrinkToFit: + { + switch( mLayoutParameters.mHeightExceedPolicy ) + { + case Toolkit::TextView::Original: + { + mLayoutParameters.mExceedPolicy = ShrinkOriginal; + break; + } + case Toolkit::TextView::Fade: + { + mLayoutParameters.mExceedPolicy = ShrinkFade; + break; + } + case Toolkit::TextView::ShrinkToFit: + { + mLayoutParameters.mExceedPolicy = Shrink; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" ); + } + } + break; + } + case Toolkit::TextView::EllipsizeEnd: + { + switch( mLayoutParameters.mHeightExceedPolicy ) + { + case Toolkit::TextView::Original: + { + mLayoutParameters.mExceedPolicy = EllipsizeEndOriginal; + break; + } + case Toolkit::TextView::EllipsizeEnd: + { + mLayoutParameters.mExceedPolicy = EllipsizeEnd; + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" ); + } + } + break; + } + default: + { + DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width exceed policy" ); + } + } +} + +Actor TextView::GetRootActor() const +{ + // Get the root actor, if text-view was rendering offscreen, or the text-view itself. + + Actor rootActor; + + if( mVisualParameters.mSnapshotModeEnabled ) + { + rootActor = mOffscreenRootActor; + } + else + { + rootActor = Self(); + } + + return rootActor; +} + +void TextView::OnMultilinePolicyPropertySet( Property::Value propertyValue ) +{ + std::string policyName( propertyValue.Get() ); + if(policyName == "SplitByNewLineChar") + { + SetMultilinePolicy(Toolkit::TextView::SplitByNewLineChar); + } + else if(policyName == "SplitByWord") + { + SetMultilinePolicy(Toolkit::TextView::SplitByWord); + } + else if(policyName == "SplitByChar") + { + SetMultilinePolicy(Toolkit::TextView::SplitByChar); + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnMultilinePolicyPropertySet(). Invalid Property value." ); + } +} + +void TextView::OnWidthExceedPolicyPropertySet( Property::Value propertyValue ) +{ + std::string policyName( propertyValue.Get() ); + if(policyName == "Original") + { + SetWidthExceedPolicy(Toolkit::TextView::Original); + } + else if(policyName == "Truncate") + { + SetWidthExceedPolicy(Toolkit::TextView::Truncate); + } + else if(policyName == "Fade") + { + SetWidthExceedPolicy(Toolkit::TextView::Fade); + } + else if(policyName == "Split") + { + SetWidthExceedPolicy(Toolkit::TextView::Split); + } + else if(policyName == "ShrinkToFit") + { + SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit); + } + else if(policyName == "EllipsizeEnd") + { + SetWidthExceedPolicy(Toolkit::TextView::EllipsizeEnd); + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnWidthExceedPolicyPropertySet(). Invalid Property value." ); + } +} + +void TextView::OnHeightExceedPolicyPropertySet( Property::Value propertyValue ) +{ + std::string policyName( propertyValue.Get() ); + if(policyName == "Original") + { + SetHeightExceedPolicy(Toolkit::TextView::Original); + } + else if(policyName == "Truncate") + { + SetHeightExceedPolicy(Toolkit::TextView::Truncate); + } + else if(policyName == "Fade") + { + SetHeightExceedPolicy(Toolkit::TextView::Fade); + } + else if(policyName == "Split") + { + SetHeightExceedPolicy(Toolkit::TextView::Split); + } + else if(policyName == "ShrinkToFit") + { + SetHeightExceedPolicy(Toolkit::TextView::ShrinkToFit); + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnHeightExceedPolicyPropertySet(). Invalid Property value." ); + } +} + +void TextView::OnLineJustificationPropertySet( Property::Value propertyValue ) +{ + std::string policyName( propertyValue.Get() ); + if(policyName == "Left") + { + SetLineJustification(Toolkit::TextView::Left); + } + else if(policyName == "Center") + { + SetLineJustification(Toolkit::TextView::Center); + } + else if(policyName == "Right") + { + SetLineJustification(Toolkit::TextView::Right); + } + else if(policyName == "Justified") + { + SetLineJustification(Toolkit::TextView::Justified); + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnLineJustificationPropertySet(). Invalid Property value." ); + } +} + +void TextView::OnFadeBoundaryPropertySet( Property::Index propertyIndex, Property::Value propertyValue ) +{ + PixelSize boundary( propertyValue.Get() ); + + if ( propertyIndex == mPropertyFadeBoundaryLeft ) + { + mVisualParameters.mFadeBoundary.mLeft = boundary; + } + else if ( propertyIndex == mPropertyFadeBoundaryRight ) + { + mVisualParameters.mFadeBoundary.mRight = boundary; + } + else if ( propertyIndex == mPropertyFadeBoundaryTop ) + { + mVisualParameters.mFadeBoundary.mTop = boundary; + } + else if ( propertyIndex == mPropertyFadeBoundaryBottom ) + { + mVisualParameters.mFadeBoundary.mBottom = boundary; + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnFadeBoundaryPropertySet(). Invalid Property value." ); + } +} + +void TextView::OnAlignmentPropertySet( Property::Index propertyIndex, Property::Value propertyValue ) +{ + std::string value( propertyValue.Get() ); + + if( propertyIndex == mPropertyHorizontalAlignment ) + { + if(value == "HorizontalLeft") + { + mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalLeft; + } + else if( value == "HorizontalCenter") + { + mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalCenter; + } + else if( value == "HorizontalRight") + { + mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalRight; + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." ); + } + } + else if( propertyIndex == mPropertyVerticalAlignment ) + { + if( value == "VerticalTop" ) + { + mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalTop; + } + else if( value == "VerticalCenter") + { + mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalCenter; + } + else if( value == "VerticalBottom") + { + mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalBottom; + } + else + { + DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." ); + } + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-impl.h b/dali-toolkit/internal/controls/text-view/text-view-impl.h new file mode 100644 index 0000000..7e5c556 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-impl.h @@ -0,0 +1,735 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include +#include +#include +#include "text-actor-cache.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * TextView is a custom control for text aligning and multiline support + */ +class TextView : public ControlImpl +{ +public: + + /** + * Internal exceed policy with the valid combinations. + */ + enum ExceedPolicy + { + Original, ///< Original size (even if it exceeds the witdh or the height). + OriginalFade, ///< Original size if it exceeds the width but faded if it exceeds the height. + OriginalShrink, ///< Shrunk if it exceeds the height. + SplitOriginal, ///< Split if it exceeds the width but no action if it exceeds the height. + SplitFade, ///< Split if it exceeds the width and faded if it exceeds the height. + SplitShrink, ///< Split if it exceeds the width and shrunk if it exceeds the height. + Fade, ///< Faded if it exceeds any boundary. + FadeOriginal, ///< Faded if it exceeds the width but no action if it exceeds the height. + ShrinkOriginal, ///< Shrunk if it exceeds the width but no action if it exceeds the height. + ShrinkFade, ///< Shrunk if it exceeds the width and faded if it exceeds the height. + Shrink, ///< Shrunk if it exceeds any boundary. + EllipsizeEndOriginal, ///< Ellipsized by the end if it exceeds the width but no action if it exceeds the height. + EllipsizeEnd ///< Ellipsized by the end if it exceeds the width and/or the height. + }; + + // Between two OnRelaidOut methods, several calls to InsertTextAt, RemoveTextFrom or SetText can happen. + // TextViewProcessorMetadata stores the type of operation. A vector stores all operations between two OnRelaidOut calls. + + enum TextViewProcessorMetadataType + { + TextSet, ///< Sets new text. + TextInserted, ///< Inserts text into current text. + TextReplaced, ///< Replaces some text from current text. + TextRemoved, ///< Removes some text from current text. + NewLineHeight, ///< Sets a new line height offset. + NewStyle ///< Sets a new style to the whole text. + }; + + /** + * Stores info about which data structures need to be modified when the OnRelaidOut() method is called + */ + struct TextViewProcessorMetadata + { + TextViewProcessorMetadata(); + + TextViewProcessorMetadataType mType; ///< Stores the type of operation. + std::size_t mPosition; ///< Character position within the text. + std::size_t mNumberOfCharacters; ///< Number of characters to be removed/ replaced. + MarkupProcessor::StyledTextArray mText; ///< The new text. + TextStyle::Mask mStyleMask; ///< The style mask. + }; + + /** + * Defines which operations have to be done in the relayout process. + */ + enum RelayoutOperationMask + { + NO_RELAYOUT = 0x0, ///< Does nothing. + RELAYOUT_REMOVE_TEXT_ACTORS = 0x1, ///< Removes current text-actors from the text-view. + RELAYOUT_SIZE_POSITION = 0x2, ///< Calculates size and position of the text but it doesn't calculate alignment. + RELAYOUT_ALIGNMENT = 0x4, ///< Aligns the whole text. + RELAYOUT_VISIBILITY = 0x8, ///< Calculates the visibility. + RELAYOUT_INITIALIZE_TEXT_ACTORS = 0x10, ///< Initialize text-actors (create handles). + RELAYOUT_TEXT_ACTOR_UPDATE = 0x20, ///< Updates text-actors (set size, position, style, ...) + RELAYOUT_INSERT_TO_TEXT_VIEW = 0x40, ///< Adds the text-actors to the text-view. + RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST = 0x80, ///< Inserts the text-actors to the text-actor list. + RELAYOUT_ALL = 0xFF ///< Does all operations. + }; + + ////////////////////////////////////////////// + + /** + * Create a new TextView. + * @return A smart-pointer to the newly allocated TextView. + */ + static Toolkit::TextView New(); + + /** + * @copydoc SetText( const std::string& text ) + */ + void SetText( const std::string& text ); + + /** + * @copydoc SetText( const StyledTextArray& text ) + */ + void SetText( const MarkupProcessor::StyledTextArray& text ); + + /** + * @copydoc InsertTextAt( std::size_t position, const std::string& text ) + */ + void InsertTextAt( std::size_t position, const std::string& text ); + + /** + * @copydoc InsertTextAt( std::size_t position, const std::string& text ) + */ + void InsertTextAt( std::size_t position, const MarkupProcessor::StyledTextArray& text ); + + /** + * @copydoc RemoveTextFrom( std::size_t position, std::size_t numberOfCharacters ) + */ + void RemoveTextFrom( std::size_t position, std::size_t numberOfCharacters ); + + /** + * @copydoc ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const std::string& text ) + */ + void ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const std::string& text ); + + /** + * @copydoc ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const std::string& text ) + */ + void ReplaceTextFromTo( std::size_t position, std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text ); + + /** + * @copydoc GetText() + */ + std::string GetText() const; + + /** + * @copydoc SetFont( const Font newFont ) + */ + void SetFont( const Font newFont ); + + /** + * @copydoc SetLineHeightOffset() + */ + void SetLineHeightOffset( PointSize offset ); + + /** + * @copydoc GetLineHeightOffset() + */ + PointSize GetLineHeightOffset() const; + + /** + * @copydoc SetStyleToCurrentText() + */ + void SetStyleToCurrentText( const TextStyle& style, TextStyle::Mask mask ); + + /** + * @copydoc SetTextAlignment( Toolkit::Alignment::Type align ) + */ + void SetTextAlignment( Toolkit::Alignment::Type align ); + + /** + * @copydoc GetTextAlignment() + */ + Toolkit::Alignment::Type GetTextAlignment() const; + + /** + * @copydoc SetMultilinePolicy() + */ + void SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy ); + + /** + * @copydoc GetMultilinePolicy() + */ + Toolkit::TextView::MultilinePolicy GetMultilinePolicy() const; + + /** + * @copydoc SetWidthExceedPolicy() + */ + void SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * @copydoc GetWidthExceedPolicy() + */ + Toolkit::TextView::ExceedPolicy GetWidthExceedPolicy() const; + + /** + * @copydoc SetHeightExceedPolicy() + */ + void SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy ); + + /** + * @copydoc GetHeightExceedPolicy() + */ + Toolkit::TextView::ExceedPolicy GetHeightExceedPolicy() const; + + /** + * @copydoc SetLineJustification() + */ + void SetLineJustification( Toolkit::TextView::LineJustification justification ); + + /** + * @copydoc GetLineJustification() + */ + Toolkit::TextView::LineJustification GetLineJustification() const; + + /** + * @copydoc SetFadeBoundary() + */ + void SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ); + + /** + * @copydoc GetFadeBoundary() + */ + const Toolkit::TextView::FadeBoundary& GetFadeBoundary() const; + + /** + * @copydoc SetEllipsizeText() + */ + void SetEllipsizeText( const std::string& ellipsizeText ); + + /** + * @copydoc SetEllipsizeText() + */ + void SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText ); + + /** + * @copydoc GetEllipsizeText() + */ + std::string GetEllipsizeText() const; + + /** + * Checks if relayout the text is needed. If it is, it relais out the text + * by calling DoRelayOut(). + */ + void GetTextLayoutInfo(); + + /** + * Calls GetTextLayoutInfo() and fills the given data structure. + * + * @see GetTextLayoutInfo() + */ + void GetTextLayoutInfo( Toolkit::TextView::TextLayoutInfo& textLayoutInfo ); + + /** + * @copydoc SetSortModifier() + */ + void SetSortModifier( float depthOffset ); + + /** + * @copydoc SetSnapshotModeEnabled() + */ + void SetSnapshotModeEnabled( bool enable ); + + /** + * @copydoc IsSnapshotModeEnabled() + */ + bool IsSnapshotModeEnabled() const; + + /** + * @copydoc SetScrollEnabled() + */ + void SetScrollEnabled( bool enable ); + + /** + * @copydoc IsScrollEnabled() + */ + bool IsScrollEnabled() const; + + /** + * @copydoc SetScrollPosition() + * @see DoSetScrollPosition() + */ + void SetScrollPosition( const Vector2& position ); + + /** + * @copydoc GetScrollPosition() + */ + const Vector2& GetScrollPosition() const; + + /** + * @copydoc IsScrollPositionTrimmed() + */ + bool IsScrollPositionTrimmed() const; + + /** + * @copydoc ScrolledSignal() + */ + Toolkit::TextView::ScrolledSignalV2& ScrolledSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Control::OnStyleChange() + */ + virtual void OnStyleChange( StyleChange change ); + + /** + * @copydoc Toolkit::Control::OnControlSizeSet() + */ + virtual void OnControlSizeSet( const Vector3& size ); + + /** + * @copydoc Toolkit::Control::OnRelaidOut() + * + * Removes text-actor and calls DoRelayOut().. + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + + /** + * Retrieves the text-view's natural size. + * + * @return The natural size. + */ + virtual Vector3 GetNaturalSize(); + + /** + * Retrieves the text-view's \e height for a given \e width. + * + * @param[in] width The given \e width. + * + * @return The \e height for the given \e width. + */ + virtual float GetHeightForWidth( float width ); + + /** + * Retrieves the text-view's \e width for a given \e height. + * + * @param[in] height The given \e height. + * + * @return The \e width for the given \e height. + */ + virtual float GetWidthForHeight( float height ); + + /** + * @copydoc Dali::CustomActorImpl::OnPropertySet() + */ + virtual void OnPropertySet( Property::Index index, Property::Value propertyValue ); + +protected: + + /** + * Construct a new TextView. + */ + TextView(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~TextView(); + +private: + + // Undefined + TextView( const TextView& ); + + // Undefined + TextView& operator=( const TextView& rhs ); + + /** + * Executes synchronously relayout operations such as set, insert, remove or replace text, or + * split groups of characters, etc. + */ + void PerformTextViewProcessorOperations(); + + /** + * Optimizes some text-view processor operations. + */ + void OptimizeTextViewProcessorOperations(); + + /** + * Synchronously relays-out all text-actors. + * + * Perform text-view-processor operations, sets the new size and position of text-actors and adds them to the text-view. + * + * @param[in] textViewSize The new text-view's size. + * @param[in] relayoutOperationMask Defines which operations need to be done in the relayout process. + */ + void DoRelayOut( const Size& textViewSize, RelayoutOperationMask relayoutOperationMask ); + + /** + * Process Snapshot. It refresh the render-task in order to generate a new snapshot image. + * + * ProcessSnapshot is called from OnRelaidOut() only if text has been relaid out. + * It creates a new image buffer only if the size of the text has changed. + * + * @param[in] textViewSize The new text-view's size. + */ + void ProcessSnapshot( const Size& textViewSize ); + + /** + * Aligns the offscreen rendering camera actor to cover the whole text and the resulting image actor accordingly with the text view's alignment. + * + * @param[in] textViewSize The text view's size. + * @param[in] offscreenSize The offscreen's frame buffer's size. + */ + void AlignOffscreenCameraActor( const Size& textViewSize, const Size& offscreenSize ); + + /** + * Callback called when the render task has processed. + * + * It makes the root actor invisible. + * + * @param[in] renderTask The processed render task. + */ + void RenderTaskFinished( Dali::RenderTask& renderTask ); + + /** + * Destroys offscreen rendering resources. + * + * It disconects the render task finished signal from the TextView::RenderTaskFinished() method, + * removes the render task from the render task list and resets the offscreen camera actor, root actor, + * image actor and the frame buffer. + */ + void DestroyOffscreenRenderingResources(); + + /** + * Called when text-view is scrolled. + * + * Sets the new scroll position. @see DoSetScrollPosition() + * + * @param[in] actor Handle of the text-view. + * @param[in] gesture Data structure with the parameters of the gesture. + */ + void OnTextPan( Actor actor, PanGesture gesture ); + + /** + * Ensures the text-view's boundaries are fully covered of text. + * + * i.e. if the text-view's size is 100x100 and the text's size is 150x100, the scroll position + * can be in the range -50,0 and 50,0. + */ + void TrimScrollPosition(); + + /** + * Called from SetScrollPosition() and OnTextPan() + * + * Updates the stored scroll position ensuring the text-view is always covered with text by calling + * TrimScrollPosition(), calculates the difference with the previous one and emits the Toolkit::TextView::SignalScrolled() signal. + * + * @param[in] position The new scroll position. + */ + void DoSetScrollPosition( const Vector2& position ); + + /** + * Combines width and height exceed policies. + * + * This method is a big switch() which combines two exceed policies into one. + * The aim is avoid this switch inside the relayout code. + * + * i.e. Width policy = Split. Height policy = Original. Internally the policy is SplitOriginal. + */ + void CombineExceedPolicies(); + + /** + * Retrieves the text-view's root actor which stores all text-actors. + * It could be the text-view itself or an actor used in the snapshot mode. + * + * @return the root actor. + */ + Actor GetRootActor() const; + + /** + * Handles SetProperty for multiline policy. + * @param[in] propertyValue The new property value. + */ + void OnMultilinePolicyPropertySet( Property::Value propertyValue ); + + /** + * Handles SetProperty for width exceed policy. + * @param[in] propertyValue The new property value. + */ + void OnWidthExceedPolicyPropertySet( Property::Value propertyValue ); + + /** + * Handles SetProperty for height exceed policy. + * @param[in] propertyValue The new property value. + */ + void OnHeightExceedPolicyPropertySet( Property::Value propertyValue ); + + /** + * Handles SetProperty for line justification. + * @param[in] propertyValue The new property value. + */ + void OnLineJustificationPropertySet( Property::Value propertyValue ); + + /** + * Handles SetProperty for fade boundary. + * @param[in] propertyIndex The property index. + * @param[in] propertyValue The new property value. + */ + void OnFadeBoundaryPropertySet( Property::Index propertyIndex, Property::Value propertyValue ); + + /** + * Handles SetProperty for alignment property. + * @param[in] propertyIndex The property index. + * @param[in] propertyValue The new property value. + */ + void OnAlignmentPropertySet( Property::Index propertyIndex, Property::Value propertyValue ); + +public: + + /** + * The parameters which affects the layout of the text. + */ + struct LayoutParameters + { + /** + * Default constructor. + */ + LayoutParameters(); + + /** + * Constructor + */ + LayoutParameters( Toolkit::TextView::MultilinePolicy multilinePolicy, + Toolkit::TextView::ExceedPolicy widthExceedPolicy, + Toolkit::TextView::ExceedPolicy heightExceedPolicy, + Toolkit::Alignment::Type alignment, + Toolkit::TextView::LineJustification lineJustification, + float lineHeightOffset, + const std::string& ellipsizeText ); + + /** + * Copy constructor + */ + LayoutParameters( const LayoutParameters& layoutParameters ); + + /** + * Assignment operator. + */ + LayoutParameters& operator=( const LayoutParameters& layoutParameters ); + + Toolkit::TextView::MultilinePolicy mMultilinePolicy; ///< Stores the multiline policy. + TextView::ExceedPolicy mExceedPolicy; ///< Stores a combination of both policies; + Toolkit::TextView::ExceedPolicy mWidthExceedPolicy; ///< Stores the text width exceed policy. + Toolkit::TextView::ExceedPolicy mHeightExceedPolicy; ///< Stores the text height exceed policy. + Toolkit::Alignment::Type mHorizontalAlignment; ///< Stores the horizontal alignment for the whole text. + Toolkit::Alignment::Type mVerticalAlignment; ///< Stores the vertical alignment for the whole text. + Toolkit::TextView::LineJustification mLineJustification; ///< Stores the line justification. + float mLineHeightOffset; ///< Line height offset to be addded to the font line height (measured in PointSize). + MarkupProcessor::StyledTextArray mEllipsizeText; ///< Stores the ellipsize text. + }; + + /** + * Some parameters which affects the text view visualization. + */ + struct VisualParameters + { + /** + * Default constructor. + */ + VisualParameters(); + + /** + * Copy constructor. + */ + VisualParameters( const VisualParameters& visualParameters ); + + /** + * Assignment operator. + */ + VisualParameters& operator=( const VisualParameters& visualParameters ); + + Toolkit::TextView::FadeBoundary mFadeBoundary; ///< Fade boundary used in fade mode. + float mSortModifier; ///< Stores the sort modifier for all text-actors. + Vector2 mCameraScrollPosition; ///< The scroll offset. + bool mSnapshotModeEnabled:1; ///< Whether text-view is rendered offscreen. + bool mScrollEnabled:1; ///< Whether the text scroll is enabled. + bool mScrollPositionTrimmed:1; ///< Whether the last scroll position set was trimmed. + }; + + /** + * Temporary data used to calculate line justification. + */ + struct LineJustificationInfo + { + TextViewProcessor::TextInfoIndices mIndices; ///< Indices to the first character of the new line. + float mLineLength; ///< Length of the line (or portion of line). + }; + + /** + * The results of the relayout process. + */ + struct RelayoutData + { + /** + * Default constructor. + */ + RelayoutData(); + + /** + * Copy constructor + */ + RelayoutData( const RelayoutData& relayoutData ); + + /** + * Assignment operator. + */ + RelayoutData& operator=( const RelayoutData& relayoutData ); + + Size mTextViewSize; ///< The text-view's size used to relaid-out the text. + float mShrinkFactor; ///< Shrink factor used when the exceed policy contains ShrinkToFit. + TextViewProcessor::TextLayoutInfo mTextLayoutInfo; ///< Stores metrics, layout info (size, direction, type of word) and text-actor info for the whole text. + std::vector mCharacterLogicalToVisualMap; ///< Reorder map that stores each character's visual (output) index according to its logical (input) index + std::vector mCharacterVisualToLogicalMap; ///< Reorder map that stores each character's logical (input) index according to its visual (output) index + std::vector mTextActors; ///< Stores handles of those text-actors which are currently added to the text-view. + std::vector mEllipsizedTextActors; ///< Stores handles of those text-actors which are used to ellipsize the text. + Toolkit::TextView::CharacterLayoutInfoContainer mCharacterLayoutInfoTable; ///< Stores layout info per character sorted by the character's visual index. + Toolkit::TextView::LineLayoutInfoContainer mLines; ///< Stores an index to the first character of each line. + Size mTextSizeForRelayoutOption; ///< Stores the text size after relayout. + std::vector mLineJustificationInfo; ///< Stores justification info per line. + TextActorCache mTextActorCache; ///< Stores previously created text-actors to be reused. + }; + +private: + + MarkupProcessor::StyledTextArray mCurrentStyledText; ///< text currently displayed by the view + std::vector mTextViewProcessorOperations; ///< Stores all relayout operations which arrive between two consecutive OnRelaidOut() calls. + + LayoutParameters mLayoutParameters; ///< Stores some layout parameters in a struct. To be passed in layout functions. + VisualParameters mVisualParameters; ///< Some parameters which afects text-view visualization. + RelayoutData mRelayoutData; ///< struct with text-view's data structures used to pass all of them in one parameter. + RelayoutOperationMask mRelayoutOperations; ///< Which relayout operations have to be done. + + Layer mOffscreenRootActor; ///< Root actor for offscreen rendering. + ImageActor mOffscreenImageActor; ///< Image actor for offscreen rendering. + CameraActor mOffscreenCameraActor; ///< Camera actor for offscreen rendering. + Size mCurrentOffscreenSize; ///< Current used ofscreen size. + FrameBufferImage mFrameBufferImage; ///< Frame buffer used for offscreen rendering. + RenderTask mRenderTask; ///< Used to generate an offscreen rendering. + + PanGestureDetector mPanGestureDetector; ///< Pan gesture for text scrolling. + + /** + * Helper class used to prevent the modification of some members. + */ + struct Lock + { + Lock( bool& lock ) + : mLock( lock ) + { + mLock = true; + } + + ~Lock() + { + mLock = false; + } + + bool& mLock; + }; + + bool mLockPreviousSnapshotMode; ///< Whether previous stored snapshot mode should be modified. + bool mPreviousSnapshotModeEnabled:1; ///< Stores the previous snapshot mode value. + + Toolkit::TextView::ScrolledSignalV2 mScrolledSignalV2; ///< Signal emitted when text is scrolled. + + Property::Index mPropertyText; ///< Property index for text. + Property::Index mPropertyMultilinePolicy; ///< Property index for multiline policy. + Property::Index mPropertyWidthExceedPolicy; ///< Property index for width exceed policy. + Property::Index mPropertyHeightExceedPolicy; ///< Property index for height exceed policy. + Property::Index mPropertyLineJustification; ///< Property index for line justification policy. + Property::Index mPropertyFadeBoundaryLeft; ///< Property index for Left fade boundary. + Property::Index mPropertyFadeBoundaryRight; ///< Property index for Right fade boundary. + Property::Index mPropertyFadeBoundaryTop; ///< Property index for Top fade boundary. + Property::Index mPropertyFadeBoundaryBottom; ///< Property index for Bottom fade boundary. + Property::Index mPropertyLineHeightOffset; ///< Property index for Line height offset. + Property::Index mPropertyHorizontalAlignment; ///< Property index for Horizontal alignment. + Property::Index mPropertyVerticalAlignment; ///< Property index for Vertical alignment. +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::TextView& GetImpl( TextView& textView ) +{ + DALI_ASSERT_ALWAYS( textView ); + + RefObject& handle = textView.GetImplementation(); + + return static_cast< Internal::TextView& >( handle ); +} + +inline const Internal::TextView& GetImpl( const TextView& textView ) +{ + DALI_ASSERT_ALWAYS( textView ); + + const RefObject& handle = textView.GetImplementation(); + + return static_cast< const Internal::TextView& >( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_ITEM_VIEW_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-line-processor.cpp b/dali-toolkit/internal/controls/text-view/text-view-line-processor.cpp new file mode 100644 index 0000000..60ad206 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-line-processor.cpp @@ -0,0 +1,427 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-line-processor.h" +#include "text-view-word-group-processor.h" +#include "text-view-word-processor.h" +#include "text-view-processor-helper-functions.h" +#include "text-processor.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +///////////////////// +// Layout info. +///////////////////// + +LineLayoutInfo::LineLayoutInfo() +: mSize(), + mAscender( 0.f ), + mLineHeightOffset( 0.f ), + mWordGroupsLayoutInfo(), + mNumberOfCharacters( 0 ) +{ +} + +LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line ) +: mSize( line.mSize ), + mAscender( line.mAscender ), + mLineHeightOffset( line.mLineHeightOffset ), + mWordGroupsLayoutInfo( line.mWordGroupsLayoutInfo ), + mNumberOfCharacters( line.mNumberOfCharacters ) +{ +} + +LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line ) +{ + mSize = line.mSize; + mAscender = line.mAscender; + mLineHeightOffset = line.mLineHeightOffset; + mWordGroupsLayoutInfo = line.mWordGroupsLayoutInfo; + mNumberOfCharacters = line.mNumberOfCharacters; + + return *this; +} + +void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset ) +{ + lineLayoutInfo.mSize = Size(); + + for( WordGroupLayoutInfoContainer::iterator it = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordGroupLayoutInfo& layoutInfo( *it ); + + UpdateSize( lineLayoutInfo.mSize, layoutInfo.mSize ); + } + lineLayoutInfo.mSize.height += lineHeightOffset; +} + +void CreateLineInfo( const MarkupProcessor::StyledTextArray& line, + TextView::RelayoutData& relayoutData, + TextViewProcessor::LineLayoutInfo& lineLayoutInfo ) +{ + // TODO: Split the line in group of words. Each group of words has only left to right characters or right to left characters but not a mix of both. + // TODO: set the wordgroup direction (LTR or RTL) + std::vector wordGroups; + if( TextProcessor::ContainsRightToLeftCharacter( line ) ) + { + // If the text is bidirectional, the characters will be converted and reordered + // as specified by the Unicode Bidirectional Algorithm. + + // Reorders the line and converts arabic glyphs (if any). + // It also split words in different groups if there are a mix of left to right + // and right to left text. + // If the whole line is left to right or right to left all words are grouped in the same group. + TextProcessor::ConvertBidirectionalText( line, + wordGroups, + relayoutData.mCharacterLogicalToVisualMap, + relayoutData.mCharacterVisualToLogicalMap); + } + else + { + // No bidirectional text to process. + + if( !line.empty() ) + { + // Add all words in a group. + wordGroups.push_back( line ); + + // Create trivial bidirectional map tables. + std::size_t index = 0; + for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it ) + { + const MarkupProcessor::StyledText& styledText( *it ); + + for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i ) + { + relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index ); + relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index ); + ++index; + } + } + } + } + + // Traverse all group of words. + for( std::vector::const_iterator groupIt = wordGroups.begin(), groupEndIt = wordGroups.end(); groupIt != groupEndIt; ++groupIt ) + { + const MarkupProcessor::StyledTextArray& wordGroup( *groupIt ); + + // Data structures for the new group of words. + WordGroupLayoutInfo wordGroupLayoutInfo; + + CreateWordGroupInfo( wordGroup, + relayoutData.mTextLayoutInfo, + wordGroupLayoutInfo ); + + // Update layout info for the current line. + lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordGroupLayoutInfo.mAscender ); + lineLayoutInfo.mNumberOfCharacters += wordGroupLayoutInfo.mNumberOfCharacters; + UpdateSize( lineLayoutInfo.mSize, wordGroupLayoutInfo.mSize ); + + // Add the group of words to the current line. + lineLayoutInfo.mWordGroupsLayoutInfo.push_back( wordGroupLayoutInfo ); + } // end of group of words +} + +void RemoveWordGroupsFromLine( const std::size_t groupIndex, + const std::size_t numberOfGroups, + const PointSize& lineHeightOffset, + LineLayoutInfo& lineLayout ) +{ + // Removes groups of words from a line. + + // * Check if words or lines can be merged after removing a group of words or a line separator have to be done outside this method. + + // * Note: Currently it's only used to remove a number of groups of words from the beginning, or + // from groupIndex index to the end. This function doesn't merge groups of words (if a whole group is removed) + // TODO: merge groups of words if required. + + const std::size_t groupEndIndex = groupIndex + numberOfGroups; + + // Remove word groups from layout info. + lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + groupIndex, + lineLayout.mWordGroupsLayoutInfo.begin() + groupEndIndex ); + + // Update layout info. + lineLayout.mSize = Size(); + lineLayout.mAscender = 0.f; + lineLayout.mNumberOfCharacters = 0; + for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end(); + it != endIt; + ++it ) + { + const WordGroupLayoutInfo& group( *it ); + + UpdateSize( lineLayout.mSize, group.mSize ); + lineLayout.mAscender = std::max( lineLayout.mAscender, group.mAscender ); + lineLayout.mNumberOfCharacters += group.mNumberOfCharacters; + } + lineLayout.mSize.height += lineHeightOffset; + lineLayout.mLineHeightOffset = lineHeightOffset; +} + +void SplitLine( const TextInfoIndices& indices, + const PointSize& lineHeightOffset, + LineLayoutInfo& firstLineLayoutInfo, + LineLayoutInfo& lastLineLayoutInfo ) +{ + // Splits a line in two. + // A group of words and a word may be split in two as well. + + // * Split the group of words within the line. + // * Add last part of the group of words to the new line. + // * Add groups of words from groupPosition + 1 to the end. + // * Update layout info of the last line. + // * Remove groups of words added to the last part of the line from the first line. + + // early returns!! + if( ( 0 == indices.mGroupIndex ) && ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) ) + { + // the whole line goes to the last part. + lastLineLayoutInfo = firstLineLayoutInfo; + + firstLineLayoutInfo = LineLayoutInfo(); + + return; + } + + if( !firstLineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + const std::size_t numberOfGroups = firstLineLayoutInfo.mWordGroupsLayoutInfo.size(); + if( indices.mGroupIndex == numberOfGroups - 1 ) + { + const WordGroupLayoutInfo& group( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) ); + + const std::size_t numberOfWords = group.mWordsLayoutInfo.size(); + if( indices.mWordIndex == numberOfWords - 1 ) + { + const WordLayoutInfo& word( *( group.mWordsLayoutInfo.end() - 1 ) ); + if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() ) + { + // the whole line goes to the first part. + + // Just delete whatever there is in the last part of the line. + lastLineLayoutInfo = LineLayoutInfo(); + + return; + } + } + } + } + + lastLineLayoutInfo = LineLayoutInfo(); + + // 1) Split the group of words whitin the line. + WordGroupLayoutInfo& firstWordGroupLayoutInfo( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex ) ); + WordGroupLayoutInfo lastWordGroupLayoutInfo; + + SplitWordGroup( indices, + firstWordGroupLayoutInfo, + lastWordGroupLayoutInfo ); + + // 2) Add last part of the group of words to the new line. + if( !lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + lastLineLayoutInfo.mWordGroupsLayoutInfo.push_back( lastWordGroupLayoutInfo ); + } + + // 3) Add groups from group-position + 1 to the end. + lastLineLayoutInfo.mWordGroupsLayoutInfo.insert( lastLineLayoutInfo.mWordGroupsLayoutInfo.end(), + firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex + 1, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() ); + + // 4) update layout info of the last line. + for( WordGroupLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordGroupsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordGroupLayoutInfo& layoutInfo( *it ); + + lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters; + UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize ); + lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender ); + } + lastLineLayoutInfo.mSize.height += lineHeightOffset; + lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset; + + // 5) Remove groups of words added to the last part of the line from the first line. + + // if the number of characters of the last group of words of the first line is zero, it should be removed. + const std::size_t index = ( 0 == firstWordGroupLayoutInfo.mNumberOfCharacters ? indices.mGroupIndex : indices.mGroupIndex + 1 ); + + firstLineLayoutInfo.mWordGroupsLayoutInfo.erase( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() ); + + // 6) update layout info of the first line. + firstLineLayoutInfo.mNumberOfCharacters = 0; + firstLineLayoutInfo.mSize = Size(); + firstLineLayoutInfo.mAscender = 0.f; + for( WordGroupLayoutInfoContainer::iterator it = firstLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = firstLineLayoutInfo.mWordGroupsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordGroupLayoutInfo& layoutInfo( *it ); + + firstLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters; + UpdateSize( firstLineLayoutInfo.mSize, layoutInfo.mSize ); + firstLineLayoutInfo.mAscender = std::max( firstLineLayoutInfo.mAscender, layoutInfo.mAscender ); + } + firstLineLayoutInfo.mSize.height += lineHeightOffset; + firstLineLayoutInfo.mLineHeightOffset = lineHeightOffset; +} + +void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo, + const LineLayoutInfo& lastLineLayoutInfo ) +{ + // Merges two given lines. + // + // Can't merge two lines if the last word of the first one is a line separator (new line character) + + // Early returns. + + if( lastLineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + // Nothing to merge if last line is empty. + return; + } + + if( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + // If first line is empty, just copy the last line to the first one. + firstLineLineLayoutInfo = lastLineLayoutInfo; + + return; + } + + if( 1 == firstLineLineLayoutInfo.mWordGroupsLayoutInfo.size() ) + { + WordGroupLayoutInfo& wordGroupLayout( *firstLineLineLayoutInfo.mWordGroupsLayoutInfo.begin() ); + + if( wordGroupLayout.mWordsLayoutInfo.empty() ) + { + // If first line is empty, just copy the last line to the first one. + firstLineLineLayoutInfo = lastLineLayoutInfo; + + return; + } + } + + // Check the last word of the last group of the first line doesn't finish with a new line character. + WordGroupLayoutInfo& lastWordGroupLayout( *( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) ); + WordLayoutInfo& lastWordLayout( *( lastWordGroupLayout.mWordsLayoutInfo.end() - 1 ) ); + if( LineSeparator == lastWordLayout.mType ) + { + DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." ); + } + + // If the last group of the first line has the same direction than the first group of the last line, both lines can be concatenated. + // Otherwise, both groups have to be merged first. + const WordGroupLayoutInfo& firstWordGroupLayout( *lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() ); + + std::size_t index = 0; + if( lastWordGroupLayout.mDirection == firstWordGroupLayout.mDirection ) + { + // Both groups of words have the same direction. They need to be merged. + MergeWordGroup( lastWordGroupLayout, + firstWordGroupLayout ); + + // After merging two groups of words, the rest of groups need to be added. + ++index; // By increasing this index the group of words already merged won't be added again. + } + + // Merge layout info + firstLineLineLayoutInfo.mWordGroupsLayoutInfo.insert( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end(), + lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordGroupsLayoutInfo.end() ); + UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize ); + firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender ); + firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset ); + firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters; + +} + +WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo ) +{ + WordLayoutInfo layoutInfo; + + if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + const WordGroupLayoutInfo& groupInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) ); + + if( !groupInfo.mWordsLayoutInfo.empty() ) + { + layoutInfo = *( groupInfo.mWordsLayoutInfo.end() - 1 ); + } + } + + return layoutInfo; +} + +CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo ) +{ + CharacterLayoutInfo layoutInfo; + + if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + const WordGroupLayoutInfo& groupInfo( *lineLayoutInfo.mWordGroupsLayoutInfo.begin() ); + + if( !groupInfo.mWordsLayoutInfo.empty() ) + { + const WordLayoutInfo& wordInfo( *groupInfo.mWordsLayoutInfo.begin() ); + + layoutInfo = GetFirstCharacterLayoutInfo( wordInfo ); + } + } + + return layoutInfo; +} + +CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo ) +{ + const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( lineLayoutInfo ); + + return GetLastCharacterLayoutInfo( wordInfo ); +} + +void CollectTextActorsFromLines( std::vector& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t lineIndexBegin, const std::size_t lineIndexEnd ) +{ + for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexBegin, lineEndIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexEnd; + lineIt != lineEndIt; + ++lineIt ) + { + const LineLayoutInfo& line( *lineIt ); + + CollectTextActorsFromGroups( textActors, line, 0, line.mWordGroupsLayoutInfo.size() ); + } +} + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-line-processor.h b/dali-toolkit/internal/controls/text-view/text-view-line-processor.h new file mode 100644 index 0000000..975efb5 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-line-processor.h @@ -0,0 +1,144 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_LINE_PROCESSOR_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_LINE_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Updates the line layout size info. + * + * @param[in,out] lineLayoutInfo The line layout info. + * @param[in] lineHeightOffset The line height offset. + */ +void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, float lineHeightOffset ); + +/** + * Creates a data structure with info to layout the line, and data structures with useful info to modify the layout data structure if characters are added or removed. + * + * @param[in] line The styled line. + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + * @param[out] lineLayoutInfo Layout info for the whole line. + */ +void CreateLineInfo( const MarkupProcessor::StyledTextArray& line, + TextView::RelayoutData& relayoutData, + TextViewProcessor::LineLayoutInfo& lineLayoutInfo ); + +/** + * Removes a given number of groups of words from the given line. + * + * @pre \e groupIndex and \e groupIndex + \e numberOfGroups can't exceed the bounds of the line. + * + * @param[in] groupIndex Index to the group of words within the line with the starting position to be deleted. + * @param[in] numberOfGroups The number of group of words to be deleted. + * @param[in] lineHeightOffset Additional space between lines. Needed to update layout info. + * @param[in,out] lineLayout The input is the layout info of the line. The output is the layout info of the line without the removed group of words. + */ +void RemoveWordGroupsFromLine( std::size_t groupIndex, + std::size_t numberOfGroups, + const PointSize& lineHeightOffset, + LineLayoutInfo& lineLayout ); + +/** + * Splits a given line in two. + * + * @note It deletes whatever there is in the last part of the line. + * + * @param[in] indices Index to the group of words within the line, index to the word and index to the character within the word to split the line. + * @param[in] lineHeightOffset Additional space between lines. Needed to update layout info. + * @param[in,out] firstLineLayoutInfo The input is the layout info of the given line. The output is the first part of the input line (from the group of words \e 0 to the group of words \e groupPosition). + * @param[in,out] lastLineLayoutInfo Layout info of the last part of the given line ( from the group of words \e groupPosition + \e 1 to the end of the line). + */ +void SplitLine( const TextInfoIndices& indices, + const PointSize& lineHeightOffset, + LineLayoutInfo& firstLineLayoutInfo, + LineLayoutInfo& lastLineLayoutInfo ); + +/** + * Merges the two given lines by adding groups of words of the last line to the firs one. + * + * @note Does nothing if last part of the line is empty. + * @note If the first part of the line is empty it just copy the last part to it. + * @note it asserts if the last word of the first group is a line separator (new line character) + * + * @param[in,out] firstLineLineLayoutInfo The input is the layout info of the first line. The output is the layout info of the merged line. + * @param[in] lastLineLayoutInfo Layout info of the last line. + * + */ +void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo, + const LineLayoutInfo& lastLineLayoutInfo ); + +/** + * Retrieves the layout information of the last word of the given line. + * + * @param[in] lineLayoutInfo The line layout. + * + * @return Layout information of the last word of the line. + */ +WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo ); + +/** + * Retrieves the layout information of the first character of the given line. + * + * @param[in] lineLayoutInfo The line layout. + * + * @return Layout information of the first character of the line. + */ +CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo ); + +/** + * Retrieves the layout information of the last character of the given line. + * + * @param[in] lineLayoutInfo The line layout. + * + * @return Layout information of the last character of the line. + */ +CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo ); + +/** + * Collects text-actors from the given lines and stores them into the text-actor vector. + * + * @param[out] textActors Stores the text-actors of the given lines. + * @param[in] textLayoutInfo Whole text with the given lines. + * @param[in] lineIndexBegin The first line. + * @param[in] lineIndexEnd The last line. + */ +void CollectTextActorsFromLines( std::vector& textActors, const TextLayoutInfo& textLayoutInfo, std::size_t lineIndexBegin, std::size_t lineIndexEnd ); + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_LINE_PROCESSOR_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.cpp b/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.cpp new file mode 100644 index 0000000..cda3846 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.cpp @@ -0,0 +1,172 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-processor-dbg.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +void dbgPrint( const WordLayoutInfo& word ) +{ + for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), endCharacterIt = word.mCharactersLayoutInfo.end(); + characterIt != endCharacterIt; + ++characterIt ) + { + const CharacterLayoutInfo& character( *characterIt ); + + std::cout << "[" << character.mSize << std::endl; + std::cout << " ascender " << character.mAscender << std::endl; + + if( character.mTextActor ) + { + std::cout << "[" << character.mTextActor.GetText() << "]"; + } + std::cout << "{" << character.mStyledText.mText.GetText() << "}"; + } + std::cout << " size " << word.mSize << std::endl; + std::cout << " ascender " << word.mAscender << std::endl; + std::cout << " num char " << word.mCharactersLayoutInfo.size() << std::endl; + std::cout << " type "; + switch( word.mType ) + { + case NoSeparator: + { + std::cout << "NoSeparator" << std::endl; + break; + } + case LineSeparator: + { + std::cout << "LineSeparator" << std::endl; + break; + } + case WordSeparator: + { + std::cout << "WordSeparator" << std::endl; + break; + } + } +} + +void dbgPrint( const WordGroupLayoutInfo& group ) +{ + std::cout << "( "; + std::cout << group.mSize; + std::cout << group.mNumberOfCharacters; + for( WordLayoutInfoContainer::const_iterator wordIt = group.mWordsLayoutInfo.begin(), endWordIt = group.mWordsLayoutInfo.end(); + wordIt != endWordIt; + ++wordIt ) + { + dbgPrint( *wordIt ); + } + std::cout << " )"; +} + +void dbgPrint( const LineLayoutInfo& line ) +{ + std::cout << "< "; + std::cout << line.mSize; + for( WordGroupLayoutInfoContainer::const_iterator groupIt = line.mWordGroupsLayoutInfo.begin(), endGroupIt = line.mWordGroupsLayoutInfo.end(); + groupIt != endGroupIt; + ++groupIt ) + { + dbgPrint( *groupIt ); + } + std::cout << " >"; + std::cout << std::endl; +} + +void dbgPrint( const TextLayoutInfo& textInfo ) +{ + std::cout << "||" << std::endl; + std::cout << textInfo.mWholeTextSize; + for( LineLayoutInfoContainer::const_iterator it = textInfo.mLinesLayoutInfo.begin(), endIt = textInfo.mLinesLayoutInfo.end(); + it != endIt; + ++it ) + { + dbgPrint( *it ); + } + std::cout << "||" << std::endl; +} + +void dbgPrint( const TextStyle& style ) +{ + std::cout << " font name : " << style.GetFontName() << std::endl; + std::cout << " font style : " << style.GetFontStyle() << std::endl; + std::cout << " font point size : " << style.GetFontPointSize() << std::endl; + std::cout << " weight : " << style.GetWeight() << std::endl; + std::cout << " text color : " << style.GetTextColor() << std::endl; + std::cout << " italics : " << style.GetItalics() << std::endl; + std::cout << " underline : " << style.GetUnderline() << std::endl; + std::cout << " shadow : " << style.GetShadow() << std::endl; + std::cout << " shadow color : " << style.GetShadowColor() << std::endl; + std::cout << " shadow offset : " << style.GetShadowOffset() << std::endl; + std::cout << " glow : " << style.GetGlow() << std::endl; + std::cout << " italics angle : " << style.GetItalicsAngle() << std::endl; + std::cout << " glow color : " << style.GetGlowColor() << std::endl; + std::cout << " glow intensity : " << style.GetGlowIntensity() << std::endl; + std::cout << " smooth edge : " << style.GetSmoothEdge() << std::endl; + std::cout << " outline : " << style.GetOutline() << std::endl; + std::cout << " outline color : " << style.GetOutlineColor() << std::endl; + std::cout << " outline thickness : " << style.GetOutlineThickness() << std::endl; +} + +void dbgPrint( const TextInfoIndices& indices ) +{ + std::cout << " line : " << indices.mLineIndex << std::endl; + std::cout << " group : " << indices.mGroupIndex << std::endl; + std::cout << " word : " << indices.mWordIndex << std::endl; + std::cout << " char : " << indices.mCharacterIndex << std::endl; +} + +void dbgPrint( const MarkupProcessor::StyledTextArray& textArray ) +{ + for( MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it ) + { + const MarkupProcessor::StyledText& text( *it ); + + std::cout << text.mText.GetText(); + } +} + +void dbgPrintText( const WordLayoutInfo& word ) +{ + for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), endCharacterIt = word.mCharactersLayoutInfo.end(); + characterIt != endCharacterIt; + ++characterIt ) + { + const CharacterLayoutInfo& character( *characterIt ); + + std::cout << character.mStyledText.mText.GetText(); + } +} + +} // namespace TextViewProcessor + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h b/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h new file mode 100644 index 0000000..dc1e210 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h @@ -0,0 +1,53 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_DBG_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_DBG_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +void dbgPrint( const WordLayoutInfo& word ); +void dbgPrint( const WordGroupLayoutInfo& group ); +void dbgPrint( const LineLayoutInfo& line ); +void dbgPrint( const TextLayoutInfo& textInfo ); +void dbgPrint( const TextStyle& style ); +void dbgPrint( const TextInfoIndices& indices ); +void dbgPrint( const MarkupProcessor::StyledTextArray& text ); + +void dbgPrintText( const WordLayoutInfo& word ); + +} // namespace TextViewProcessor + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_DBG_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.cpp b/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.cpp new file mode 100644 index 0000000..f09955d --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.cpp @@ -0,0 +1,196 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-processor-helper-functions.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +void UpdateSize( Size& size1, const Size& size2, const SizeGrowType type ) +{ + switch( type ) + { + case GrowWidth: + { + size1.width += size2.width; + size1.height = std::max( size1.height, size2.height ); + break; + } + case GrowHeight: + { + size1.width = std::max( size1.width, size2.width ); + size1.height += size2.height; + break; + } + } +} + +TextSeparatorType GetTextSeparatorType( const Character& character ) +{ + // returns if the given character is a line separator '\n', a word separator ' ' or if is not a separator (any other character). + return ( character.IsNewLine() ? LineSeparator : ( character.IsWhiteSpace() ? WordSeparator : NoSeparator ) ); +} + +void ChooseFontFamilyName( MarkupProcessor::StyledText& text ) +{ + bool userDefinedFontFamilyName = false; + + // First check if there is a font defined in the style and it supports the given text. + if( !text.mStyle.GetFontName().empty() ) + { + const FontParameters fontParams( text.mStyle.GetFontName(), text.mStyle.GetFontStyle() , text.mStyle.GetFontPointSize() ); + const Font font = Font::New( fontParams ); + + if( !font.IsDefaultSystemFont() && font.AllGlyphsSupported( text.mText ) ) + { + userDefinedFontFamilyName = true; + } + } + + if( !userDefinedFontFamilyName ) + { + const Font defaultSystemFont = Font::New(); + + // At this point no font is set or doesn't support the given text. + if( !defaultSystemFont.AllGlyphsSupported( text.mText ) ) + { + // If the default system font doesn't support the given text, + // an appropiate font is selected. + text.mStyle.SetFontName( Font::GetFamilyForText( text.mText ) ); + // @TODO Font::GetFamilyForText() should return font family and font style. + } + else + { + // All characters are supported with default font, so use it + text.mStyle.SetFontName( "" ); + } + } +} + +void GetIndicesFromGlobalCharacterIndex( const std::size_t index, + const TextLayoutInfo& textLayoutInfo, + TextInfoIndices& indices ) +{ + // TODO : Check for mixed LTR and RTL. + + // clear all indices + indices = TextInfoIndices(); + + // Early return. + if( textLayoutInfo.mLinesLayoutInfo.empty() ) + { + // Text is empty. All indices are 0. + return; + } + + std::size_t currentIndex = 0; // stores how many characters have been traversed. + + // Traverse all lines, groups of words and words until global index is found. + bool found = false; + for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), + lineEndIt = textLayoutInfo.mLinesLayoutInfo.end(); + ( !found ) && ( lineIt != lineEndIt ); + ++lineIt, ++indices.mLineIndex ) + { + const LineLayoutInfo& lineLayoutInfo( *lineIt ); + + if( currentIndex + lineLayoutInfo.mNumberOfCharacters > index ) + { + // The character is in this line + for( WordGroupLayoutInfoContainer::const_iterator groupIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), + groupEndIt = lineLayoutInfo.mWordGroupsLayoutInfo.end(); + ( !found ) && ( groupIt != groupEndIt ); + ++groupIt, ++indices.mGroupIndex ) + { + const WordGroupLayoutInfo& wordGroupLayoutInfo( *groupIt ); + + if( currentIndex + wordGroupLayoutInfo.mNumberOfCharacters > index ) + { + // The character is in this group of words. + for( WordLayoutInfoContainer::const_iterator wordIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), + wordEndIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + ( !found ) && ( wordIt != wordEndIt ); + ++wordIt, ++indices.mWordIndex ) + { + const WordLayoutInfo& wordLayoutInfo( *wordIt ); + + if( currentIndex + wordLayoutInfo.mCharactersLayoutInfo.size() > index ) + { + // The character is in this word + indices.mCharacterIndex = index - currentIndex; + found = true; + } + else + { + // check in the next word. + currentIndex += wordLayoutInfo.mCharactersLayoutInfo.size(); + } + } // end words. + if( !wordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + --indices.mWordIndex; + } + } + else + { + // check in the next group of words + currentIndex += wordGroupLayoutInfo.mNumberOfCharacters; + } + } // end groups of words. + if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + --indices.mGroupIndex; + } + } + else + { + // check in the next line + currentIndex += lineLayoutInfo.mNumberOfCharacters; + } + } // end lines. + + // Need to decrease indices as they have been increased in the last loop. + if( !textLayoutInfo.mLinesLayoutInfo.empty() ) + { + --indices.mLineIndex; + } +} + +void ClearText( std::vector& textActors ) +{ + for( std::vector::iterator it = textActors.begin(), endIt = textActors.end(); it != endIt; ++it ) + { + (*it).SetText( std::string( "" ) ); + } +} + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h b/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h new file mode 100644 index 0000000..1798758 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_HELPER_FUNCTIONS_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_HELPER_FUNCTIONS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Enum type used to update a size adding other one. + * If the grow type is \e GrowWidth, size's widths are added and size's height is the max of both. + * Alternativelly if grow type is \e GrowHeight heights are added and width is the max of both. + */ +enum SizeGrowType +{ + GrowWidth, + GrowHeight, +}; + +/** + * Updates the size of a group of characters, word, group of words, line or the whole text with a given size. + * + * For group of characters, words, groups of words and lines the new height will be the maximum of both sizes + * and the new width will be the sum of both. + * + * For the whole text, the new height will be the sum of both and the new width the max of both. + * + * The \e type parameter speficies which type of update is required. + * + * @param[in,out] size1 The current size to be updated. + * @param[in] size2 The size of a group of characters, word, group of words or a line. + * @param[in] type Type of update. + */ +void UpdateSize( Size& size1, const Size& size2, SizeGrowType type = GrowWidth ); + +/** + * Return the type of separator (white space, new line or no separator) for the given character. + * + * @param[in] character The given character. + * + * @return The type of separator. + */ +TextSeparatorType GetTextSeparatorType( const Character& character ); + +/** + * Choose a suitable font family name for the given styled text. + * + * It may modify the text-style of the given text by setting a suitable font-family. + * + * @param[in,out] text Text with style. + */ +void ChooseFontFamilyName( MarkupProcessor::StyledText& text ); + +/** + * Retrieves the line, word group, word and character indices for the given global character's index. + * + * i.e. The retrieved indices for the character 18 (j) for the following text would be: 2, 0, 2, 0 + * + * a b c d + * e f g h + * i j k l + * m n o p + * + * @param[in] index The global character index. + * @param[in] textLayoutInfo Contains info about the number of characters per word, group and line. + * @param[out] indices Index to the line, group of words, word and character within the word where the given character is located. + */ +void GetIndicesFromGlobalCharacterIndex( std::size_t index, + const TextLayoutInfo& textLayoutInfo, + TextInfoIndices& indices ); + +/** + * Clear the text of the given text-actors. + * + * @param[in] textActors The given text-actors. + */ +void ClearText( std::vector& textActors ); + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_HELPER_FUNCTIONS_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor-types.h b/dali-toolkit/internal/controls/text-view/text-view-processor-types.h new file mode 100644 index 0000000..8c9d60a --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor-types.h @@ -0,0 +1,273 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_TYPES_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_TYPES_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Whether the direction is Right To Left or Left To Right. + */ +enum Direction +{ + LTR, ///< Left To Right direction. + RTL ///< Right To Left direction. +}; + +/** + * Whether the text is a new line character, a white space or normal text. + */ +enum TextSeparatorType +{ + LineSeparator, + WordSeparator, + NoSeparator +}; + +/** + * Whether to clear the text of the text-actors when text is removed. + */ +enum TextOperationOnRemove +{ + CLEAR_TEXT, + KEEP_TEXT +}; + + +/** + * Stores text info indices. + */ +struct TextInfoIndices +{ + /** + * Default constructor. + */ + TextInfoIndices(); + + /** + * Constructor. + */ + TextInfoIndices( std::size_t lineIndex, + std::size_t groupIndex, + std::size_t wordIndex, + std::size_t characterIndex ); + + /** + * Equality operator. + * @param [in] indices The text-info indices to be compared. + * + * @return \e true if both indices are equal. + */ + bool operator==( const TextInfoIndices& indices ) const; + + std::size_t mLineIndex; + std::size_t mGroupIndex; + std::size_t mWordIndex; + std::size_t mCharacterIndex; +}; + +/** + * Layout information for a character. + * It stores the position, size and ascender of its respective text-actor. + */ +struct CharacterLayoutInfo +{ + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + CharacterLayoutInfo(); + + /** + * Copy constructor. + */ + CharacterLayoutInfo( const CharacterLayoutInfo& character ); + + /** + * Assignment operator. + */ + CharacterLayoutInfo& operator=( const CharacterLayoutInfo& character ); + + // Natural size (metrics) of the glyph. + float mHeight; ///< Natural height of the character. + float mAdvance; ///< Natural horizontal distance from origin of current character and the next one. + float mBearing; ///< Natural vertical distance from the baseline to the top of the glyph's bbox. + + // Size of the text-actor (may be modified by a scale factor). + Vector3 mPosition; ///< Position within the text-view + Vector2 mOffset; ///< Alignment and justification offset. + Size mSize; ///< Size of this character. + float mAscender; ///< Distance from the base line to the top of the line. + float mUnderlineThickness; ///< The underline's thickness. + float mUnderlinePosition; ///< The underline's position. + + TextActor mTextActor; ///< Handle to a text-actor. + MarkupProcessor::StyledText mStyledText; ///< Stores the text and its style. + float mColorAlpha; ///< Alpha component for the initial text color when text is faded. + Vector4 mGradientColor; ///< Gradient color. + Vector2 mStartPoint; ///< Gradient start point. + Vector2 mEndPoint; ///< Gradient end point. + + bool mIsVisible:1; ///< Whether the text-actor is visible. + bool mSetText:1; ///< Whether a new text needs to be set in the text-actor. + bool mSetStyle:1; ///< Whether a new style needs to be set in the text-actor. +}; +typedef std::vector CharacterLayoutInfoContainer; + +/** + * Layout information for a word. + */ +struct WordLayoutInfo +{ + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + WordLayoutInfo(); + + /** + * Copy constructor. + */ + WordLayoutInfo( const WordLayoutInfo& word ); + + /** + * Assignment operator. + */ + WordLayoutInfo& operator=( const WordLayoutInfo& word ); + + Size mSize; ///< Size of the word. + float mAscender; ///< Max of all ascenders of all characters. + TextSeparatorType mType; ///< Whether this word is a word separator, a line separator or is not a separator. + CharacterLayoutInfoContainer mCharactersLayoutInfo; ///< Layout info for all characters. +}; +typedef std::vector WordLayoutInfoContainer; + +/** + * Layout information for a group of words. + */ +struct WordGroupLayoutInfo +{ + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + WordGroupLayoutInfo(); + + /** + * Copy constructor. + */ + WordGroupLayoutInfo( const WordGroupLayoutInfo& group ); + + /** + * Assignment operator. + */ + WordGroupLayoutInfo& operator=( const WordGroupLayoutInfo& group ); + + Size mSize; ///< Size of the group of words. + float mAscender; ///< Max of all ascenders of all words. + Direction mDirection; ///< Whether this group of words is Right To Left or Left To Right. + WordLayoutInfoContainer mWordsLayoutInfo; ///< Layout info for all words. + std::size_t mNumberOfCharacters; ///< Stores the number of characters. +}; +typedef std::vector WordGroupLayoutInfoContainer; + +/** + * Layout information for a line. + */ +struct LineLayoutInfo +{ + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + LineLayoutInfo(); + + /** + * Copy constructor. + */ + LineLayoutInfo( const LineLayoutInfo& line ); + + /** + * Assignment operator. + */ + LineLayoutInfo& operator=( const LineLayoutInfo& line ); + + Size mSize; ///< Size of the line. + float mAscender; ///< Max of all ascenders of all groups of words. + float mLineHeightOffset; ///< Line height offset. + WordGroupLayoutInfoContainer mWordGroupsLayoutInfo; ///< Layout info for all groups of words. + std::size_t mNumberOfCharacters; ///< Stores the number of characters. +}; +typedef std::vector LineLayoutInfoContainer; + +/** + * Layout information for the whole text. + */ +struct TextLayoutInfo +{ + /** + * Default constructor. + * + * Initializes all members to their default values. + */ + TextLayoutInfo(); + + /** + * Copy constructor. + */ + TextLayoutInfo( const TextLayoutInfo& text ); + + /** + * Assignment operator. + */ + TextLayoutInfo& operator=( const TextLayoutInfo& text ); + + Size mWholeTextSize; ///< width and height of the whole text. + float mMaxWordWidth; ///< maximum width between all words. + LineLayoutInfoContainer mLinesLayoutInfo; ///< Layout information for all lines. + std::size_t mNumberOfCharacters; ///< Stores the number of characters. + float mMaxItalicsOffset; ///< When rendering text-view in offscreen an extra width offset is needed to prevent italic characters to be cut if they are in the right edge. + WordLayoutInfo mEllipsizeLayoutInfo; ///< Layout information for the ellipsize text. +}; + +} // namespace TextViewProcessor + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_TYPES_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor.cpp b/dali-toolkit/internal/controls/text-view/text-view-processor.cpp new file mode 100644 index 0000000..b5800c8 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor.cpp @@ -0,0 +1,1208 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include "text-view-processor.h" +#include "text-view-word-processor.h" +#include "text-view-word-group-processor.h" +#include "text-view-line-processor.h" +#include "text-view-processor-helper-functions.h" +#include "text-processor.h" +#include "text-view-processor-dbg.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +namespace +{ +/** + * Update text layout info. + * + * Updates the size of the whole text, the maximum width of all words and the number of characters. + * + * @param[in,out] textLayoutInfo + */ +void UpdateTextLayoutInfo( TextLayoutInfo& textLayoutInfo ) +{ + // Initialize members to be updated. + textLayoutInfo.mWholeTextSize = Size(); + textLayoutInfo.mMaxWordWidth = 0.f; + textLayoutInfo.mNumberOfCharacters = 0; + + // Traverse all text updating values. + for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + const LineLayoutInfo& line( *lineIt ); + + // Updates text size with the size of all lines. + UpdateSize( textLayoutInfo.mWholeTextSize, line.mSize, GrowHeight ); + + // Updates number of characters. + textLayoutInfo.mNumberOfCharacters += line.mNumberOfCharacters; + + // Updates the max word's width. + for( WordGroupLayoutInfoContainer::const_iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt ) + { + const WordGroupLayoutInfo& group( *groupIt ); + for( WordLayoutInfoContainer::const_iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt ) + { + const WordLayoutInfo& word( *wordIt ); + + textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, word.mSize.width ); + } + } + } +} + +} // namespace + +// Constructors and assignment operators + +TextInfoIndices::TextInfoIndices() +: mLineIndex( 0 ), + mGroupIndex( 0 ), + mWordIndex( 0 ), + mCharacterIndex( 0 ) +{ +} + +TextInfoIndices::TextInfoIndices( const std::size_t lineIndex, + const std::size_t groupIndex, + const std::size_t wordIndex, + const std::size_t characterIndex ) +: mLineIndex( lineIndex ), + mGroupIndex( groupIndex ), + mWordIndex( wordIndex ), + mCharacterIndex( characterIndex ) +{ +} + +bool TextInfoIndices::operator==( const TextInfoIndices& indices ) const +{ + return ( ( mLineIndex == indices.mLineIndex ) && + ( mGroupIndex == indices.mGroupIndex ) && + ( mWordIndex == indices.mWordIndex ) && + ( mCharacterIndex == indices.mCharacterIndex ) ); +} + +///////////////////// +// Layout info. +///////////////////// + +TextLayoutInfo::TextLayoutInfo() +: mWholeTextSize(), + mMaxWordWidth( 0.f ), + mLinesLayoutInfo(), + mNumberOfCharacters( 0 ), + mMaxItalicsOffset( 0.f ), + mEllipsizeLayoutInfo() +{ +} + +TextLayoutInfo::TextLayoutInfo( const TextLayoutInfo& text ) +: mWholeTextSize( text.mWholeTextSize ), + mMaxWordWidth( text.mMaxWordWidth ), + mLinesLayoutInfo( text.mLinesLayoutInfo ), + mNumberOfCharacters( text.mNumberOfCharacters ), + mMaxItalicsOffset( text.mMaxItalicsOffset ), + mEllipsizeLayoutInfo( text.mEllipsizeLayoutInfo ) +{ +} + +TextLayoutInfo& TextLayoutInfo::operator=( const TextLayoutInfo& text ) +{ + mWholeTextSize = text.mWholeTextSize; + mMaxWordWidth = text.mMaxWordWidth; + mLinesLayoutInfo = text.mLinesLayoutInfo; + mNumberOfCharacters = text.mNumberOfCharacters; + mMaxItalicsOffset = text.mMaxItalicsOffset; + mEllipsizeLayoutInfo = text.mEllipsizeLayoutInfo; + + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +void CreateTextInfo( const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // * Traverse the given text spliting it in lines, each line in groups of words and each group of words in words. + // * If possible, it joins characters with same style in one text-actor. + // * White spaces and new line characters are alone in one word. + // * Bidirectional text is processed in each line. + // * A group of words contains text in only one direction (Left to Right or Right to Left but not a mix of both). + // * Generates a layout data structure to store layout information (size, position, ascender, text direction, etc) and metrics of all characters. + // * Generates a text-actor data structure to store text, style and text-actors. + // TODO: finish and test the bidirectional implementation. + + // Collect previously created text-actors. + std::vector textActors; + CollectTextActorsFromLines( textActors, relayoutData.mTextLayoutInfo, 0, relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() ); + + if( !textActors.empty() ) + { + // Add text-actors to the cache. + relayoutData.mTextActorCache.InsertTextActors( textActors ); + relayoutData.mTextActorCache.ClearTexts(); + } + + // Store the ellipsize layout info before clearing the previous created info. + const WordLayoutInfo ellipsizeInfo = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo; + + // clear previous created info. + relayoutData.mTextLayoutInfo = TextLayoutInfo(); + relayoutData.mCharacterLogicalToVisualMap.clear(); + relayoutData.mCharacterVisualToLogicalMap.clear(); + + // Sets the ellipsize layout info. + relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = ellipsizeInfo; + + // Split the whole text in lines. + std::vector lines; + TextProcessor::SplitInLines( text, + lines ); + + // Traverse all lines + for( std::vector::const_iterator lineIt = lines.begin(), lineEndIt = lines.end(); lineIt != lineEndIt; ++lineIt ) + { + const MarkupProcessor::StyledTextArray& line( *lineIt ); + + // Data structures for the new line + LineLayoutInfo lineLayoutInfo; + + // Fills the line data structures with the layout info. + CreateLineInfo( line, + relayoutData, + lineLayoutInfo ); + + if( 0 < lineLayoutInfo.mNumberOfCharacters ) + { + // do not add the line offset if the line has no characters. + lineLayoutInfo.mSize.height += layoutParameters.mLineHeightOffset; + lineLayoutInfo.mLineHeightOffset = layoutParameters.mLineHeightOffset; + } + else + { + // line height needs to be added for the last line. + + float lineHeight = 0.f; + // Get the last character of the last line. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + const LineLayoutInfo& lineInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + const CharacterLayoutInfo characterInfo = GetLastCharacterLayoutInfo( lineInfo ); + + lineHeight = characterInfo.mSize.height; + } + + lineLayoutInfo.mSize.height = lineHeight; + } + + // Update layout info for the whole text. + UpdateSize( relayoutData.mTextLayoutInfo.mWholeTextSize, lineLayoutInfo.mSize, GrowHeight ); + relayoutData.mTextLayoutInfo.mNumberOfCharacters += lineLayoutInfo.mNumberOfCharacters; + + // Add the line to the current text. + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.push_back( lineLayoutInfo ); + } // end of lines +} + +void UpdateTextInfo( const std::size_t position, + const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // Update current internal data structure with added text. + + // * Creates layout info for the given text. + // * With the given position, find where to add the text. + // * If the new text is not added at the end of current text, a line need to be split. + // * Merge the last line of the new text to the last part or the split line. + // * Add lines between first and last of the new text. + // * Merge the first part of the split line with the first line of the new text. + // * Update layout info and create new text actors if needed. + + // Early returns: + + if( text.empty() ) + { + // nothing to do if the input text is empty. + return; + } + + if( 0 == relayoutData.mTextLayoutInfo.mNumberOfCharacters ) + { + // Current text is empty. There is no need to update current data structure, + // just create a new one with the new input text. + CreateTextInfo( text, + layoutParameters, + relayoutData ); + return; + } + + // initial checks. + if( position > relayoutData.mTextLayoutInfo.mNumberOfCharacters ) + { + // Asserts if text is to be added out of bounds. + DALI_ASSERT_ALWAYS( !"TextViewProcessor::UpdateTextInfo (insert). Trying to insert text out of bounds." ); + } + + TextView::RelayoutData relayoutDataForNewText; + + // Creates layout info for the given text. + // It doesn't create text-actors as text could be added to an existing one. + CreateTextInfo( text, + layoutParameters, + relayoutDataForNewText ); + + // Update logical-to-visual and visual-to-logical tables. + // TODO: check that for mixed RTL/LTR text. + std::size_t index = 0; + for( std::size_t i = 0; i < relayoutDataForNewText.mTextLayoutInfo.mNumberOfCharacters; ++i ) + { + relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index ); + relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index ); + ++index; + } + + // If a line is split, it stores the last part of the line. + LineLayoutInfo lastLineLayoutInfo; + + // Stores indices to the line, group of words, word and character of the given position. + TextInfoIndices textInfoIndices; + + if( position < relayoutData.mTextLayoutInfo.mNumberOfCharacters ) + { + // Get line, group, word and character indices for given position. + GetIndicesFromGlobalCharacterIndex( position, + relayoutData.mTextLayoutInfo, + textInfoIndices ); + + // 1) Split the line + + // Split a line in two is needed, then merge the first part of the split line + // with the first line of the new text, add subsequent lines and merge the last line + // of the new text with the last part of the split line. + + // Implementation notes! + // + // These references to the first line are declared in this scope because if new lines are inserted in step 2, + // they become invalid, making the algorithm to crash if used again. + // In the step 3, references to the first line are needed and declared again. + + // Stores the first part of the split line. + LineLayoutInfo& firstLineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex ) ); + + SplitLine( textInfoIndices, + PointSize( layoutParameters.mLineHeightOffset ), + firstLineLayoutInfo, + lastLineLayoutInfo ); + } + else + { + // Position is just after the last character. + // Calculates indices for that position. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + textInfoIndices.mLineIndex = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() - 1; + const LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) + { + textInfoIndices.mGroupIndex = lineLayoutInfo.mWordGroupsLayoutInfo.size() - 1; + const WordGroupLayoutInfo& groupLayoutInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) ); + + if( !groupLayoutInfo.mWordsLayoutInfo.empty() ) + { + textInfoIndices.mWordIndex = groupLayoutInfo.mWordsLayoutInfo.size() - 1; + + const WordLayoutInfo& wordLayoutInfo( *( groupLayoutInfo.mWordsLayoutInfo.end() - 1 ) ); + textInfoIndices.mCharacterIndex = wordLayoutInfo.mCharactersLayoutInfo.size(); + } + } + } + } + + // 2) If the new text has more than 1 line, merge the last line of the input text with the last part of the split line. + //TODO check this cases ( num lines ==1, >1, >2 ) if it could be simplified. + if( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() > 1 ) + { + LineLayoutInfo& lastInputLineLayoutInfo( *( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + MergeLine( lastInputLineLayoutInfo, + lastLineLayoutInfo ); + + if( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() > 2 ) + { + // Insert all lines except first and last in the text. + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.insert( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex + 1, + relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() + 1, + relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ); + } + + // Insert the last line to the text + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.insert( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex + relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() - 1, + lastInputLineLayoutInfo ); + } + else + { + // Merge the new line to the last part of the split line. + LineLayoutInfo& inputLineLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() ); + + MergeLine( inputLineLayoutInfo, + lastLineLayoutInfo ); + } + + // 3) Merge the first line of the split text with the first line of the input text. + LineLayoutInfo& firstLineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex ) ); + LineLayoutInfo& firstInputLineLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() ); + + MergeLine( firstLineLayoutInfo, + firstInputLineLayoutInfo ); + + // 4) Update text info. + + // Updates the whole text size, maximum word size, etc. + UpdateTextLayoutInfo( relayoutData.mTextLayoutInfo ); +} + +void UpdateTextInfo( const std::size_t position, + const std::size_t numberOfCharacters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData, + const TextOperationOnRemove clearText ) +{ + // Removes 'numberOfCharacters' starting from 'position'. + + // * It checks if text to be deleted is in the same line or not: + // * If it is not, check which lines need to be split/merged or deleted. + // * If it is but all characters of the line are going to be deleted, just delete the line (nothing needs to be split/merged) + // * If only some characters of the same line are going to be deleted, proceed similarly: check if text to be deleted is in the same group of words. + // * If it is not, check which groups of words need to be split/merged or deleted. Two groups of words can't be merged if they contain text with different direction (Left to Right / Right to Left) + // * If it is but all characters of the group are going to be deleted, delete the group. TODO: Check if previous and following group need to be merged. + // * If only some characters of the same group of words need to be deleted, proceed similarly: check if text to be deleted is in the same word. + // * If it is not, split/merge words. + // * Check if the whole word needs to be deleted. + // * Check if only some characters of the word need to be deleted. + // * Updates layout info. + + // * The algorithm checks if a word separator is deleted (in that case, different words need to be merged) and if a new line separator is deleted (two lines need to be merged). + + // Early return + + if( 0 == numberOfCharacters ) + { + DALI_ASSERT_DEBUG( !"TextViewProcessor::UpdateTextInfo. WARNING: trying to delete 0 characters!" ) + + // nothing to do if no characters are deleted. + return; + } + + // Asserts if trying to delete text out of bounds. + DALI_ASSERT_ALWAYS( position + numberOfCharacters <= relayoutData.mTextLayoutInfo.mNumberOfCharacters && "TextViewProcessor::UpdateTextInfo. ERROR: trying to delete characters out of boundary" ); + + // Remove characters from character to visual map and vs //TODO: check this for RTL text!! + relayoutData.mCharacterLogicalToVisualMap.erase( relayoutData.mCharacterLogicalToVisualMap.end() - numberOfCharacters, relayoutData.mCharacterLogicalToVisualMap.end() ); + relayoutData.mCharacterVisualToLogicalMap.erase( relayoutData.mCharacterVisualToLogicalMap.end() - numberOfCharacters, relayoutData.mCharacterVisualToLogicalMap.end() ); + + // Get line, group of words, word and character indices for the given start position. + TextInfoIndices textInfoIndicesBegin; + GetIndicesFromGlobalCharacterIndex( position, + relayoutData.mTextLayoutInfo, + textInfoIndicesBegin ); + + // Get line, group of words, word and character indices for the given end position (start position + number of characters to be deleted). + TextInfoIndices textInfoIndicesEnd; + GetIndicesFromGlobalCharacterIndex( position + numberOfCharacters - 1, + relayoutData.mTextLayoutInfo, + textInfoIndicesEnd ); + + // Vectors used to temporary store text-actors removed from text. + // Three vectors are needed because text-actors are not removed in order + // but insert them in order is required to reuse them later. + std::vector removedTextActorsFromBegin; + std::vector removedTextActorsFromMid; + std::vector removedTextActorsFromEnd; + + // Whether lines, group of words and words need to be merged. + bool mergeLines = false; + bool mergeGroups = false; + bool mergeWords = false; + + // Indices of the lines, group of words and words to be merged. + TextInfoIndices textInfoMergeIndicesBegin; + TextInfoIndices textInfoMergeIndicesEnd; + + const LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex ) ); // used to check the number of characters of the line + // if all characters to be deleted are in the same line. + if( textInfoIndicesBegin.mLineIndex < textInfoIndicesEnd.mLineIndex ) + { + // Deleted text is from different lines. It may need to split two lines, and merge first part of the first one with last part of the last one. + + // whether first or last line need to be split and merged with the last part. + bool mergeFirstLine = false; + bool mergeLastLine = true; + + textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex; + textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesEnd.mLineIndex; + + if( ( textInfoIndicesBegin.mGroupIndex > 0 ) || ( textInfoIndicesBegin.mWordIndex > 0 ) || ( textInfoIndicesBegin.mCharacterIndex > 0 ) ) + { + // first character to be deleted is not the first one of the current line. + ++textInfoIndicesBegin.mLineIndex; // won't delete current line + + // As some characters remain, this line could be merged with the last one. + mergeFirstLine = true; + } + + // Check if all characters of the last line are going to be deleted. + bool wholeLinedeleted = false; + const LineLayoutInfo& lastLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesEnd.mLineIndex ) ); + if( textInfoIndicesEnd.mGroupIndex + 1 == lastLineLayout.mWordGroupsLayoutInfo.size() ) + { + const WordGroupLayoutInfo& lastGroupLayout( *( lastLineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesEnd.mGroupIndex ) ); + if( textInfoIndicesEnd.mWordIndex + 1 == lastGroupLayout.mWordsLayoutInfo.size() ) + { + const WordLayoutInfo& lastWordLayout( *( lastGroupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) ); + if( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) + { + // All characters of the last line are going to be deleted. + ++textInfoIndicesEnd.mLineIndex; // will delete the last line. + + // the whole last line is deleted. Need to check if the next line could be merged. + mergeLastLine = false; + wholeLinedeleted = true; + } + } + } + + if( wholeLinedeleted ) + { + // It means the whole last line is deleted completely. + // It's needed to check if there is another line after that could be merged. + if( textInfoIndicesEnd.mLineIndex < relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() ) + { + mergeLastLine = true; + + // Point the first characters of the next line. + textInfoIndicesEnd.mGroupIndex = 0; + textInfoIndicesEnd.mWordIndex = 0; + textInfoIndicesEnd.mCharacterIndex = 0; + textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesEnd.mLineIndex; + } + } + + // If some characters remain in the first and last line, they need to be merged. + mergeLines = mergeFirstLine && mergeLastLine; + + if( mergeLines ) + { + // last line is going to be merged with the first one, so is not needed. + ++textInfoIndicesEnd.mLineIndex; // will delete the last line. + } + + if( mergeFirstLine ) + { + // Remove characters from the first line. + + // Vectors used to temporary store text-actors removed from the line. + // Three vectors are needed because text-actors are not removed in order + // but insert them in order is required to reuse them later. + std::vector removedTextActorsFromFirstWord; + std::vector removedTextActorsFromFirstGroup; + std::vector removedTextActorsFromGroups; + + // As lineIndexBegin has been increased just to not to remove the line, decrease now is needed to access it. + LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex - 1 ) ); + + if( textInfoIndicesBegin.mGroupIndex + 1 < lineLayout.mWordGroupsLayoutInfo.size() ) + { + // Store text-actors before removing them. + CollectTextActorsFromGroups( removedTextActorsFromGroups, lineLayout, textInfoIndicesBegin.mGroupIndex + 1, lineLayout.mWordGroupsLayoutInfo.size() ); + + // Remove extra groups. If a line has left to right and right to left text, groups after current one could be removed. + RemoveWordGroupsFromLine( textInfoIndicesBegin.mGroupIndex + 1, + lineLayout.mWordGroupsLayoutInfo.size() - ( textInfoIndicesBegin.mGroupIndex + 1 ), + PointSize( layoutParameters.mLineHeightOffset ), + lineLayout ); + } + + WordGroupLayoutInfo& groupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesBegin.mGroupIndex ) ); + + if( ( textInfoIndicesBegin.mWordIndex + 1 < groupLayout.mWordsLayoutInfo.size() ) || ( 0 == textInfoIndicesBegin.mCharacterIndex ) ) + { + // Remove extra words within current group of words. (and current word if whole characters are removed) + // 0 == characterIndexBegin means the whole word is deleted. + const std::size_t wordIndex = ( ( 0 == textInfoIndicesBegin.mCharacterIndex ) ? textInfoIndicesBegin.mWordIndex : textInfoIndicesBegin.mWordIndex + 1 ); + + // Store text-actors before removing them. + CollectTextActorsFromWords( removedTextActorsFromFirstGroup, groupLayout, wordIndex, groupLayout.mWordsLayoutInfo.size() ); + + const std::size_t groupNumberCharacters = groupLayout.mNumberOfCharacters; + RemoveWordsFromWordGroup( wordIndex, + groupLayout.mWordsLayoutInfo.size() - wordIndex, + groupLayout ); + + // discount the removed number of characters. + lineLayout.mNumberOfCharacters -= ( groupNumberCharacters - groupLayout.mNumberOfCharacters ); + } + + if( ( textInfoIndicesBegin.mWordIndex < groupLayout.mWordsLayoutInfo.size() ) && ( textInfoIndicesBegin.mCharacterIndex > 0 ) ) + { + // Only some characters of the word need to be removed. + WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) ); + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromFirstWord, wordLayout, textInfoIndicesBegin.mCharacterIndex, wordLayout.mCharactersLayoutInfo.size() ); + + const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size(); + RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex, + wordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex, + wordLayout ); + + // discount the removed number of characters. + const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() ); + groupLayout.mNumberOfCharacters -= removedNumberOfCharacters; + lineLayout.mNumberOfCharacters -= removedNumberOfCharacters; + } + UpdateLineLayoutInfo( lineLayout, layoutParameters.mLineHeightOffset ); + + // Insert the text-actors in order. + removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() ); + removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstGroup.begin(), removedTextActorsFromFirstGroup.end() ); + removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromGroups.begin(), removedTextActorsFromGroups.end() ); + } + + if( mergeLastLine && !wholeLinedeleted ) + { + // Some characters from the last line need to be removed. + + // Vectors used to temporary store text-actors removed from the group. + // Three vectors are needed because text-actors are not removed in order + // but insert them in order is required to reuse them later. + std::vector removedTextActorsFromFirstWord; + std::vector removedTextActorsFromFirstGroup; + std::vector removedTextActorsFromGroups; + + // lineIndexEnd was increased to delete the last line if lines need to be merged. + // To access now the last line we need to decrease the index. + const std::size_t lineIndex = ( mergeLines ? textInfoIndicesEnd.mLineIndex - 1 : textInfoIndicesEnd.mLineIndex ); + + // Get the last line. + LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + lineIndex ) ); + + if( textInfoIndicesEnd.mGroupIndex > 0 ) + { + // Store text-actors before removing them. + CollectTextActorsFromGroups( removedTextActorsFromGroups, lineLayout, 0, textInfoIndicesEnd.mGroupIndex ); + + // Remove extra groups from the beginning of the line to the current group of words. + RemoveWordGroupsFromLine( 0, + textInfoIndicesEnd.mGroupIndex, + PointSize( layoutParameters.mLineHeightOffset ), + lineLayout ); + } + + // The group of characters which contains the characters to be removed is now the first one. + WordGroupLayoutInfo& groupLayout( *lineLayout.mWordGroupsLayoutInfo.begin() ); + + // Check if is needed remove the whole word. (If the character index is pointing just after the end of the word) + const WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) ); + bool removeWholeWord = wordLayout.mCharactersLayoutInfo.size() == textInfoIndicesEnd.mCharacterIndex + 1; + + if( ( textInfoIndicesEnd.mWordIndex > 0 ) || ( removeWholeWord ) ) + { + // Store text-actors before removing them. + CollectTextActorsFromWords( removedTextActorsFromFirstGroup, groupLayout, 0, ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1 : textInfoIndicesEnd.mWordIndex ); + + // Remove extra words. (and current word if whole characters are removed) + const std::size_t groupNumberCharacters = groupLayout.mNumberOfCharacters; + RemoveWordsFromWordGroup( 0, + ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1 : textInfoIndicesEnd.mWordIndex, + groupLayout ); + + // discount the removed number of characters. + lineLayout.mNumberOfCharacters -= ( groupNumberCharacters - groupLayout.mNumberOfCharacters ); + } + + if( !removeWholeWord ) + { + // Only some characters of the word need to be deleted. + + // After removing all extra words. The word with the characters to be removed is the first one. + WordLayoutInfo& wordLayout( *groupLayout.mWordsLayoutInfo.begin() ); + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromFirstWord, wordLayout, 0, textInfoIndicesEnd.mCharacterIndex + 1 ); + + const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size(); + RemoveCharactersFromWord( 0, + textInfoIndicesEnd.mCharacterIndex + 1, + wordLayout ); + + // discount the removed number of characters. + const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() ); + groupLayout.mNumberOfCharacters -= removedNumberOfCharacters; + lineLayout.mNumberOfCharacters -= removedNumberOfCharacters; + UpdateGroupLayoutInfo( groupLayout ); + } + UpdateLineLayoutInfo( lineLayout, layoutParameters.mLineHeightOffset ); + + // Insert the text-actors in order. + removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() ); + removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstGroup.begin(), removedTextActorsFromFirstGroup.end() ); + removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromGroups.begin(), removedTextActorsFromGroups.end() ); + } + } // end delete text from different lines + else if( ( textInfoIndicesBegin.mLineIndex == textInfoIndicesEnd.mLineIndex ) && ( lineLayout.mNumberOfCharacters == numberOfCharacters ) ) + { + // the whole line needs to be deleted. + ++textInfoIndicesEnd.mLineIndex; // will delete current line. + } + else + { + // deleted text is within the same line. (merge lines could be needed if the line separator character is deleted) + + // Line which contains the characters to be deleted. + LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex ) ); + + const WordGroupLayoutInfo& groupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesBegin.mGroupIndex ) ); // used to check the number of characters of the group of words + // if all characters to be deleted are in the same group of words. + if( textInfoIndicesBegin.mGroupIndex < textInfoIndicesEnd.mGroupIndex ) + { + // Deleted text is from different group of words. The two different group of words may be merged if they have text with same direction. + + // whether first or last group of words need to be split and merged with the last part. + bool splitFirstGroup = false; + bool splitLastGroup = true; + + textInfoMergeIndicesBegin.mGroupIndex = textInfoIndicesBegin.mGroupIndex; + textInfoMergeIndicesEnd.mGroupIndex = textInfoIndicesEnd.mGroupIndex; + + if( ( textInfoIndicesBegin.mWordIndex > 0 ) || ( textInfoIndicesBegin.mCharacterIndex > 0 ) ) + { + // first character to be deleted is not the first one of the current group. + ++textInfoIndicesBegin.mGroupIndex; // won't delete current group + + // As some characters remain, this group needs to be split and could be merged with the last one. + splitFirstGroup = true; + } + + // Check if all characters of the last group are going to be deleted. + const WordGroupLayoutInfo& lastGroupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesEnd.mGroupIndex ) ); + if( textInfoIndicesEnd.mWordIndex + 1 == lastGroupLayout.mWordsLayoutInfo.size() ) + { + const WordLayoutInfo& lastWordLayout( *( lastGroupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) ); + if( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) + { + // All characters of the last group are going to be deleted. + ++textInfoIndicesEnd.mGroupIndex; // will delete the last group. + + // The whole last group is deleted. No need to merge groups. + splitLastGroup = false; + } + } + + // Only merge two groups if they are not deleted completely and they have same direction. + mergeGroups = ( splitFirstGroup && splitLastGroup ) && ( groupLayout.mDirection == lastGroupLayout.mDirection ); + + if( mergeGroups ) + { + // last group is going to be merged. + ++textInfoIndicesEnd.mGroupIndex; // will delete the last group. + } + + if( splitFirstGroup ) + { + // Remove characters from the first group. + + // As wordGroupIndexBegin has been increased just to not to remove the group of words, decrease now is needed to access it. + WordGroupLayoutInfo& groupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesBegin.mGroupIndex - 1 ) ); + + if( ( textInfoIndicesBegin.mWordIndex + 1 < groupLayout.mWordsLayoutInfo.size() ) || ( 0 == textInfoIndicesBegin.mCharacterIndex ) ) + { + // Remove extra words within current group of words. (and current word if whole characters are removed) + // 0 == characterIndexBegin means the whole word is deleted. + const std::size_t wordIndex = ( ( 0 == textInfoIndicesBegin.mCharacterIndex ) ? textInfoIndicesBegin.mWordIndex : textInfoIndicesBegin.mWordIndex + 1 ); + + // Store text-actors before removing them. + CollectTextActorsFromWords( removedTextActorsFromBegin, groupLayout, wordIndex, groupLayout.mWordsLayoutInfo.size() ); + + RemoveWordsFromWordGroup( wordIndex, + groupLayout.mWordsLayoutInfo.size() - wordIndex, + groupLayout ); + } + + if( ( textInfoIndicesBegin.mWordIndex < groupLayout.mWordsLayoutInfo.size() ) && ( textInfoIndicesBegin.mCharacterIndex > 0 ) ) + { + // Only some characters of the word need to be removed. + WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) ); + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromBegin, wordLayout, textInfoIndicesBegin.mCharacterIndex, wordLayout.mCharactersLayoutInfo.size() ); + + RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex, + wordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex, + wordLayout ); + } + } + + if( splitLastGroup ) + { + // Some characters from the last group of words need to be removed. + + // textInfoIndicesEnd.mGroupIndex was increased to delete the last group of words if groups need to be merged. + // To access now the last group of words we need to decrease the index. + std::size_t index = mergeGroups ? textInfoIndicesEnd.mGroupIndex - 1 : textInfoIndicesEnd.mGroupIndex; + + // Get the last group of words. + WordGroupLayoutInfo& groupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + index ) ); + + // Check if is needed remove the whole word. (If the character index is pointing just after the end of the word) + const WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) ); + bool removeWholeWord = wordLayout.mCharactersLayoutInfo.size() == textInfoIndicesEnd.mCharacterIndex + 1; + + if( ( textInfoIndicesEnd.mWordIndex > 0 ) || ( removeWholeWord ) ) + { + // Store text-actors before removing them. + CollectTextActorsFromWords( removedTextActorsFromBegin, groupLayout, 0, ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1 : textInfoIndicesEnd.mWordIndex ); + + // Remove extra words. (and current word if whole characters are removed) + RemoveWordsFromWordGroup( 0, + ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1 : textInfoIndicesEnd.mWordIndex, + groupLayout ); + } + + if( !removeWholeWord ) + { + // Only some characters of the word need to be deleted. + + // After removing all extra words. The word with the characters to be removed is the first one. + WordLayoutInfo& wordLayout( *groupLayout.mWordsLayoutInfo.begin() ); + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromBegin, wordLayout, 0, textInfoIndicesEnd.mCharacterIndex + 1 ); + + RemoveCharactersFromWord( 0, + textInfoIndicesEnd.mCharacterIndex + 1, + wordLayout ); + } + } + } // end of remove from different groups + else if( ( textInfoIndicesBegin.mGroupIndex == textInfoIndicesEnd.mGroupIndex ) && ( groupLayout.mNumberOfCharacters == numberOfCharacters ) ) + { + // The whole group is deleted. + ++textInfoIndicesEnd.mGroupIndex; // will delete current group. + // TODO group before and group after need to be merged!!! + } + else + { + // characters to be deleted are on the same group of words. (words may need to be merged) + + // Group of words which contains the characters to be deleted. + WordGroupLayoutInfo& groupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesBegin.mGroupIndex ) ); + + RemoveCharactersFromWordGroupInfo( relayoutData, + numberOfCharacters, + mergeWords, + mergeLines, + textInfoIndicesBegin, + textInfoIndicesEnd, + textInfoMergeIndicesBegin, + textInfoMergeIndicesEnd, + groupLayout, + removedTextActorsFromBegin, + removedTextActorsFromEnd ); + + if( mergeWords ) + { + // Merges words pointed by textInfoMergeIndicesBegin.mWordIndex and textInfoMergeIndicesEnd.mWordIndex calculated previously. + DALI_ASSERT_DEBUG( ( textInfoMergeIndicesBegin.mWordIndex < groupLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (begin) out of bounds." ); + DALI_ASSERT_DEBUG( ( textInfoMergeIndicesEnd.mWordIndex < groupLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (end) out of bounds." ); + + WordLayoutInfo& firstWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesBegin.mWordIndex ) ); + WordLayoutInfo& lastWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesEnd.mWordIndex ) ); + + MergeWord( firstWordLayout, + lastWordLayout ); + } + + // Store text-actors before removing them. + const std::size_t endIndex = ( mergeWords && ( textInfoIndicesEnd.mWordIndex > 0 ) ) ? textInfoIndicesEnd.mWordIndex - 1 : textInfoIndicesEnd.mWordIndex; // text-actors from the last word may have been added in the merge above. + CollectTextActorsFromWords( removedTextActorsFromMid, groupLayout, textInfoIndicesBegin.mWordIndex, endIndex ); + + // Remove unwanted words using previously calculated indices. (including the last part of the merged word) + groupLayout.mWordsLayoutInfo.erase( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex, groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ); + + // Update group of words info + groupLayout.mNumberOfCharacters -= numberOfCharacters; + groupLayout.mSize = Size(); + groupLayout.mAscender = 0; + for( WordLayoutInfoContainer::const_iterator it = groupLayout.mWordsLayoutInfo.begin(), endIt = groupLayout.mWordsLayoutInfo.end(); + it != endIt; + ++it ) + { + const WordLayoutInfo& layoutInfo( *it ); + UpdateSize( groupLayout.mSize, layoutInfo.mSize ); + groupLayout.mAscender = std::max( groupLayout.mAscender, layoutInfo.mAscender ); + } + } // end of remove from same group + + if( mergeGroups ) + { + // Merges group of words pointed by textInfoMergeIndicesBegin.mGroupIndex and textInfoMergeIndicesEnd.mGroupIndex calculated previously. + + WordGroupLayoutInfo& firstGroupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoMergeIndicesBegin.mGroupIndex ) ); + + const WordGroupLayoutInfo& lastGroupLayout( *( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoMergeIndicesEnd.mGroupIndex ) ); + + MergeWordGroup( firstGroupLayout, + lastGroupLayout ); + } + + // Remove unwanted groups of words using previously calculated indices. (including the last part of the merged group of words) + lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesBegin.mGroupIndex, lineLayout.mWordGroupsLayoutInfo.begin() + textInfoIndicesEnd.mGroupIndex ); + + // Update line info. + lineLayout.mNumberOfCharacters -= numberOfCharacters; + lineLayout.mSize = Size(); + lineLayout.mAscender = 0; + for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end(); + it != endIt; + ++it ) + { + const WordGroupLayoutInfo& layoutInfo( *it ); + UpdateSize( lineLayout.mSize, layoutInfo.mSize ); + lineLayout.mAscender = std::max( lineLayout.mAscender, layoutInfo.mAscender ); + } + lineLayout.mSize.height += layoutParameters.mLineHeightOffset; + lineLayout.mLineHeightOffset = layoutParameters.mLineHeightOffset; + }// end delete text from same line. + + if( mergeLines ) + { + // Merges lines pointed by textInfoMergeIndicesBegin.mLineIndex and textInfoMergeIndicesEnd.mLineIndex calculated previously. + + LineLayoutInfo& firstLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoMergeIndicesBegin.mLineIndex ) ); + + const LineLayoutInfo& lastLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoMergeIndicesEnd.mLineIndex ) ); + + MergeLine( firstLineLayout, + lastLineLayout ); + } + + // Store text-actors before removing them. + const std::size_t endIndex = ( mergeLines && ( textInfoIndicesEnd.mLineIndex > 0 ) ) ? textInfoIndicesEnd.mLineIndex - 1 : textInfoIndicesEnd.mLineIndex; // text-actors from the last line may have been added in the merge above. + CollectTextActorsFromLines( removedTextActorsFromMid, + relayoutData.mTextLayoutInfo, + textInfoIndicesBegin.mLineIndex, + endIndex ); + + // Remove unwanted lines using previously calculated indices. (including the last part of the merged line) + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.erase( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex, + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesEnd.mLineIndex ); + + // Update text info. + UpdateTextLayoutInfo( relayoutData.mTextLayoutInfo ); + + // If the last character of the last line is a new line character, an empty line need to be added. + if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() ) + { + const WordLayoutInfo lastWordLayout = GetLastWordLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) ); + + if( LineSeparator == lastWordLayout.mType ) + { + LineLayoutInfo lastLineLayout; + + const CharacterLayoutInfo layoutInfo = GetLastCharacterLayoutInfo( lastWordLayout ); + lastLineLayout.mSize.height = layoutInfo.mSize.height; + + relayoutData.mTextLayoutInfo.mLinesLayoutInfo.push_back( lastLineLayout ); + + relayoutData.mTextLayoutInfo.mWholeTextSize.height += layoutInfo.mSize.height; + } + } + + // Clear the text from the text-actors if required. + if( CLEAR_TEXT == clearText ) + { + ClearText( removedTextActorsFromEnd ); + ClearText( removedTextActorsFromMid ); + ClearText( removedTextActorsFromBegin ); + } + + // Insert text-actors into the cache. + // Text-actors are inserted in reverse order to use first the first removed. + relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromEnd ); + relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromMid ); + relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromBegin ); +} + +void UpdateTextInfo( const std::size_t position, + const std::size_t numberOfCharacters, + const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ) +{ + // Replaces 'numberOfCharacters' of text starting from 'position' with the given text. + + // TODO: Temporary implementation with remove and insert. + + // Remove. + UpdateTextInfo( position, + numberOfCharacters, + layoutParameters, + relayoutData, + KEEP_TEXT ); // Do not clear the text from the text-actors. + + // Insert. + UpdateTextInfo( position, + text, + layoutParameters, + relayoutData ); +} + +void UpdateTextInfo( const float lineHeightOffset, + TextLayoutInfo& textLayoutInfo ) +{ + // Updates the space between lines with the new offset value. + + float newTextHeight = 0.f; + + for( LineLayoutInfoContainer::iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + LineLayoutInfo& lineLayoutInfo( *lineIt ); + + lineLayoutInfo.mSize.height += ( lineHeightOffset - lineLayoutInfo.mLineHeightOffset ); + newTextHeight += lineLayoutInfo.mSize.height; + + lineLayoutInfo.mLineHeightOffset = lineHeightOffset; + } + + textLayoutInfo.mWholeTextSize.height = newTextHeight; +} + +void UpdateTextInfo( const TextStyle& style, + const TextStyle::Mask mask, + TextView::RelayoutData& relayoutData ) +{ + // Change text style for all text-actors. + + for( LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + LineLayoutInfo& line( *lineIt ); + + for( WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt ) + { + WordGroupLayoutInfo& group( *groupIt ); + + for( WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt ) + { + WordLayoutInfo& word( *wordIt ); + + for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + CharacterLayoutInfo& characterLayout( *characterIt ); + + characterLayout.mStyledText.mStyle.Copy( style, mask ); + + // Checks if the font family supports all glyphs. If not, chooses a most suitable one. + ChooseFontFamilyName( characterLayout.mStyledText ); + + // Mark the character to be set the new style into the text-actor. + characterLayout.mSetStyle = true; + } // end characters + } // end words + } // end group of words + } // end lines +} + +void InitializeTextActorInfo( TextView::RelayoutData& relayoutData ) +{ + TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo; + if( textLayoutInfo.mLinesLayoutInfo.empty() ) + { + // nothing to do if there is no lines. + return; + } + + std::size_t characterGlobalIndex = 0; // Index to the global character (within the whole text). + std::size_t lineLayoutInfoIndex = 0; // Index to the laid out line info. + const std::size_t lineLayoutInfoSize = relayoutData.mLines.size(); // Number or laid out lines. + bool lineLayoutEnd = false; // Whether lineLayoutInfoIndex points at the last laid out line. + + TextActor currentTextActor; // text-actor used when the edit mode is disabled. + TextStyle currentStyle; // style for the current text-actor. + Vector4 currentGradientColor; // gradient color for the current text-actor. + Vector2 currentStartPoint; // start point for the current text-actor. + Vector2 currentEndPoint; // end point for the current text-actor. + + std::vector textActorsToRemove; // Keep a vector of text-actors to be included into the cache. + + for( LineLayoutInfoContainer::iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end(); + lineIt != lineEndIt; + ++lineIt ) + { + LineLayoutInfo& line( *lineIt ); + + for( WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end(); + groupIt != groupEndIt; + ++groupIt ) + { + WordGroupLayoutInfo& group( *groupIt ); + + for( WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end(); + wordIt != wordEndIt; + ++wordIt ) + { + WordLayoutInfo& word( *wordIt ); + + for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + CharacterLayoutInfo& characterLayout( *characterIt ); + + if( !characterLayout.mStyledText.mText.IsEmpty() ) + { + // Do not create a text-actor if there is no text. + const std::size_t length = characterLayout.mStyledText.mText.GetLength(); + const Character character = characterLayout.mStyledText.mText[0]; + + if( ( 1 < length ) || + ( ( 1 == length ) && character.IsWhiteSpace() && characterLayout.mStyledText.mStyle.GetUnderline() ) || + ( ( 1 == length ) && !character.IsNewLine() && !character.IsWhiteSpace() ) ) + { + // Do not create a text-actor if it's a white space (without underline) or a new line character. + + // Creates one text-actor per each counsecutive group of characters, with the same style, per line. + + // Check if there is a new line. + const bool newLine = !lineLayoutEnd && ( characterGlobalIndex == relayoutData.mLines[lineLayoutInfoIndex].mCharacterGlobalIndex ); + + if( ( characterLayout.mStyledText.mStyle != currentStyle ) || + ( characterLayout.mGradientColor != currentGradientColor ) || + ( characterLayout.mStartPoint != currentStartPoint ) || + ( characterLayout.mEndPoint != currentEndPoint ) || + newLine ) + { + // There is a new style or a new line. + if( newLine ) + { + // Point to the next line. + ++lineLayoutInfoIndex; + if( lineLayoutInfoIndex >= lineLayoutInfoSize ) + { + // Arrived at last line. + lineLayoutEnd = true; + } + } + + if( characterLayout.mTextActor ) + { + // Try to reuse first the text-actor of this character. + currentTextActor = characterLayout.mTextActor; + currentTextActor.SetTextStyle( characterLayout.mStyledText.mStyle ); + } + else + { + // If there is no text-actor, try to retrieve one from the cache. + currentTextActor = relayoutData.mTextActorCache.RetrieveTextActor(); + + // If still there is no text-actor, create one. + if( !currentTextActor ) + { + currentTextActor = TextActor::New( Text(), characterLayout.mStyledText.mStyle, false, true ); + } + else + { + currentTextActor.SetTextStyle( characterLayout.mStyledText.mStyle ); + } + } + + // Update style to be checked with next characters. + currentStyle = characterLayout.mStyledText.mStyle; + currentGradientColor = characterLayout.mGradientColor; + currentStartPoint = characterLayout.mStartPoint; + currentEndPoint = characterLayout.mEndPoint; + + characterLayout.mSetText = false; + characterLayout.mSetStyle = false; + + characterLayout.mTextActor = currentTextActor; + characterLayout.mTextActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + characterLayout.mTextActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT ); + } + else + { + // Same style than previous one. + if( characterLayout.mTextActor ) + { + // There is a previously created text-actor for this character. + // If this character has another one put it into the cache. + characterLayout.mTextActor.SetText( "" ); + textActorsToRemove.push_back( characterLayout.mTextActor ); + characterLayout.mTextActor.Reset(); + } + } + } // no white space / new line char + } // text not empty + + ++characterGlobalIndex; + } // characters + } // words + } // groups + } // lines + + // Insert the spare text-actors into the cache. + relayoutData.mTextActorCache.InsertTextActors( textActorsToRemove ); +} + +} // namespace TextViewProcessor + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-processor.h b/dali-toolkit/internal/controls/text-view/text-view-processor.h new file mode 100644 index 0000000..7114df6 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-processor.h @@ -0,0 +1,133 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Creates a data structure with info to layout the text, and data structures with useful info to modify the layout data structure if characters are added or removed. + * + * @param[in] text The given styled text. + * @param[in] layoutParameters Layout configuration. + * @param[out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + */ +void CreateTextInfo( const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Updates the layout data structures with the new inserted text. + * + * @note Does nothing if text has no characters. + * @note It asserts if position is bigger than the number of characters of the whole text. + * + * @param[in] position Where the text is going to be inserted. + * @param[in] text New styled text to be inserted. + * @param[in] layoutParameters Layout configuration. + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + */ +void UpdateTextInfo( std::size_t position, + const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Updates the layout data structures by removing text. + * + * @note Does nothing if number of characters to be deleted is zero. + * @note It asserts if trying to delete text out of bounds. + * + * @param[in] position Position of the first character to be removed. + * @param[in] numberOfCharacters The number of characters to be removed. + * @param[in] layoutParameters Layout configuration. + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + * @param[in] clearText Whether to clear text-actor's text before insert the text-actors into the cache. + */ +void UpdateTextInfo( std::size_t position, + std::size_t numberOfCharacters, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData, + TextOperationOnRemove clearText ); + +/** + * Updates the layout data structures by replacing text. + * + * @param[in] position Position of the first character to be replaced. + * @param[in] numberOfCharacters The number of characters to be replaced. + * @param[in] text The new styled text. + * @param[in] layoutParameters Layout configuration. + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info and conversion from visual to logical order and vice versa (for RTL text). + */ +void UpdateTextInfo( std::size_t position, + std::size_t numberOfCharacters, + const MarkupProcessor::StyledTextArray& text, + const TextView::LayoutParameters& layoutParameters, + TextView::RelayoutData& relayoutData ); + +/** + * Updates the layout data structure by modifying the space between lines. + * + * @param[in] lineHeightOffset The new space between lines. + * @param[in,out] textLayoutInfo Layout info for all groups of characters, words, groups of words, lines and the whole text. + */ +void UpdateTextInfo( float lineHeightOffset, + TextLayoutInfo& textLayoutInfo ); + +/** + * Updates the text-actor data structures by replacing the style. + * + * @note This operation is called only if the new style doesn't modify the metrics. Consequently it doesn't modify any size info. + * + * @param[in] style The new style. + * @param[in] mask The style mask. + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info. + */ +void UpdateTextInfo( const TextStyle& style, + TextStyle::Mask mask, + TextView::RelayoutData& relayoutData ); + +/** + * Traverse all text initializing all non initialized text-actor handles. + * + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info. + */ +void InitializeTextActorInfo( TextView::RelayoutData& relayoutData ); + +} // namespace TextViewProcessor + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_PROCESSOR_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.cpp b/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.cpp new file mode 100644 index 0000000..a76833f --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.cpp @@ -0,0 +1,550 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-word-group-processor.h" +#include "text-view-word-processor.h" +#include "text-view-processor-helper-functions.h" +#include "text-processor.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +///////////////////// +// Layout info. +///////////////////// + +WordGroupLayoutInfo::WordGroupLayoutInfo() +: mSize(), + mAscender( 0.f ), + mDirection( LTR ), + mWordsLayoutInfo(), + mNumberOfCharacters( 0 ) +{ +} + +WordGroupLayoutInfo::WordGroupLayoutInfo( const WordGroupLayoutInfo& group ) +: mSize( group.mSize ), + mAscender( group.mAscender ), + mDirection( group.mDirection ), + mWordsLayoutInfo( group.mWordsLayoutInfo ), + mNumberOfCharacters( group.mNumberOfCharacters ) +{ +} + +WordGroupLayoutInfo& WordGroupLayoutInfo::operator=( const WordGroupLayoutInfo& group ) +{ + mSize = group.mSize; + mAscender = group.mAscender; + mDirection = group.mDirection; + mWordsLayoutInfo = group.mWordsLayoutInfo; + mNumberOfCharacters = group.mNumberOfCharacters; + + return *this; +} + +void UpdateGroupLayoutInfo( TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo ) +{ + wordGroupLayoutInfo.mSize = Size(); + + for( WordLayoutInfoContainer::iterator it = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordLayoutInfo& layoutInfo( *it ); + + UpdateSize( wordGroupLayoutInfo.mSize, layoutInfo.mSize ); + } +} + +void CreateWordGroupInfo( const MarkupProcessor::StyledTextArray& wordGroup, + TextViewProcessor::TextLayoutInfo& textLayoutInfo, + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo ) +{ + // Set the direction of the group. + wordGroupLayoutInfo.mDirection = ( TextProcessor::BeginsRightToLeftCharacter( wordGroup ) ? TextViewProcessor::RTL : TextViewProcessor::LTR ); + + // Split the group of words in words + std::vector words; + TextProcessor::SplitInWords( wordGroup, words ); + + // if last word has a new line separator, create a new word. + if( !words.empty() ) + { + MarkupProcessor::StyledTextArray& word( *( words.end() - 1 ) ); + if( word.size() > 1 ) + { + // do nothing if the word has only one character. + MarkupProcessor::StyledText& styledText( *( word.end() - 1 ) ); + if( !styledText.mText.IsEmpty() ) + { + const std::size_t length = styledText.mText.GetLength(); + if( styledText.mText[length-1].IsNewLine() ) + { + // Last character of this word is a new line character. + + // Remove line separator character from current word. + styledText.mText.Remove( length - 1, 1 ); + + // Create a new word with the line separator character. + MarkupProcessor::StyledText newLineText( Text( styledText.mText[length-1] ), styledText.mStyle ); + + MarkupProcessor::StyledTextArray newLineWord; + newLineWord.push_back( newLineText ); + + words.push_back( newLineWord ); + } + } + } + } + + // Reverse if right to left. + if( TextViewProcessor::RTL == wordGroupLayoutInfo.mDirection ) + { + std::reverse( words.begin(), words.end() ); + } + + std::string lastCharacterFont; // Keeps the font used by the last character. It's used to set the font to a word separator. + + // Traverse all words. + for( std::vector::const_iterator wordIt = words.begin(), wordEndIt = words.end(); wordIt != wordEndIt; ++wordIt ) + { + const MarkupProcessor::StyledTextArray& word( *wordIt ); + + // Data structures for the new word. + WordLayoutInfo wordLayoutInfo; + + CreateWordTextInfo( word, + wordLayoutInfo ); + + // White space's size could be different depending on the type of font. It's important to use the same font than the previous character to + // avoid 'jumps' of characters when there is a switch between one text-actor per character and one text-actor per line and/or style. + if( WordSeparator == wordLayoutInfo.mType ) + { + // If current word is a word separator (white space) then the font of the last character is set. + for( CharacterLayoutInfoContainer::iterator characterIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), characterEndIt = wordLayoutInfo.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + CharacterLayoutInfo& characterLayout( *characterIt ); + + characterLayout.mStyledText.mStyle.SetFontName( lastCharacterFont ); + } + } + else + { + // kepps the font of the last character. + if( !wordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + lastCharacterFont = ( *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 ) ).mStyledText.mStyle.GetFontName(); + } + } + + // Update layout info for the current group of words. + wordGroupLayoutInfo.mNumberOfCharacters += wordLayoutInfo.mCharactersLayoutInfo.size(); + UpdateSize( wordGroupLayoutInfo.mSize, wordLayoutInfo.mSize ); + wordGroupLayoutInfo.mAscender = std::max( wordGroupLayoutInfo.mAscender, wordLayoutInfo.mAscender ); + + // Add current word to the group of words. + wordGroupLayoutInfo.mWordsLayoutInfo.push_back( wordLayoutInfo ); + + // Update the max word width figure. + textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, wordLayoutInfo.mSize.width ); + } // end of words +} + +void RemoveCharactersFromWordGroupInfo( TextView::RelayoutData& relayoutData, + const std::size_t numberOfCharacters, + bool& mergeWords, + bool& mergeLines, + TextViewProcessor::TextInfoIndices& textInfoIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoIndicesEnd, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd, + TextViewProcessor::WordGroupLayoutInfo& groupLayout, + std::vector& removedTextActorsFromFirstWord, + std::vector& removedTextActorsFromLastWord ) +{ + const TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo; + + if( textInfoIndicesBegin.mWordIndex < textInfoIndicesEnd.mWordIndex ) + { + // Deleted text is from different words. The two different words may be merged. + + // Get first word. + WordLayoutInfo& firstWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) ); + + // Get last word. + WordLayoutInfo& lastWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) ); + + // whether first or last word need to be split and merged. + bool mergeFromBegin = false; + bool mergeToEnd = false; + + if( textInfoIndicesBegin.mCharacterIndex > 0 ) + { + // First word is going to be split. It could be merged with the last word. + mergeFromBegin = true; + textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex; + } + else if( ( textInfoIndicesBegin.mCharacterIndex == 0 ) && ( textInfoIndicesBegin.mWordIndex > 0 ) ) + { + // First word is going to be removed completely. + // Check if previous word could be merged. + + // Get word before. + WordLayoutInfo& previousWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1 ) ); + if( WordSeparator != previousWordLayout.mType ) + { + // Previous word is not a word separator, so could be merged. + mergeFromBegin = true; + textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1; + } + } + + if( mergeFromBegin ) + { + // First word (or previous one) could be merged. Check if last one could be merged as well. + + if( textInfoIndicesEnd.mCharacterIndex + 1 < lastWordLayout.mCharactersLayoutInfo.size() ) + { + // Last word is going to be split. It could be merged with the first word. + mergeToEnd = true; + textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex; + } + else if( ( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1 < groupLayout.mWordsLayoutInfo.size() ) ) + { + // Last word is going to be removed completely. + // Check if the word after could be merged. + + // Get word after. + WordLayoutInfo& afterWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex + 1 ) ); + if( WordSeparator != afterWordLayout.mType ) + { + // The word after is not a word separator, so could be merged. + mergeToEnd = true; + textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex + 1; + } + } + + // Merge words only if both words could be merged. + mergeWords = mergeFromBegin && mergeToEnd; + } + + if( ( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1 == groupLayout.mWordsLayoutInfo.size() ) ) + { + // Last word of the line is going to be removed completely. + // Check if it's a line separator. + + if( LineSeparator == lastWordLayout.mType ) + { + // The line separator is going to be removed. + if( textInfoIndicesBegin.mLineIndex + 1 < textLayoutInfo.mLinesLayoutInfo.size() ) + { + // Line need to be merged. + textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex; + textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1; + mergeLines= true; + + ++textInfoIndicesBegin.mLineIndex; // increase both indices, + textInfoIndicesEnd.mLineIndex +=2; // will delete last line. + } + } + } + + if( textInfoIndicesBegin.mCharacterIndex > 0 ) + { + // First word needs to be split. + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromFirstWord, firstWordLayout, textInfoIndicesBegin.mCharacterIndex, firstWordLayout.mCharactersLayoutInfo.size() ); + + RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex, + firstWordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex, + firstWordLayout ); + + ++textInfoIndicesBegin.mWordIndex; // will delete from the word after. + } + + if( textInfoIndicesEnd.mCharacterIndex + 1 < lastWordLayout.mCharactersLayoutInfo.size() ) + { + // Last word needs to be split. + + // Store text-actors before removing them. + CollectTextActors( removedTextActorsFromLastWord, lastWordLayout, 0, textInfoIndicesEnd.mCharacterIndex + 1 ); + + RemoveCharactersFromWord( 0, + textInfoIndicesEnd.mCharacterIndex + 1, + lastWordLayout ); + + if( mergeWords ) + { + // This word is going to be merged, so is not needed. + ++textInfoIndicesEnd.mWordIndex; // will delete the last word. + } + } + else if( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) + { + // The whole last word is going to be removed. + ++textInfoIndicesEnd.mWordIndex; // will delete the last word. + + if( ( WordSeparator == lastWordLayout.mType ) && mergeWords ) + { + // The last word is a word separator and the word after is going to be merged so is not needed. + ++textInfoIndicesEnd.mWordIndex; // will delete the word after the last one. + } + } + } + else + { + // Chraracters to be removed are from the same word. + + RemoveCharactersFromWordInfo( relayoutData, + numberOfCharacters, + mergeWords, + mergeLines, + textInfoIndicesBegin, + textInfoIndicesEnd, + textInfoMergeIndicesBegin, + textInfoMergeIndicesEnd, + groupLayout, + removedTextActorsFromFirstWord ); + } // word indices +} + +void RemoveWordsFromWordGroup( const std::size_t wordIndex, + const std::size_t numberOfWords, + WordGroupLayoutInfo& wordGroupLayoutInfo ) +{ + // Removes words from a group of words. + + // * Check if words or lines can be merged after removing a word or line separator have to be done outside this method. + + // * Note: Currently it's only used to remove a number of words from the beginning, or + // from wordIndex index to the end. This function doesn't merge words (if a white space is removed) + // TODO: merge words if required. + + const std::size_t wordEndIndex = wordIndex + numberOfWords; + + // Remove words from layout info. + wordGroupLayoutInfo.mWordsLayoutInfo.erase( wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordIndex, + wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordEndIndex ); + + // update layout info + wordGroupLayoutInfo.mSize = Size(); + wordGroupLayoutInfo.mAscender = 0.f; + wordGroupLayoutInfo.mNumberOfCharacters = 0; + for( WordLayoutInfoContainer::const_iterator it = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = wordGroupLayoutInfo.mWordsLayoutInfo.end(); + it != endIt; + ++it ) + { + const WordLayoutInfo& info( *it ); + + UpdateSize( wordGroupLayoutInfo.mSize, info.mSize ); + wordGroupLayoutInfo.mAscender = std::max( wordGroupLayoutInfo.mAscender, info.mAscender ); + wordGroupLayoutInfo.mNumberOfCharacters += info.mCharactersLayoutInfo.size(); + } +} + +void SplitWordGroup( const TextInfoIndices& indices, + WordGroupLayoutInfo& firstWordGroupLayoutInfo, + WordGroupLayoutInfo& lastWordGroupLayoutInfo ) +{ + // Splits a group of words in two. + // A word may be split in two as well. + + // * Split the word pointed by indices.mWordIndex using the indices.mCharacterIndex index. + // * Add the last part of the word as first word of the last part of the group of words. + // * Add folliwing words to the last part of the new group of words. + // * Remove from the first part of the group of words all words added to the last part of the group of words. + // * Update layout info. + + //early returns + if( ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) ) + { + // the whole group of words goes to the last part of the group. + lastWordGroupLayoutInfo = firstWordGroupLayoutInfo; + + firstWordGroupLayoutInfo = WordGroupLayoutInfo(); + + return; + } + + if( !firstWordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + const std::size_t numberOfWords = firstWordGroupLayoutInfo.mWordsLayoutInfo.size(); + if( indices.mWordIndex == numberOfWords - 1 ) + { + const WordLayoutInfo& word( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.end() - 1 ) ); + if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() ) + { + // the whole group of words goes to the first part. + + // Just delete whatever there is in the last part of the group of words. + lastWordGroupLayoutInfo = WordGroupLayoutInfo(); + + return; + } + } + } + + lastWordGroupLayoutInfo = WordGroupLayoutInfo(); + + // 1) Split the word within the group of words to be split. + WordLayoutInfo& firstWordLayoutInfo( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex ) ); + WordLayoutInfo lastWordLayoutInfo; + + SplitWord( indices.mCharacterIndex, + firstWordLayoutInfo, + lastWordLayoutInfo ); + + // 2) Add last part of the word to the new group of words. + if( !lastWordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + lastWordGroupLayoutInfo.mWordsLayoutInfo.push_back( lastWordLayoutInfo ); + } + + // 3) Add words from word-position + 1 to the end. + lastWordGroupLayoutInfo.mWordsLayoutInfo.insert( lastWordGroupLayoutInfo.mWordsLayoutInfo.end(), + firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex + 1, firstWordGroupLayoutInfo.mWordsLayoutInfo.end() ); + + // 4) update layout info of the last group of words. + lastWordGroupLayoutInfo.mDirection = firstWordGroupLayoutInfo.mDirection; + + for( WordLayoutInfoContainer::iterator it = lastWordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = lastWordGroupLayoutInfo.mWordsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordLayoutInfo& layoutInfo( *it ); + + UpdateSize( lastWordGroupLayoutInfo.mSize, layoutInfo.mSize ); + lastWordGroupLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size(); + lastWordGroupLayoutInfo.mAscender = std::max( lastWordGroupLayoutInfo.mAscender, layoutInfo.mAscender ); + } + + // 5) Remove words added to the last part of the group of words from the first group of words. + + // if the number of characters of the last word of the first group is zero, it should be removed. + const std::size_t index = ( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ? indices.mWordIndex : indices.mWordIndex + 1 ); + + firstWordGroupLayoutInfo.mWordsLayoutInfo.erase( firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + index, firstWordGroupLayoutInfo.mWordsLayoutInfo.end() ); + + // 6) update layout info of the first group of words. + firstWordGroupLayoutInfo.mSize = Size(); + firstWordGroupLayoutInfo.mAscender = 0.f; + firstWordGroupLayoutInfo.mNumberOfCharacters = 0; + for( WordLayoutInfoContainer::iterator it = firstWordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = firstWordGroupLayoutInfo.mWordsLayoutInfo.end(); + it != endIt; + ++it ) + { + WordLayoutInfo& layoutInfo( *it ); + + UpdateSize( firstWordGroupLayoutInfo.mSize, layoutInfo.mSize ); + firstWordGroupLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size(); + firstWordGroupLayoutInfo.mAscender = std::max( firstWordGroupLayoutInfo.mAscender, layoutInfo.mAscender ); + } +} + +void MergeWordGroup( WordGroupLayoutInfo& firstWordGroupLayoutInfo, + const WordGroupLayoutInfo& lastWordGroupLayoutInfo ) +{ + // Merges two given groups of words. + // + // Can't merge two groups if they have text with different directions (RTL , LTR ) + // or if the last word of the first one is a line separator (new line character) + + // Early returns + + if( lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + // Nothing to merge if last group is empty. + return; + } + + if( firstWordGroupLayoutInfo.mWordsLayoutInfo.empty() ) + { + // If first group is empty, just copy the last group to the first one. + firstWordGroupLayoutInfo = lastWordGroupLayoutInfo; + + return; + } + + // Check both groups have the same direction. + if( firstWordGroupLayoutInfo.mDirection != lastWordGroupLayoutInfo.mDirection ) + { + DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWordGroup(). ERROR: groups with different direction can't be merged." ); + } + + // Check first group doesn't finish with a new line character. + WordLayoutInfo& lastWordLayout( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.end() - 1 ) ); + if( LineSeparator == lastWordLayout.mType ) + { + DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWordGroup(). ERROR: A group of words can't be merged to another group which finishes with a new line character." ); + } + + // If the las word of the first group or the first word of the last group is a white space, both groups can be concatenated. + // Otherwise both words need to be merged first. + const WordLayoutInfo& firstWordLayout( *lastWordGroupLayoutInfo.mWordsLayoutInfo.begin() ); + + std::size_t index = 0; + if( ( WordSeparator != lastWordLayout.mType ) && ( WordSeparator != firstWordLayout.mType ) && ( LineSeparator != firstWordLayout.mType ) ) + { + // Last word of the first group is not a word separator and first word of the last group is not a word or line separator. + // Words need to be merged. + + MergeWord( lastWordLayout, + firstWordLayout ); + + // After merging two words, the rest of the words need to be added. + ++index; // By increasing this index the word already merged won't be added again. + } + + // Merge layout info + firstWordGroupLayoutInfo.mWordsLayoutInfo.insert( firstWordGroupLayoutInfo.mWordsLayoutInfo.end(), + lastWordGroupLayoutInfo.mWordsLayoutInfo.begin() + index, lastWordGroupLayoutInfo.mWordsLayoutInfo.end() ); + UpdateSize( firstWordGroupLayoutInfo.mSize, lastWordGroupLayoutInfo.mSize ); + firstWordGroupLayoutInfo.mAscender = std::max( firstWordGroupLayoutInfo.mAscender, lastWordGroupLayoutInfo.mAscender ); + firstWordGroupLayoutInfo.mNumberOfCharacters += lastWordGroupLayoutInfo.mNumberOfCharacters; +} + +void CollectTextActorsFromGroups( std::vector& textActors, const LineLayoutInfo& line, const std::size_t groupIndexBegin, const std::size_t groupIndexEnd ) +{ + for( WordGroupLayoutInfoContainer::const_iterator groupIt = line.mWordGroupsLayoutInfo.begin() + groupIndexBegin, groupEndIt = line.mWordGroupsLayoutInfo.begin() + groupIndexEnd; + groupIt != groupEndIt; + ++groupIt ) + { + const WordGroupLayoutInfo& group( *groupIt ); + + CollectTextActorsFromWords( textActors, group, 0, group.mWordsLayoutInfo.size() ); + } +} + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h b/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h new file mode 100644 index 0000000..2ff5554 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h @@ -0,0 +1,138 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_GROUP_PROCESSOR_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_GROUP_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Updates the word group layout size info. + * + * @param[in,out] wordGroupLayoutInfo The word group layout info. + */ +void UpdateGroupLayoutInfo( TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo ); + +/** + * Creates a data structure with info to layout the group of words, and data structures with useful info to modify the layout data structure if characters are added or removed. + * + * @param[in] wordGroup The styled group of words. + * @param[in,out] textLayoutInfo Layout info for the whole text. This function uses it to check if the words should be split into individual characters. It also modifies which is the maximum word width. + * @param[out] wordGroupLayoutInfo Layout info for the whole group of words. + */ +void CreateWordGroupInfo( const MarkupProcessor::StyledTextArray& wordGroup, + TextViewProcessor::TextLayoutInfo& textLayoutInfo, + TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo ); + +/** + * Removes a given number of words from the given group of words. + * + * @pre \e wordIndex and \e wordIndex + \e numberOfWords can't exceed the bounds of the group. + * + * @param[in] wordIndex Index to the word within the group of words with the starting position to be deleted. + * @param[in] numberOfWords The number of words to be deleted. + * @param[in,out] wordGroupLayoutInfo The input is the layout info of the group of words. The output is the layout info of the group of words without the removed words. + */ +void RemoveWordsFromWordGroup( std::size_t wordIndex, + std::size_t numberOfWords, + WordGroupLayoutInfo& wordGroupLayoutInfo ); + +/** + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info. + * @param[in] numberOfCharacters The number of characters to be deleted. + * @param[out] mergeWords Whether words need to be merged after removing characters. + * @param[out] mergeLines Whether current line need to be merged with the next one. + * @param[in,out] textInfoIndicesBegin Indices to the line, word and characters from where to delete characters. It returns from where words need to be removed. + * @param[out] textInfoIndicesEnd If lines or words need to be merged it returns info to delete them (If a word is merged, it has to be removed. Equal for lines). + * @param[out] textInfoMergeIndicesBegin The indices to the first part of the line, group and word to be merged. + * @param[out] textInfoMergeIndicesEnd The indices to the last part of the line, group and word to be merged. + * @param[in,out] groupLayout Layout info of the group of words where the word is located. + * @param[out] removedTextActorsFromFirstWord Stores removed text-actors of the word pointed by the 'begin' index. + * @param[out] removedTextActorsFromLastWord Stores removed text-actors of the word pointed by the 'end' index. + */ +void RemoveCharactersFromWordGroupInfo( TextView::RelayoutData& relayoutData, + std::size_t numberOfCharacters, + bool& mergeWords, + bool& mergeLines, + TextViewProcessor::TextInfoIndices& textInfoIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoIndicesEnd, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd, + TextViewProcessor::WordGroupLayoutInfo& groupLayout, + std::vector& removedTextActorsFromFirstWord, + std::vector& removedTextActorsFromLastWord ); + +/** + * Splits a group of words in two. + * + * @note It deletes whatever there is in the last part of the group of words. + * + * @param[in] indices Index to the word within the group of words and index to the character within the word where to split the word. + * @param[in,out] firstWordGroupLayoutInfo The input is the layout info of the given group of words. The output is the first part of the input group of words (from the word \e 0 to the word \e wordPosition). + * @param[in,out] lastWordGroupLayoutInfo Layout info of the last part of the given group of words ( from the word \e wordPosition + \e 1 to the end of the group of words). + */ +void SplitWordGroup( const TextInfoIndices& indices, + WordGroupLayoutInfo& firstWordGroupLayoutInfo, + WordGroupLayoutInfo& lastWordGroupLayoutInfo ); + +/** + * Merges the two given groups of words by adding words of the last group of words to the firs one. + * + * @note Does nothing if last part of the group of words is empty. + * @note If the first part of the group of words is empty it just copy the last part to it. + * @note It assets if groups of words contain text with different direction. (Left to Right and Right to Left text) + * @note it asserts if the last word of the first group is a line separator (new line character) + * + * @param[in,out] firstWordGroupLayoutInfo The input is the layout info of the first group of words. The output is the layout info of the merged group of words. + * @param[in] lastWordGroupLayoutInfo Layout info of the last group of words. + * + */ +void MergeWordGroup( WordGroupLayoutInfo& firstWordGroupLayoutInfo, + const WordGroupLayoutInfo& lastWordGroupLayoutInfo ); + +/** + * Collects text-actors from the given line, within the given indices, and stores them into the text-actor vector. + * + * @param[out] textActors Stores the text-actors of the given line. + * @param[in] line The line with groups of words. + * @param[in] groupIndexBegin Index to the first group of words. + * @param[in] groupIndexEnd Index to the last group of words. + */ +void CollectTextActorsFromGroups( std::vector& textActors, const LineLayoutInfo& line, std::size_t groupIndexBegin, std::size_t groupIndexEnd ); + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_GROUP_PROCESSOR_H__ diff --git a/dali-toolkit/internal/controls/text-view/text-view-word-processor.cpp b/dali-toolkit/internal/controls/text-view/text-view-word-processor.cpp new file mode 100644 index 0000000..eff5577 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-word-processor.cpp @@ -0,0 +1,415 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-word-processor.h" +#include "text-view-processor-helper-functions.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +namespace +{ + +/** + * Updates the word size and ascender. + * + * It's called after deleting some characters. + * + * @param[in] wordLayout The word layout info. + */ +void UpdateLayoutInfo( WordLayoutInfo& wordLayout ) +{ + // Initialize layout info for the whole word. + wordLayout.mSize = Size(); + wordLayout.mAscender = 0.f; + + // Traverse the character layout info to update the word layout. + for( CharacterLayoutInfoContainer::iterator layoutIt = wordLayout.mCharactersLayoutInfo.begin(), layoutEndIt = wordLayout.mCharactersLayoutInfo.end(); + layoutIt != layoutEndIt; + ++layoutIt ) + { + // Layout info for the current character. + CharacterLayoutInfo& layoutInfo( *layoutIt ); + + // Update layout info for the current word. + UpdateSize( wordLayout.mSize, layoutInfo.mSize ); + wordLayout.mAscender = std::max( wordLayout.mAscender, layoutInfo.mAscender ); + } +} + +} // namespace + +///////////////////// +// Layout info. +///////////////////// + +WordLayoutInfo::WordLayoutInfo() +: mSize(), + mAscender( 0.f ), + mType( NoSeparator ), + mCharactersLayoutInfo() +{ +} + +WordLayoutInfo::WordLayoutInfo( const WordLayoutInfo& word ) +: mSize( word.mSize ), + mAscender( word.mAscender ), + mType( word.mType ), + mCharactersLayoutInfo( word.mCharactersLayoutInfo ) +{ +} + +WordLayoutInfo& WordLayoutInfo::operator=( const WordLayoutInfo& word ) +{ + mSize = word.mSize; + mAscender = word.mAscender; + mType = word.mType; + mCharactersLayoutInfo = word.mCharactersLayoutInfo; + + return *this; +} + +void CreateWordTextInfo( const MarkupProcessor::StyledTextArray& word, + TextViewProcessor::WordLayoutInfo& wordLayoutInfo ) +{ + // Split in characters. + for( MarkupProcessor::StyledTextArray::const_iterator charIt = word.begin(), charEndIt = word.end(); charIt != charEndIt; ++charIt ) + { + const MarkupProcessor::StyledText& styledText( *charIt ); + + const std::size_t length = styledText.mText.GetLength(); + + // It could be a group of characters. + for( std::size_t index = 0; index < length; ++index ) + { + MarkupProcessor::StyledText styledCharacter; + styledCharacter.mStyle = styledText.mStyle; + Character character = styledText.mText[index]; + styledCharacter.mText.Append( character ); + + //Choose the right font for the given character and style. + ChooseFontFamilyName( styledCharacter ); + + const Font font = Font::New( FontParameters( styledCharacter.mStyle.GetFontName(), styledCharacter.mStyle.GetFontStyle(), styledCharacter.mStyle.GetFontPointSize() ) ); + const Font::Metrics metrics = font.GetMetrics( character ); + const float ascender = font.GetAscender(); + + // Create layout character info. + CharacterLayoutInfo characterLayoutInfo; + + // Fill Natural size info for current character. + characterLayoutInfo.mHeight = font.GetLineHeight(); + characterLayoutInfo.mAdvance = metrics.GetAdvance(); + characterLayoutInfo.mBearing = metrics.GetBearing(); + + if( character.IsNewLine() ) + { + // A new line character doesn't have any width. + characterLayoutInfo.mSize.width = 0.f; + } + else + { + // Uses advance as width. + characterLayoutInfo.mSize.width = characterLayoutInfo.mAdvance; + } + characterLayoutInfo.mSize.height = characterLayoutInfo.mHeight; + characterLayoutInfo.mAscender = ascender; + + if( styledCharacter.mStyle.GetUnderline() ) + { + characterLayoutInfo.mUnderlineThickness = font.GetUnderlineThickness(); // Both thickness and position includes the + characterLayoutInfo.mUnderlinePosition = font.GetUnderlinePosition(); // vertical pad adjust used in effects like glow or shadow. + } + + // stores the styled text. + characterLayoutInfo.mStyledText.mText = styledCharacter.mText; + characterLayoutInfo.mStyledText.mStyle = styledCharacter.mStyle; + + // Add character layout info to the word layout info and update it. + wordLayoutInfo.mCharactersLayoutInfo.push_back( characterLayoutInfo ); + UpdateSize( wordLayoutInfo.mSize, characterLayoutInfo.mSize ); + wordLayoutInfo.mAscender = std::max( wordLayoutInfo.mAscender, characterLayoutInfo.mAscender ); + wordLayoutInfo.mType = GetTextSeparatorType( character ); + } // end of each character in the group of characters. + } // end of characters in the word. +} + +void RemoveCharactersFromWordInfo( TextView::RelayoutData& relayoutData, + const std::size_t numberOfCharacters, + bool& mergeWords, + bool& mergeLines, + TextViewProcessor::TextInfoIndices& textInfoIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoIndicesEnd, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd, + TextViewProcessor::WordGroupLayoutInfo& groupLayout, + std::vector& removedTextActors ) +{ + const TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo; + + // Get the word. + WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) ); + + if( TextViewProcessor::LineSeparator == wordLayout.mType ) + { + // If the word is a line separator and there is more lines, then current line and the line after need to be merged. + if( textInfoIndicesBegin.mLineIndex + 1 < textLayoutInfo.mLinesLayoutInfo.size() ) + { + // current line is not the last one. + + // Update indices to merge lines. + textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex; + textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1; + + mergeLines = true; + + ++textInfoIndicesBegin.mLineIndex; // increase both indices, + textInfoIndicesEnd.mLineIndex +=2; // will delete last line. + } + + ++textInfoIndicesEnd.mWordIndex; //will delete the line separator; + } + else if( WordSeparator == wordLayout.mType ) + { + // If the word is a word separator. Check if the word before and the word after can be merged. + + if( ( 0 < textInfoIndicesBegin.mWordIndex ) && ( groupLayout.mWordsLayoutInfo.size() > textInfoIndicesBegin.mWordIndex + 1 ) ) + { + const WordLayoutInfo& wordLayoutBefore( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1 ) ); + const WordLayoutInfo& wordLayoutAfter( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex + 1 ) ); + + if( ( NoSeparator == wordLayoutBefore.mType ) && ( NoSeparator == wordLayoutAfter.mType ) ) + { + // This word is a word separator (white space) and is not the first word of the group nor the last one. + mergeWords = true; + + // Set indices to merge the words. + textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1; // word before word separator. + textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesBegin.mWordIndex + 1; // word after word separator. + + textInfoIndicesEnd.mWordIndex += 2; // will delete the word separator and the merged word. + } + else + { + ++textInfoIndicesEnd.mWordIndex; // will delete the word separator; + } + } + else + { + ++textInfoIndicesEnd.mWordIndex; // will delete the word separator; + } + } + else if( numberOfCharacters == wordLayout.mCharactersLayoutInfo.size() ) + { + // The whole word needs to be removed. + ++textInfoIndicesEnd.mWordIndex; // will delete the current word. + } + else + { + // Store text-actors before removing them. + CollectTextActors( removedTextActors, wordLayout, textInfoIndicesBegin.mCharacterIndex, textInfoIndicesBegin.mCharacterIndex + numberOfCharacters ); + + // just remove some characters from current word. + RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex, + numberOfCharacters, + wordLayout ); + } +} + +void RemoveCharactersFromWord( const std::size_t position, + const std::size_t numberOfCharacters, + WordLayoutInfo& wordLayout ) +{ + // Removes a given number of characters from the given word starting from the 'position' index. + + // Early return. + if( 0 == numberOfCharacters ) + { + // nothing to do if the number of characters is zero. + + return; + } + + // Remove characters from layout and text-actor info. + wordLayout.mCharactersLayoutInfo.erase( wordLayout.mCharactersLayoutInfo.begin() + position, wordLayout.mCharactersLayoutInfo.begin() + position + numberOfCharacters ); + + // Some characters have been removed from the word. Update the layout info is needed. + UpdateLayoutInfo( wordLayout ); +} + +void SplitWord( const std::size_t position, + WordLayoutInfo& firstWordLayoutInfo, + WordLayoutInfo& lastWordLayoutInfo ) +{ + // Splits a word in two. + // It moves characters from the first part of the word to the last one. + + // early returns + if( 0 == position ) + { + // the whole word goes to the last part of the word. + lastWordLayoutInfo = firstWordLayoutInfo; + + firstWordLayoutInfo = WordLayoutInfo(); + + return; + } + + if( position == firstWordLayoutInfo.mCharactersLayoutInfo.size() ) + { + // the whole word goes to the first part of the word. + + // Just delete whatever there is in the last part of the word. + lastWordLayoutInfo = WordLayoutInfo(); + + return; + } + + // Initialize output data structures. + + // Layout info + lastWordLayoutInfo = WordLayoutInfo(); + + // Split layout info. + + // Insert characters from the given index 'position' to the end. + lastWordLayoutInfo.mCharactersLayoutInfo.insert( lastWordLayoutInfo.mCharactersLayoutInfo.end(), + firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() ); + + // Delete characters from the first part of the word. + firstWordLayoutInfo.mCharactersLayoutInfo.erase( firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() ); + + // Update the layout info of both new words. + UpdateLayoutInfo( firstWordLayoutInfo ); + UpdateLayoutInfo( lastWordLayoutInfo ); +} + +void MergeWord( WordLayoutInfo& firstWordLayoutInfo, + const WordLayoutInfo& lastWordLayoutInfo ) +{ + // Merges two given words. + + // Early returns. + if( lastWordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + // nothing to do + return; + } + + if( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + // copy last to first + + firstWordLayoutInfo = lastWordLayoutInfo; + + return; + } + + if( ( NoSeparator != firstWordLayoutInfo.mType ) || ( NoSeparator != lastWordLayoutInfo.mType ) ) + { + // Do not merge white spaces or new line characters. + DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words." ); + } + + // Merge layout info + firstWordLayoutInfo.mCharactersLayoutInfo.insert( firstWordLayoutInfo.mCharactersLayoutInfo.end(), + lastWordLayoutInfo.mCharactersLayoutInfo.begin(), + lastWordLayoutInfo.mCharactersLayoutInfo.end() ); + + // Update the word layout info. + UpdateSize( firstWordLayoutInfo.mSize, lastWordLayoutInfo.mSize ); + firstWordLayoutInfo.mAscender = std::max( firstWordLayoutInfo.mAscender, lastWordLayoutInfo.mAscender ); +} + +CharacterLayoutInfo GetFirstCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo ) +{ + CharacterLayoutInfo layoutInfo; + + if( !wordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + layoutInfo = *wordLayoutInfo.mCharactersLayoutInfo.begin(); + } + + return layoutInfo; +} + +CharacterLayoutInfo GetLastCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo ) +{ + CharacterLayoutInfo layoutInfo; + + if( !wordLayoutInfo.mCharactersLayoutInfo.empty() ) + { + layoutInfo = *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 ); + } + + return layoutInfo; +} + +void CollectTextActors( std::vector& textActors, const WordLayoutInfo& word, const std::size_t characterIndexBegin, const std::size_t characterIndexEnd ) +{ + for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin() + characterIndexBegin, + characterEndIt = word.mCharactersLayoutInfo.begin() + characterIndexEnd; + characterIt != characterEndIt; + ++characterIt ) + { + const CharacterLayoutInfo& characterLayout( *characterIt ); + + if( characterLayout.mTextActor ) + { + textActors.push_back( characterLayout.mTextActor ); + } + } +} + +void CollectTextActorsFromWords( std::vector& textActors, const WordGroupLayoutInfo& group, const std::size_t wordIndexBegin, const std::size_t wordIndexEnd ) +{ + for( WordLayoutInfoContainer::const_iterator wordIt = group.mWordsLayoutInfo.begin() + wordIndexBegin, wordEndIt = group.mWordsLayoutInfo.begin() + wordIndexEnd; + wordIt != wordEndIt; + ++wordIt ) + { + const WordLayoutInfo& word( *wordIt ); + + for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end(); + characterIt != characterEndIt; + ++characterIt ) + { + const CharacterLayoutInfo& characterLayout( *characterIt ); + + if( characterLayout.mTextActor ) + { + textActors.push_back( characterLayout.mTextActor ); + } + } + } +} + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/internal/controls/text-view/text-view-word-processor.h b/dali-toolkit/internal/controls/text-view/text-view-word-processor.h new file mode 100644 index 0000000..3ef6d26 --- /dev/null +++ b/dali-toolkit/internal/controls/text-view/text-view-word-processor.h @@ -0,0 +1,162 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_PROCESSOR_H__ +#define __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_PROCESSOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include "text-view-impl.h" +#include "text-view-processor-types.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace TextViewProcessor +{ + +/** + * Creates a data structure with info to layout the word, and data structures with useful info to modify the layout data structure if characters are added or removed. + * + * @param[in] word The styled word. + * @param[out] wordLayoutInfo Layout info for all characters of the word. + */ +void CreateWordTextInfo( const MarkupProcessor::StyledTextArray& word, + TextViewProcessor::WordLayoutInfo& wordLayoutInfo ); + +/** + * Removes a given number of characters from the given word. + * + * It calls the RemoveCharactersFromWord() function to remove characters from the word. + * + * If the word is a white space \e mergeWords will return \e true and \e textInfoMergeIndicesBegin and \e textInfoMergeIndicesEnd will be set to merge the two adjacent words. + * If the word is a new line character \e mergeLines will return \e true and \e textInfoMergeIndicesBegin and \e textInfoMergeIndicesEnd will be set to merge the two lines. + * + * @param[in,out] relayoutData Natural size (metrics), layout, text-actor info. + * @param[in] numberOfCharacters The number of characters to be deleted. + * @param[out] mergeWords Whether adjacent words need to be merged. + * @param[out] mergeLines Whether current line need to be merged with the next one. + * @param[in,out] textInfoIndicesBegin Indices to the line, word and characters from where to delete characters. It returns from where words need to be removed. + * @param[out] textInfoIndicesEnd If lines or words need to be merged it returns info to delete them (If a word is merged, it has to be removed. Equal for lines). + * @param[out] textInfoMergeIndicesBegin The indices to the first part of the line, group and word to be merged. + * @param[out] textInfoMergeIndicesEnd The indices to the last part of the line, group and word to be merged. + * @param[in,out] groupLayout Layout info of the group of words where the word is located. + * @param[out] removedTextActors Stores handles of temoved text-actors. + */ +void RemoveCharactersFromWordInfo( TextView::RelayoutData& relayoutData, + std::size_t numberOfCharacters, + bool& mergeWords, + bool& mergeLines, + TextViewProcessor::TextInfoIndices& textInfoIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoIndicesEnd, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin, + TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd, + TextViewProcessor::WordGroupLayoutInfo& groupLayout, + std::vector& removedTextActors ); +/** + * Removes a given number of characters from the given word. + * + * @pre \e positon and \e position + \e numberOfCharacters can't exceed the bounds of the word. + * + * @param[in] position Character index within the word with the starting position to be deleted. + * @param[in] numberOfCharacters The number of characters to be deleted. + * @param[in,out] wordLayout The input is the layout info of the word. The output is the layout info of the word without the removed characters. + */ +void RemoveCharactersFromWord( std::size_t position, + std::size_t numberOfCharacters, + WordLayoutInfo& wordLayout ); + +/** + * Splits a word in two. + * + * Removes part of the text from the input word and creates a new word with the removed text. + * + * i.e. The result of split 'word' by the position 3 would be 'wor' and 'd'. + * + * @note It deletes whatever there is in the last part of the word. + * + * @param[in] position Character index where to split the word. + * @param[in,out] firstWordLayoutInfo The input is the layout info of the given word. The output is the first part of the input word (from the character \e 0 to the character \e position - \e 1). + * @param[out] lastWordLayoutInfo Layout info of the last part of the given word ( from the character \e position to the end of the word). + */ +void SplitWord( std::size_t position, + WordLayoutInfo& firstWordLayoutInfo, + WordLayoutInfo& lastWordLayoutInfo ); + +/** + * Merges the two given words by adding characters of the last word to the firs one. + * + * @note Does nothing if last part of the word is empty. + * @note If the first part of the word is empty it just copy the last part to it. + * @note It asserts if the first or the last word is a word separator (white space) or a line separator (new line character) + * + * @param[in,out] firstWordLayoutInfo The input is the layout info of the first word. The output is the layout info of the merged word. + * @param[in] lastWordLayoutInfo Layout info of the last word. + */ +void MergeWord( WordLayoutInfo& firstWordLayoutInfo, + const WordLayoutInfo& lastWordLayoutInfo ); + +/** + * Retrieves the layout information of the first character of the given word. + * + * @param[in] wordLayoutInfo The word layout. + * + * @return Layout information of the first character of the word. + */ +CharacterLayoutInfo GetFirstCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo ); + +/** + * Retrieves the layout information of the last character of the given word. + * + * @param[in] wordLayoutInfo The word layout. + * + * @return Layout information of the last character of the word. + */ +CharacterLayoutInfo GetLastCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo ); + +/** + * Collects text-actors from the given word, within the given indices, and stores them into the text-actor vector. + * + * @param[out] textActors Stores the text-actors of the given word. + * @param[in] characterIndexBegin The first text-actor to be stored. + * @param[in] characterIndexEnd The last text-actor to be stored. + */ +void CollectTextActors( std::vector& textActors, const WordLayoutInfo& word, std::size_t characterIndexBegin, std::size_t characterIndexEnd ); + +/** + * Collects text-actors from the given group of words, within the given indices, and stores them into the text-actor vector. + * + * @param[out] textActors Stores the text-actors of the given group of words. + * @param[in] group The group of words. + * @param[in] wordIndexBegin Index to the first word. + * @param[in] wordIndexEnd Index to the last word. + */ +void CollectTextActorsFromWords( std::vector& textActors, const WordGroupLayoutInfo& group, std::size_t wordIndexBegin, std::size_t wordIndexEnd ); + +} //namespace TextViewProcessor + +} //namespace Internal + +} //namespace Toolkit + +} //namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TEXT_VIEW_WORD_PROCESSOR_H__ diff --git a/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.cpp b/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.cpp new file mode 100644 index 0000000..64160ed --- /dev/null +++ b/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.cpp @@ -0,0 +1,357 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "tool-bar-impl.h" + +// INTERNAL INCLUDES +#include +#include + +// EXTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +BaseHandle Create() +{ + return Toolkit::ToolBar::New(); +} + +TypeRegistration mType( typeid(Toolkit::ToolBar), typeid(Toolkit::Control), Create ); + +const float DEFAULT_RELATIVE_SIZE( 0.1f ); +const Toolkit::Alignment::Type DEFAULT_ALIGNMENT( Toolkit::Alignment::HorizontalLeft ); +} // namespace + +Toolkit::ToolBar ToolBar::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr< ToolBar > internalToolBar = new ToolBar(); + + // Pass ownership to Toolkit::View + Toolkit::ToolBar toolBar( *internalToolBar ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalToolBar->Initialize(); + + return toolBar; +} + +void ToolBar::SetBackground( Actor background ) +{ + Lock lock( mInitializing ); + + // ToolBar image + background.SetParentOrigin( Dali::ParentOrigin::TOP_CENTER ); + background.SetAnchorPoint( Dali::AnchorPoint::TOP_CENTER ); + background.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + + RenderableActor renderableActor = RenderableActor::DownCast( background ); + if ( renderableActor ) + { + renderableActor.SetSortModifier( 1.f ); + } + + Self().Add( background ); +} + +void ToolBar::AddControl( Actor control, float relativeSize, Toolkit::Alignment::Type alignment, const Toolkit::Alignment::Padding& padding ) +{ + // Work out index and update bases and offsets for further insertions. + unsigned int index = 0; + switch( alignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + index = mLeftOffset; + ++mLeftOffset; + ++mCenterBase; + ++mRightBase; + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + index = mCenterBase + mCenterOffset; + ++mCenterOffset; + ++mRightBase; + break; + } + case Toolkit::Alignment::HorizontalRight: + { + index = mRightBase - mRightOffset; + ++mRightBase; + ++mRightOffset; + break; + } + default: + { + DALI_ASSERT_ALWAYS( false ); + } + } + + // Create a new column for the new control. + mLayout.InsertColumn( index ); + + // Create an alignment container where to place the control. + Toolkit::Alignment alignmentContainer = Toolkit::Alignment::New( alignment ); + alignmentContainer.SetScaling( Toolkit::Alignment::ScaleToFill ); + alignmentContainer.SetPadding( padding ); + alignmentContainer.Add( control ); + + // Insert the control in the table view. + mLayout.AddChild( alignmentContainer, Toolkit::TableView::CellPosition( 0, index ) ); + mLayout.SetRelativeWidth( index, relativeSize ); + + // Relate control and alignmentContainer in order to allow removing controls. + mControls[control] = alignmentContainer; + + // Update accumulated relative space. + mAccumulatedRelativeSpace += relativeSize; + + // Update spaces between left, center and right groups of controls. + switch( alignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + mLeftRelativeSpace -= relativeSize; + if ( mLeftRelativeSpace < 0.f ) + { + mLeftRelativeSpace = 0.f; + } + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + mLeftRelativeSpace -= 0.5f * relativeSize; + if ( mLeftRelativeSpace < 0.f ) + { + mLeftRelativeSpace = 0.f; + } + mRightRelativeSpace -= 0.5f * relativeSize; + if ( mRightRelativeSpace < 0.f ) + { + mRightRelativeSpace = 0.f; + } + break; + } + case Toolkit::Alignment::HorizontalRight: + { + mRightRelativeSpace -= relativeSize; + if ( mRightRelativeSpace < 0.f ) + { + mRightRelativeSpace = 0.f; + } + break; + } + default: + { + DALI_ASSERT_ALWAYS( false ); + } + } + + mLayout.SetRelativeWidth( mLeftOffset, mLeftRelativeSpace ); + mLayout.SetRelativeWidth( mCenterBase + mCenterOffset, mRightRelativeSpace ); +} + +void ToolBar::RemoveControl( Actor control ) +{ + Toolkit::TableView::CellPosition position; + + // Find the alignment where the control is placed. + std::map::iterator it = mControls.find( control ); + + if( ( it != mControls.end() ) && ( mLayout.FindChildPosition( it->second, position ) ) ) + { + // Update accumulated relative space. + mAccumulatedRelativeSpace -= mLayout.GetRelativeWidth( position.columnIndex ); + + // Update spaces between left, center and right groups of controls. + if( 1.0 > mAccumulatedRelativeSpace ) + { + Toolkit::Alignment::Type alignment = Toolkit::Alignment::HorizontalLeft; + if( position.columnIndex < mLeftOffset ) + { + alignment = Toolkit::Alignment::HorizontalLeft; + } + else if( ( position.columnIndex > mLeftOffset ) && ( position.columnIndex < mCenterBase + mCenterOffset ) ) + { + alignment = Toolkit::Alignment::HorizontalCenter; + } + else if( position.columnIndex > mCenterBase + mCenterOffset ) + { + alignment = Toolkit::Alignment::HorizontalRight; + } + else + { + DALI_ASSERT_ALWAYS( false ); + } + + float relativeSize = mLayout.GetRelativeWidth( position.columnIndex ); + + switch( alignment ) + { + case Toolkit::Alignment::HorizontalLeft: + { + mLeftRelativeSpace += relativeSize; + if ( mLeftRelativeSpace < 0.f ) + { + mLeftRelativeSpace = 0.f; + } + break; + } + case Toolkit::Alignment::HorizontalCenter: + { + mLeftRelativeSpace += 0.5f * relativeSize; + if ( mLeftRelativeSpace < 0.f ) + { + mLeftRelativeSpace = 0.f; + } + mRightRelativeSpace += 0.5f * relativeSize; + if ( mRightRelativeSpace < 0.f ) + { + mRightRelativeSpace = 0.f; + } + break; + } + case Toolkit::Alignment::HorizontalRight: + { + mRightRelativeSpace += relativeSize; + if ( mRightRelativeSpace < 0.f ) + { + mRightRelativeSpace = 0.f; + } + break; + } + default: + { + DALI_ASSERT_ALWAYS( false ); + } + } + mLayout.SetRelativeWidth( mLeftOffset, mLeftRelativeSpace ); + mLayout.SetRelativeWidth( mCenterBase + mCenterOffset, mRightRelativeSpace ); + } + + // Remove alignment as parent of control. + it->second.Remove( control ); + + // Remove the relationship between control and alignment. + mControls.erase( it ); + + // Remove column from tableview. + mLayout.DeleteColumn( position.columnIndex ); + + // Update bases and offsets. + if( position.columnIndex < mCenterBase ) + { + // Control is on the left side. Decrease left offset and center and right bases. + --mLeftOffset; + --mCenterBase; + --mRightBase; + } + else if( position.columnIndex < mCenterBase + mCenterOffset ) + { + // Control is on the center side. Decrease center offset and right base. + --mCenterOffset; + --mRightBase; + } + else + { + // Control is on the right side. Decrease right base and right offset. + --mRightBase; + --mRightOffset; + } + } +} + +ToolBar::ToolBar() +: ControlImpl( false ), // doesn't require touch events + mLayout(), + mLeftOffset( 0 ), + mCenterBase( 1 ), + mCenterOffset( 0 ), + mRightBase( 2 ), + mRightOffset( 0 ), + mLeftRelativeSpace( 0.5f ), + mRightRelativeSpace( 0.5f ), + mAccumulatedRelativeSpace( 0.f ), + mInitializing( false ), + mControls() +{ +} + +ToolBar::~ToolBar() +{ +} + +void ToolBar::OnInitialize() +{ + Lock lock( mInitializing ); + + // Layout + mLayout = Toolkit::TableView::New( 1, 1 ); + mLayout.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + Self().Add( mLayout ); + + // Add two default actors to create spaces between controls grouped on the left, center and right. + Actor leftSpace = Actor::New(); + Actor rightSpace = Actor::New(); + mLayout.AddChild( leftSpace, Toolkit::TableView::CellPosition( 0, 0 ) ); + mLayout.AddChild( rightSpace, Toolkit::TableView::CellPosition( 0, 1 ) ); + mLayout.SetRelativeWidth( 0, mLeftRelativeSpace ); + mLayout.SetRelativeWidth( 1, mRightRelativeSpace ); +} + +void ToolBar::OnControlChildAdd(Actor& child) +{ + if( !mInitializing ) + { + // An actor is being added through the Actor's API. + + // Remove child from tool bar actor and insert it in table view with some 'default' values + if ( child && child.GetParent() ) + { + child.GetParent().Remove( child ); + } + + AddControl( child, DEFAULT_RELATIVE_SIZE, DEFAULT_ALIGNMENT, Toolkit::ToolBar::DEFAULT_PADDING ); + } + + // No OnControlChildRemove method required because Actors are added to the mLayout table view, so if an + // actor is removed using the Actor::RemoveChild method it will not remove anything because the + // actor is in mLayout not in Self(). +} + +void ToolBar::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + Relayout( mLayout, size, container ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.h b/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.h new file mode 100644 index 0000000..9a58aec --- /dev/null +++ b/dali-toolkit/internal/controls/tool-bar/tool-bar-impl.h @@ -0,0 +1,168 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_TOOL_BAR_H__ +#define __DALI_TOOLKIT_INTERNAL_TOOL_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class ToolBar; + +namespace Internal +{ + +/** + * ToolBar is a control to create a tool bar. + * @see Dali::Toolkit::ToolBar for more details. + */ +class ToolBar : public ControlImpl +{ +public: + + /** + * Create an initialized ToolBar. + * @return A handle to a newly allocated Dali resource. + */ + static Toolkit::ToolBar New(); + + /** + * @copydoc Dali::Toolkit::View::SetBackground() + */ + void SetBackground( Actor background ); + + /** + * @copydoc Dali::Toolkit::View::AddControl() + */ + void AddControl( Dali::Actor control, float relativeSize, Toolkit::Alignment::Type alignment, const Toolkit::Alignment::Padding& padding ); + + /** + * @copydoc Dali::Toolkit::View::RemoveControl() + */ + void RemoveControl( Dali::Actor control ); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + + /** + * Adds a control using some default values (the control uses 10% of the tool bar space and is placed on the left group). + * @param child The control to be added. + * + * @see Toolkit::ControlImpl::OnControlChildAdd() + */ + virtual void OnControlChildAdd(Actor& child); + + /** + * Called when the tool-bar is relaid out. + * @param[in] size The size allocated. + * @param[in/out] container the container to put actors not handled. + */ + virtual void OnRelaidOut( Vector2 size, ActorSizeContainer& container ); + +private: + /** + */ + class Lock + { + public: + /** + * Constructor, sets the lock boolean + */ + Lock( bool& lock ) + : mLock( lock ) + { + mLock = true; + } + + /** + * Destructor, releases lock boolean + */ + ~Lock() + { + mLock = false; + } + private: + bool& mLock; + }; + + /** + * Constructor. + * It initializes ToolBar members. + */ + ToolBar(); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~ToolBar(); + +private: + Toolkit::TableView mLayout; ///< TableView used to place controls. + unsigned int mLeftOffset; ///< Offset index where the next control is going to be added in the left group. + unsigned int mCenterBase; ///< Base index where the first control of the center group is placed. + unsigned int mCenterOffset; ///< Offset index where the next control is going to be added in the center group. + unsigned int mRightBase; ///< Base index where the first control of the right group is placed. + unsigned int mRightOffset; ///< Offset index where the next control is going to be added in the right group. + float mLeftRelativeSpace; ///< Relative space between left and center groups of controls. + float mRightRelativeSpace; ///< Relative space between center and right groups of controls. + float mAccumulatedRelativeSpace; ///< Stores the total percentage space used by controls. + bool mInitializing; ///< Allows the use of Actor's API to add controls. + + std::map mControls; ///< Stores a relationship between controls and their alignments used to place them inside the table view. +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::ToolBar& GetImpl( Toolkit::ToolBar& toolBar ) +{ + DALI_ASSERT_ALWAYS( toolBar ); + + Dali::RefObject& handle = toolBar.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::ToolBar& GetImpl( const Toolkit::ToolBar& toolBar ) +{ + DALI_ASSERT_ALWAYS( toolBar ); + + const Dali::RefObject& handle = toolBar.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_TOOL_BAR_H__ diff --git a/dali-toolkit/internal/controls/view/view-impl.cpp b/dali-toolkit/internal/controls/view/view-impl.cpp new file mode 100644 index 0000000..a756f94 --- /dev/null +++ b/dali-toolkit/internal/controls/view/view-impl.cpp @@ -0,0 +1,342 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "view-impl.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // to register type +{ + +BaseHandle Create() +{ + return Toolkit::View::New(); +} + +TypeRegistration typeRegistration( typeid(Toolkit::View), typeid(Toolkit::Control), Create ); + +SignalConnectorType signalConnector1( typeRegistration, Toolkit::View::SIGNAL_ORIENTATION_ANIMATION_START , &View::DoConnectSignal ); + +} + +namespace +{ + +const float ROTATION_ANIMATION_DURATION = 0.5f; + +} + +Toolkit::View View::New( bool fullscreen ) +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< View > internalView = new View(fullscreen); + + // Pass ownership to CustomActor handle + Toolkit::View view( *internalView ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + internalView->Initialize(); + + return view; +} + +Layer View::GetContentLayer( unsigned int index ) const +{ + // Returns the layer stored in the layer map. + Layer layer; + + LayerConstIt it = mContentLayers.find( index ); + + if( it != mContentLayers.end() ) + { + layer = it->second; + } + + return layer; +} + +unsigned int View::AddContentLayer( Layer layer ) +{ + // layer must exist. + DALI_ASSERT_ALWAYS( layer ); + + unsigned int index = mNextLayerIndex; + LayerIt it = FindLayer( layer ); + + if( it == mContentLayers.end() ) + { + // Add layer to the custom actor. + Self().Add( layer ); + + // Store the layer. + mContentLayers[mNextLayerIndex] = layer; + + // Increase the index. + ++mNextLayerIndex; + } + + return index; +} + +void View::RemoveContentLayer( Layer layer ) +{ + // Check if layer was added in this view. + LayerIt it = FindLayer( layer ); + if( it != mContentLayers.end() ) + { + // Remove layer from custom actor. + Self().Remove( layer ); + + // Remove layer from layer map. + mContentLayers.erase( it ); + } +} + +Layer View::GetBackgroundLayer() const +{ + return mBackgroundLayer; +} + +void View::SetBackground( ImageActor backgroundImage ) +{ + // Create background layer if doesn't exist. + + if( !mBackgroundLayer ) + { + mBackgroundLayer = Layer::New(); + + mBackgroundLayer.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + mBackgroundLayer.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + + // Add background layer to custom actor. + Self().Add( mBackgroundLayer ); + + // Drop the background layer + + DALI_ASSERT_ALWAYS( mBackgroundLayer.OnStage() ); // We need to be on-stage to drop the layer + mBackgroundLayer.LowerToBottom(); + } + else + { + // It removes the old background + if( 0 < mBackgroundLayer.GetChildCount() ) + { + mBackgroundLayer.Remove( mBackgroundLayer.GetChildAt(0) ); + } + } + + backgroundImage.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + Constraint constraint = Constraint::New( + Actor::SCALE, + LocalSource( Actor::SIZE ), + ParentSource( Actor::SIZE ), + ScaleToFillXYKeepAspectRatioConstraint() ); + backgroundImage.ApplyConstraint( constraint ); + mBackgroundLayer.Add( backgroundImage ); +} + +void View::SetOrientationFunction( Degree portrait, Degree landscale, Degree portraitInverse, Degree landscapeInverse ) +{ + mOrientationFunction[View::PORTRAIT] = portrait; + mOrientationFunction[View::LANDSCAPE] = landscale; + mOrientationFunction[View::PORTRAIT_INVERSE] = portraitInverse; + mOrientationFunction[View::LANDSCAPE_INVERSE] = landscapeInverse; +} + +void View::OrientationChanged( Dali::Orientation orientation ) +{ + // Nothing to do if orientation doesn't really change. + if ( orientation.GetDegrees() == mOrientation || !mAutoRotateEnabled ) + { + return; + } + + mOrientation = orientation.GetDegrees(); + + // has parent so we expect it to be on stage + mRotateAnimation = Animation::New( ROTATION_ANIMATION_DURATION ); + mRotateAnimation.RotateTo( Self(), Degree( -orientation.GetDegrees() ), Vector3::ZAXIS, AlphaFunctions::EaseOut ); + + // Resize the view + if( mFullScreen ) + { + const Vector2& stageSize( Stage::GetCurrent().GetSize() ); + const Vector3& currentSize( Self().GetCurrentSize() ); + + float minSize = std::min( stageSize.width, stageSize.height ); + float maxSize = std::max( stageSize.width, stageSize.height ); + + Vector3 targetSize; + View::Orientation viewOrientation = DegreeToViewOrientation( Degree( orientation.GetDegrees() ) ); + switch( viewOrientation ) + { + case View::PORTRAIT: // Fallthrough + case View::PORTRAIT_INVERSE: + targetSize = Vector3( minSize, maxSize, currentSize.depth ); + break; + case View::LANDSCAPE: // Fallthrough + case View::LANDSCAPE_INVERSE: + targetSize = Vector3( maxSize, minSize, currentSize.depth ); + break; + default: + DALI_ASSERT_ALWAYS( false ); + } + + // if we linearly resize from portrait to landscape halfway through the animation + // we get size which is square between the both. This would cause a square image to grow + // if it is fitted to be 100% of view size. Therefore we do a nonlinear size animation + // where we shrink faster + // which one grows + if( targetSize.width > currentSize.width ) + { + // width grows, shrink height faster + Vector3 shrink( currentSize );shrink.height = targetSize.height; + mRotateAnimation.Resize( Self(), shrink, AlphaFunctions::EaseOut, 0.0f, ROTATION_ANIMATION_DURATION * 0.5f ); + mRotateAnimation.Resize( Self(), targetSize, AlphaFunctions::EaseIn, 0.0f, ROTATION_ANIMATION_DURATION ); + } + else + { + // height grows, shrink width faster + Vector3 shrink( currentSize );shrink.width = targetSize.width; + mRotateAnimation.Resize( Self(), shrink, AlphaFunctions::EaseOut, 0.0f, ROTATION_ANIMATION_DURATION * 0.5f ); + mRotateAnimation.Resize( Self(), targetSize, AlphaFunctions::EaseIn, 0.0f, ROTATION_ANIMATION_DURATION ); + } + } + + mRotateAnimation.SetDestroyAction( Animation::Bake ); + + Toolkit::View handle( GetOwner() ); + mOrientationAnimationStartedSignalV2.Emit( handle, mRotateAnimation, orientation ); + + mRotateAnimation.Play(); +} + +void View::SetAutoRotate( bool enabled ) +{ + mAutoRotateEnabled = enabled; +} + +Toolkit::View::OrientationAnimationStartedSignalV2& View::OrientationAnimationStartedSignal() +{ + return mOrientationAnimationStartedSignalV2; +} + +bool View::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::View view = Toolkit::View::DownCast(handle); + + if( Toolkit::View::SIGNAL_ORIENTATION_ANIMATION_START == signalName ) + { + view.OrientationAnimationStartedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +View::View(bool fullscreen) +: ControlImpl( false ), // doesn't require touch events + mOrientation( -1 ), + mFullScreen(fullscreen), + mContentLayers(), + mNextLayerIndex( 0 ), + mOrientationFunction(), + mAutoRotateEnabled( true ) +{ + mOrientationFunction[View::PORTRAIT] = 0.f; + mOrientationFunction[View::LANDSCAPE] = 90.f; + mOrientationFunction[View::PORTRAIT_INVERSE] = 180.f; + mOrientationFunction[View::LANDSCAPE_INVERSE] = 270.f; +} + +View::~View() +{ +} + +void View::OnInitialize() +{ + Self().SetAnchorPoint( AnchorPoint::CENTER ); + Self().SetParentOrigin( ParentOrigin::CENTER ); + + if( mFullScreen ) + { + Self().SetSize( Stage::GetCurrent().GetSize() ); + } +} + +View::Orientation View::DegreeToViewOrientation( Degree degree ) +{ + View::Orientation orientation = PORTRAIT; + + if( fabsf( mOrientationFunction[PORTRAIT] - degree ) <= GetRangedEpsilon( mOrientationFunction[PORTRAIT], degree ) ) + { + orientation = PORTRAIT; + } + else if( fabsf( mOrientationFunction[LANDSCAPE] - degree ) <= GetRangedEpsilon( mOrientationFunction[LANDSCAPE], degree ) ) + { + orientation = LANDSCAPE; + } + else if( fabsf( mOrientationFunction[PORTRAIT_INVERSE] - degree ) <= GetRangedEpsilon( mOrientationFunction[PORTRAIT_INVERSE], degree ) ) + { + orientation = PORTRAIT_INVERSE; + } + else if( fabsf( mOrientationFunction[LANDSCAPE_INVERSE] - degree ) <= GetRangedEpsilon( mOrientationFunction[LANDSCAPE_INVERSE], degree ) ) + { + orientation = LANDSCAPE_INVERSE; + } + + return orientation; +} + +View::LayerIt View::FindLayer( Layer layer ) +{ + for( LayerIt it = mContentLayers.begin(); it != mContentLayers.end(); ++it ) + { + if(layer == it->second) + { + return it; + } + } + + return mContentLayers.end(); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/controls/view/view-impl.h b/dali-toolkit/internal/controls/view/view-impl.h new file mode 100644 index 0000000..ea0a4c5 --- /dev/null +++ b/dali-toolkit/internal/controls/view/view-impl.h @@ -0,0 +1,205 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_VIEW_H__ +#define __DALI_TOOLKIT_INTERNAL_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class View; + +namespace Internal +{ + +/** + * View is a control to add layers and a background. + * @see Dali::Toolkit::View for more details. + */ +class View : public ControlImpl +{ +private: + typedef std::map LayerContainer; + typedef std::map::iterator LayerIt; + typedef std::map::const_iterator LayerConstIt; + + /** + * Orientation declaration used internally to rotate the view. + * The angles associated with each enum value could be changed with the SetOrientationFunction method. + */ + enum Orientation + { + PORTRAIT, ///< portrait orientation. + LANDSCAPE, ///< landscape orientation. + PORTRAIT_INVERSE, ///< portrait inverse orientation. + LANDSCAPE_INVERSE ///< landscape inverse orientation. + }; + +public: + + /** + * Create an initialized View. + * @param fullscreen If true, the view's size is going to be set with the Dali::Stage size. Otherwise a size must be provided. + * @return A handle to a newly allocated Dali resource. + */ + static Toolkit::View New( bool fullscreen ); + + /** + * @copydoc Dali::Toolkit::View::GetContentLayer() + */ + Layer GetContentLayer( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::View::AddContentLayer() + */ + unsigned int AddContentLayer( Layer layer ); + + /** + * @copydoc Dali::Toolkit::View::RemoveContentLayer() + */ + void RemoveContentLayer( Layer layer ); + + /** + * @copydoc Dali::Toolkit::View::GetBackgroundLayer() + */ + Layer GetBackgroundLayer() const; + + /** + * @copydoc Dali::Toolkit::View::SetBackground() + */ + void SetBackground( ImageActor image ); + + /** + * @copydoc Dali::Toolkit::View::SetOrientationFunction() + */ + void SetOrientationFunction( Degree portrait, Degree landscale, Degree portraitInverse, Degree landscapeInverse ); + + /** + * @copydoc Dali::Toolkit::View::OrientationChanged() + * + */ + void OrientationChanged( Dali::Orientation orientation ); + + /** + * @copydoc Dali::Toolkit::View::SetAutoRotate() + * + */ + void SetAutoRotate( bool enabled ); + +public: + + /** + * @copydoc Dali::Toolkit::View::AnimationStartedSignalOrientation() + */ + Toolkit::View::OrientationAnimationStartedSignalV2& OrientationAnimationStartedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +private: // From ControlImpl + + /** + * @copydoc Toolkit::Control::OnInitialize() + */ + virtual void OnInitialize(); + +private: + + + /** + * Constructor. + * It initializes View members. + * It initializes orientations as follows: portrait 0, landscape 90, portrait inverse 180, landscape inverse 270. + * @param fullscreen If true, the view's size is going to be set with the Dali::Stage size. Otherwise a size must be provided. + */ + View(bool fullscreen); + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~View(); + + /** + * Return an orientation for the given angle in degrees. + * @param degree angle in degrees. + * @return An internal orientation. + */ + View::Orientation DegreeToViewOrientation( Degree degree ); + + /** + * Find a layer in the layer container. Non const method + */ + LayerIt FindLayer( Layer layer ); + +private: + int mOrientation; ///< Stores the given orientation in degrees. + bool mFullScreen; ///< Stores if the view is fullscreen or not. + LayerContainer mContentLayers; ///< Layer container. + unsigned int mNextLayerIndex; ///< Next index to be used when a layer is added. + Layer mBackgroundLayer; ///< The background layer. + Animation mRotateAnimation; ///< The animation which rotates the view (and all layers added to it) + float mOrientationFunction[4]; ///< The orientation function used to transform from degrees to the internal orientation. + bool mAutoRotateEnabled; ///< Whether the view rotates if the OrientationChanged method is called. + + Toolkit::View::OrientationAnimationStartedSignalV2 mOrientationAnimationStartedSignalV2; +}; + +} // namespace Internal + + +// Helpers for public-api forwarding methods + +inline Toolkit::Internal::View& GetImpl( Toolkit::View& view ) +{ + DALI_ASSERT_ALWAYS( view ); + + Dali::RefObject& handle = view.GetImplementation(); + + return static_cast( handle ); +} + +inline const Toolkit::Internal::View& GetImpl( const Toolkit::View& view ) +{ + DALI_ASSERT_ALWAYS( view ); + + const Dali::RefObject& handle = view.GetImplementation(); + + return static_cast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_VIEW_H__ diff --git a/dali-toolkit/internal/factory/localized-control-factory-impl.cpp b/dali-toolkit/internal/factory/localized-control-factory-impl.cpp new file mode 100644 index 0000000..cdf9819 --- /dev/null +++ b/dali-toolkit/internal/factory/localized-control-factory-impl.cpp @@ -0,0 +1,120 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "localized-control-factory-impl.h" + +// INTERNAL INCLUDES +#include +#include + +// EXTERNAL INCLUDES +#include + +using std::string; +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + + +} + +LocalizedControlFactory::LocalizedControlFactory() +: mObjectEntries(), + mSignalsConnected( false ) +{ +} + +LocalizedControlFactory::~LocalizedControlFactory() +{ +} + +Dali::Toolkit::TextView LocalizedControlFactory::CreateLocalizedTextView( const std::string& textID, const std::string& textDomain, const std::string& textViewTheme ) +{ + if( !mSignalsConnected ) + { + Stage::GetCurrent().GetObjectRegistry().ObjectDestroyedSignal().Connect( this, &LocalizedControlFactory::OnObjectDestruction ); + Adaptor::Get().LanguageChangedSignal().Connect( this, &LocalizedControlFactory::OnLanguageChanged ); + mSignalsConnected = true; + } + + const string& localizedText = dgettext(textDomain.c_str(), textID.c_str()); + Dali::Toolkit::TextView textView = Dali::Toolkit::TextView::New(); + textView.SetText(localizedText); + + LocalisedStringInfo info(textID, textDomain, textViewTheme); + + mObjectEntries[textView.GetObjectPtr()] = info; + + return textView; +} + +void LocalizedControlFactory::OnObjectDestruction( const Dali::RefObject* objectPointer ) +{ + if(!mObjectEntries.empty()) + { + //Needs optimization. All the destructed objects are currently checked for existence in entries. + mObjectEntries.erase(objectPointer); + } +} + +void LocalizedControlFactory::OnLanguageChanged( Dali::Adaptor& adaptor) +{ + if(!mObjectEntries.empty()) + { + ObjectEntriesIterator iter = mObjectEntries.begin(); + ObjectEntriesIterator iterEnd = mObjectEntries.end(); + while(iter != iterEnd) + { + RefObject* refObjectPtr = const_cast (iter->first); + BaseHandle handle(static_cast(refObjectPtr)); + LocalisedStringInfo info = iter->second; + + const string& localizedText = dgettext(info.textDomain.c_str(), info.textID.c_str()); + + Toolkit::TextView textView = Dali::Toolkit::TextView::DownCast( handle ); + + if(textView) + { + textView.SetText( localizedText ); + } + else + { + DALI_ASSERT_ALWAYS(false && "Corrupt TextView internal pointer in entries!"); + } + + ++iter; + } + } +} + + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/factory/localized-control-factory-impl.h b/dali-toolkit/internal/factory/localized-control-factory-impl.h new file mode 100644 index 0000000..fa8b24c --- /dev/null +++ b/dali-toolkit/internal/factory/localized-control-factory-impl.h @@ -0,0 +1,138 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_LOCALIZED_CONTROL_FACTORY_H__ +#define __DALI_TOOLKIT_INTERNAL_LOCALIZED_CONTROL_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * @copydoc Toolkit::LocalizedControlFactory + */ +class LocalizedControlFactory : public Dali::BaseObject, public ConnectionTracker +{ +public: + + /** + * Structure used to store/retrieve localisation info. + */ + struct LocalisedStringInfo + { + LocalisedStringInfo() + { + } + + LocalisedStringInfo(std::string id, std::string domain, std::string theme) + : textID(id), + textDomain(domain), + textViewTheme(theme) + { + + } + + std::string textID; + std::string textDomain; + std::string textViewTheme; + }; + + typedef std::map< const Dali::RefObject*, LocalisedStringInfo > ObjectEntriesContainer; + typedef ObjectEntriesContainer::iterator ObjectEntriesIterator; + typedef ObjectEntriesContainer::const_iterator ObjectEntriesConstIterator; + + /** + * Construct a new LocalizedControlFactory. + */ + LocalizedControlFactory(); + + /** + * @copydoc Toolkit::LocalizedControlFactory::CreateLocalizedTextView + */ + Dali::Toolkit::TextView CreateLocalizedTextView( const std::string& textID, const std::string& textDomain, const std::string& textViewTheme ); + +protected: + + /** + * Destructor + */ + virtual ~LocalizedControlFactory(); + +private: + + /** + * Callback for Object destructed signal. + * @param objectPointer Pointer to a RefObject + */ + void OnObjectDestruction( const Dali::RefObject* objectPointer ); + + /** + * Callback for language changed signal. + * @param adaptor Reference to a Dali::Adaptor instance + */ + void OnLanguageChanged( Dali::Adaptor& adaptor); + +private: + + // Undefined + LocalizedControlFactory(const LocalizedControlFactory&); + LocalizedControlFactory& operator=(const LocalizedControlFactory& rhs); + +private: + + ObjectEntriesContainer mObjectEntries; + bool mSignalsConnected:1; + +}; + +} // namespace Internal + +inline Internal::LocalizedControlFactory& GetImpl(Dali::Toolkit::LocalizedControlFactory& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::LocalizedControlFactory& GetImpl(const Dali::Toolkit::LocalizedControlFactory& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_LOCALIZED_CONTROL_FACTORY_H__ diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list new file mode 100644 index 0000000..4c6317d --- /dev/null +++ b/dali-toolkit/internal/file.list @@ -0,0 +1,97 @@ +# Add local source files here + +toolkit_src_files = \ + $(toolkit_src_dir)/builder/builder-impl.cpp \ + $(toolkit_src_dir)/builder/builder-animations.cpp \ + $(toolkit_src_dir)/builder/builder-set-property.cpp \ + $(toolkit_src_dir)/builder/builder-signals.cpp \ + $(toolkit_src_dir)/builder/builder-actor.cpp \ + $(toolkit_src_dir)/builder/builder-control.cpp \ + $(toolkit_src_dir)/builder/json-parser-state.cpp \ + $(toolkit_src_dir)/builder/json-parser-impl.cpp \ + $(toolkit_src_dir)/builder/tree-node-manipulator.cpp \ + $(toolkit_src_dir)/controls/relayout-controller.cpp \ + $(toolkit_src_dir)/controls/relayout-controller-impl.cpp \ + $(toolkit_src_dir)/controls/relayout-helper.cpp \ + $(toolkit_src_dir)/controls/style-change-processor.cpp \ + $(toolkit_src_dir)/controls/alignment/alignment-impl.cpp \ + $(toolkit_src_dir)/controls/bloom-view/bloom-view-impl.cpp \ + $(toolkit_src_dir)/controls/buttons/button-impl.cpp \ + $(toolkit_src_dir)/controls/buttons/check-box-button-default-painter-impl.cpp \ + $(toolkit_src_dir)/controls/buttons/check-box-button-impl.cpp \ + $(toolkit_src_dir)/controls/buttons/push-button-default-painter-impl.cpp \ + $(toolkit_src_dir)/controls/buttons/push-button-impl.cpp \ + $(toolkit_src_dir)/controls/cluster/cluster-impl.cpp \ + $(toolkit_src_dir)/controls/cluster/cluster-style-impl.cpp \ + $(toolkit_src_dir)/controls/effects-view/effects-view-impl.cpp \ + $(toolkit_src_dir)/controls/gaussian-blur-view/gaussian-blur-view-impl.cpp \ + $(toolkit_src_dir)/controls/image-view/image-view-impl.cpp \ + $(toolkit_src_dir)/controls/image-view/masked-image-view-impl.cpp \ + $(toolkit_src_dir)/controls/page-turn-view/page-turn-view-impl.cpp \ + $(toolkit_src_dir)/controls/page-turn-view/page-turn-portrait-view-impl.cpp \ + $(toolkit_src_dir)/controls/page-turn-view/page-turn-landscape-view-impl.cpp \ + $(toolkit_src_dir)/controls/popup/popup-impl.cpp \ + $(toolkit_src_dir)/controls/popup/popup-style-impl.cpp \ + $(toolkit_src_dir)/controls/scroll-component/scroll-bar-impl.cpp \ + $(toolkit_src_dir)/controls/scroll-component/scroll-component-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/item-view/item-view-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scrollable-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-base-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-overshoot-indicator-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-custom-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-carousel-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-cube-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-helper-functions.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-page-cube-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-page-carousel-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-slide-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-twist-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.cpp \ + $(toolkit_src_dir)/controls/selectors/rotating-selector-impl.cpp \ + $(toolkit_src_dir)/controls/shadow-view/shadow-view-impl.cpp \ + $(toolkit_src_dir)/controls/slider/slider-impl.cpp \ + $(toolkit_src_dir)/controls/super-blur-view/super-blur-view-impl.cpp \ + $(toolkit_src_dir)/controls/bubble-effect/bubble-emitter-impl.cpp \ + $(toolkit_src_dir)/controls/table-view/table-view-impl.cpp \ + $(toolkit_src_dir)/controls/text-input/text-input-impl.cpp \ + $(toolkit_src_dir)/controls/text-input/text-input-popup-impl.cpp \ + $(toolkit_src_dir)/controls/text-view/relayout-utilities.cpp \ + $(toolkit_src_dir)/controls/text-view/split-by-new-line-char-policies.cpp \ + $(toolkit_src_dir)/controls/text-view/split-by-word-policies.cpp \ + $(toolkit_src_dir)/controls/text-view/split-by-char-policies.cpp \ + $(toolkit_src_dir)/controls/text-view/text-actor-cache.cpp \ + $(toolkit_src_dir)/controls/text-view/text-processor.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-impl.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-character-processor.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-line-processor.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-processor.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-processor-dbg.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-processor-helper-functions.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-word-processor.cpp \ + $(toolkit_src_dir)/controls/text-view/text-view-word-group-processor.cpp \ + $(toolkit_src_dir)/controls/tool-bar/tool-bar-impl.cpp \ + $(toolkit_src_dir)/controls/view/view-impl.cpp \ + $(toolkit_src_dir)/controls/navigation-frame/navigation-control-impl.cpp \ + $(toolkit_src_dir)/controls/navigation-frame/navigation-bar.cpp \ + $(toolkit_src_dir)/controls/navigation-frame/navigation-tool-bar.cpp \ + $(toolkit_src_dir)/controls/navigation-frame/navigation-title-bar.cpp \ + $(toolkit_src_dir)/controls/navigation-frame/page-impl.cpp \ + $(toolkit_src_dir)/factory/localized-control-factory-impl.cpp \ + $(toolkit_src_dir)/filters/blur-two-pass-filter.cpp \ + $(toolkit_src_dir)/filters/emboss-filter.cpp \ + $(toolkit_src_dir)/filters/image-filter.cpp \ + $(toolkit_src_dir)/filters/spread-filter.cpp \ + $(toolkit_src_dir)/focus-manager/focus-manager-impl.cpp \ + $(toolkit_src_dir)/focus-manager/keyboard-focus-manager-impl.cpp \ + $(toolkit_src_dir)/focus-manager/keyinput-focus-manager-impl.cpp \ + $(toolkit_src_dir)/shader-effects/water-effect-impl.cpp \ + $(toolkit_src_dir)/shader-effects/page-turn-effect-impl.cpp \ + $(toolkit_src_dir)/transition-effects/cube-transition-effect-impl.cpp \ + $(toolkit_src_dir)/transition-effects/cube-transition-wave-effect-impl.cpp \ + $(toolkit_src_dir)/transition-effects/cube-transition-cross-effect-impl.cpp \ + $(toolkit_src_dir)/transition-effects/cube-transition-fold-effect-impl.cpp \ + $(toolkit_src_dir)/controls/magnifier/magnifier-impl.cpp diff --git a/dali-toolkit/internal/filters/blur-two-pass-filter.cpp b/dali-toolkit/internal/filters/blur-two-pass-filter.cpp new file mode 100644 index 0000000..8f9de98 --- /dev/null +++ b/dali-toolkit/internal/filters/blur-two-pass-filter.cpp @@ -0,0 +1,327 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "blur-two-pass-filter.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +const float DEFAULT_KERNEL0[] = { 12.0f/16.0f, 2.0f/16.0f, 2.0f/16.0f }; + +const float DEFAULT_KERNEL1[] = { 8.0f/16.0f, 2.75f/16.0f, 2.75f/16.0f, 1.25f/16.0f, + 1.25f/16.0f }; + +const float DEFAULT_KERNEL2[] = { 5.0f/16.0f, 2.75f/16.0f, 2.75f/16.0f, 1.75f/16.0f, + 1.75f/16.0f, 1.5f/16.0f, 1.5f/16.0f }; + +const float DEFAULT_KERNEL3[] = { 3.0f/16.0f, 2.0f/16.0f, 2.0f/16.0f, 2.0f/16.0f, + 2.0f/16.0f, 2.0f/16.0f, 2.0f/16.0f, 0.5f/16.0f, + 0.5f/16.0f }; + +const float DEFAULT_KERNEL4[] = { 2.0f/16.0f, 1.5f/16.0f, 1.5f/16.0f, 1.5f/16.0f, + 1.5f/16.0f, 1.0f/16.0f, 1.0f/16.0f, 1.0f/16.0f, + 1.0f/16.0f, 1.0f/16.0f, 1.0f/16.0f, 0.5f/16.0f, + 0.5f/16.0f, 0.5f/16.0f, 0.5f/16.0f }; + + +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; + +const char* BLUR_TWO_PASS_FRAGMENT_SOURCE = +{ + "uniform vec2 uSampleOffsets[NUM_SAMPLES];\n" + "uniform float uSampleWeights[NUM_SAMPLES];\n" + "void main()\n" + "{\n" + " vec4 color = vec4(0.0);\n" + "# ifdef DEBUG_RENDER\n" + " if( vTexCoord.s < 0.495 )\n" + " {\n" + "# endif //def DEBUG_RENDER\n" + " for( int i = 0; i < NUM_SAMPLES; ++i )\n" + " {\n" + " color += texture2D( sTexture, vTexCoord + uSampleOffsets[i] ) * uSampleWeights[i];\n" + " }\n" + "# ifdef DEBUG_RENDER\n" + " }\n" + " else if( vTexCoord.s > 0.505 )\n" + " {\n" + " color = texture2D( sTexture, vTexCoord );\n" + " }\n" + " else\n" + " {\n" + " color = vec4( 1.0, 0.0, 0.0, 1.0 );\n" + " }\n" + "# endif //def DEBUG_RENDER\n" + " gl_FragColor = color;\n" + "}\n" +}; + +std::string GetOffsetUniformName( int index ) +{ + std::ostringstream oss; + oss << "uSampleOffsets[" << index << "]"; + return oss.str(); +} + +std::string GetWeightUniformName( int index ) +{ + std::ostringstream oss; + oss << "uSampleWeights[" << index << "]"; + return oss.str(); +} + +const char* BLEND_TWO_IMAGES_FRAGMENT_SOURCE = +{ + "precision highp float;\n" + "uniform float uBlurStrength; \n " + "void main()\n" + "{\n" + " gl_FragColor = texture2D( sTexture, vTexCoord ) * uBlurStrength" + " + texture2D( sEffect, vTexCoord )*(1.0-uBlurStrength); \n" + "}\n" +}; + +std::string GetBlurStrengthUniformName() +{ + return "uBlurStrength"; +} + +} // namespace + + +BlurTwoPassFilter::BlurTwoPassFilter() +: ImageFilter() +{ + mShaderForBlending = ShaderEffect::New( "", BLEND_TWO_IMAGES_FRAGMENT_SOURCE ); + mShaderForBlending.SetUniform( GetBlurStrengthUniformName(), 1.f ); + mBlurStrengthPropertyIndex = mShaderForBlending.GetPropertyIndex( GetBlurStrengthUniformName() ); +} + +BlurTwoPassFilter::~BlurTwoPassFilter() +{ +} + +void BlurTwoPassFilter::Enable() +{ + mCameraForBlur = CameraActor::New(); + mCameraForBlur.SetParentOrigin(ParentOrigin::CENTER); + + // create actor to render input with applied emboss effect + mActorForInput = ImageActor::New( mInputImage ); + mActorForInput.SetParentOrigin( ParentOrigin::CENTER ); + mActorForInput.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForInput.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + // create internal offscreen for result of horizontal pass + mImageForHorz = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Image::Unused ); + + // create an actor to render mImageForHorz for vertical blur pass + mActorForHorz = ImageActor::New( mImageForHorz ); + mActorForHorz.SetParentOrigin( ParentOrigin::CENTER ); + mActorForHorz.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForHorz.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + // create internal offscreen for result of the two pass blurred image + mBlurredImage = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Image::Unused ); + + // create an actor to blend the blurred image and the input image with the given blur strength + mActorForBlending = ImageActor::New( mBlurredImage ); + mActorForBlending.SetParentOrigin( ParentOrigin::CENTER ); + mActorForBlending.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForBlending.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + mRootActor.Add( mActorForInput ); + mRootActor.Add( mActorForHorz ); + mRootActor.Add( mActorForBlending ); + mRootActor.Add( mCameraForBlur ); + + // create custom shader effect + if( !GetKernelSize() ) + { + CreateKernel( DEFAULT_KERNEL4, sizeof(DEFAULT_KERNEL4)/sizeof(DEFAULT_KERNEL4[0]) ); + } + int kernelSize( static_cast< int >(GetKernelSize()) ); + + std::ostringstream fragmentSource; + if( mDebugRender ) + { + fragmentSource << "#define DEBUG_RENDER\n"; + } + fragmentSource << "#define NUM_SAMPLES " << kernelSize << "\n"; + fragmentSource << BLUR_TWO_PASS_FRAGMENT_SOURCE; + mShaderForHorz = ShaderEffect::New( "", fragmentSource.str() ); + mActorForInput.SetShaderEffect( mShaderForHorz ); + mShaderForVert = ShaderEffect::New( "", fragmentSource.str() ); + mActorForHorz.SetShaderEffect( mShaderForVert ); + + for( int i = 0; i < kernelSize; ++i ) + { + const std::string offsetUniform( GetOffsetUniformName( i ) ); + const std::string weightUniform( GetWeightUniformName( i ) ); + + mShaderForHorz.SetUniform( offsetUniform, Vector2(mKernel[i]) * Vector2::XAXIS ); + mShaderForHorz.SetUniform( weightUniform, mKernel[i].z ); + + mShaderForVert.SetUniform( offsetUniform, Vector2(mKernel[i]) * Vector2::YAXIS ); + mShaderForVert.SetUniform( weightUniform, mKernel[i].z ); + } + + mActorForBlending.SetShaderEffect( mShaderForBlending ); + mShaderForBlending.SetEffectImage( mInputImage ); + + SetupCamera(); + CreateRenderTasks(); +} + +void BlurTwoPassFilter::Disable() +{ + if( mRootActor ) + { + if( mCameraForBlur ) + { + mRootActor.Remove( mCameraForBlur ); + mCameraForBlur.Reset(); + } + + if( mActorForInput ) + { + mRootActor.Remove( mActorForInput ); + mActorForInput.Reset(); + } + + if( mActorForHorz ) + { + mRootActor.Remove( mActorForHorz ); + mActorForHorz.Reset(); + } + + if( mActorForBlending ) + { + mRootActor.Remove( mActorForBlending ); + mActorForBlending.Reset(); + } + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + if( mRenderTaskForHorz ) + { + taskList.RemoveTask(mRenderTaskForHorz); + } + if( mRenderTaskForVert ) + { + taskList.RemoveTask(mRenderTaskForVert); + } + if( mRenderTaskForBlending ) + { + taskList.RemoveTask(mRenderTaskForBlending); + } + + mRootActor.Reset(); + } +} + +void BlurTwoPassFilter::Refresh() +{ + if( mRenderTaskForHorz ) + { + mRenderTaskForHorz.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + + if( mRenderTaskForVert ) + { + mRenderTaskForVert.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + + if( mRenderTaskForBlending ) + { + mRenderTaskForBlending.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } +} + +Constrainable BlurTwoPassFilter::GetHandleForAnimateBlurStrength() +{ + return mShaderForBlending; +} + +void BlurTwoPassFilter::SetupCamera() +{ + // Create and place a camera for the embossing render, corresponding to its render target size + mCameraForBlur.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + mCameraForBlur.SetNearClippingPlane(1.0f); + mCameraForBlur.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mCameraForBlur.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mCameraForBlur.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + mCameraForBlur.SetPosition(0.0f, 0.0f, ((mTargetSize.height * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))); +} + +void BlurTwoPassFilter::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + // perform a horizontal blur targeting the internal buffer + mRenderTaskForHorz = taskList.CreateTask(); + mRenderTaskForHorz.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForHorz.SetSourceActor( mActorForInput ); + mRenderTaskForHorz.SetExclusive(true); + mRenderTaskForHorz.SetInputEnabled( false ); + mRenderTaskForHorz.SetClearEnabled( true ); + mRenderTaskForHorz.SetClearColor( mBackgroundColor ); + mRenderTaskForHorz.SetTargetFrameBuffer( mImageForHorz ); + mRenderTaskForHorz.SetCameraActor( mCameraForBlur ); + + // use the internal buffer and perform a horizontal blur targeting the output buffer + mRenderTaskForVert = taskList.CreateTask(); + mRenderTaskForVert.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForVert.SetSourceActor( mActorForHorz ); + mRenderTaskForVert.SetExclusive(true); + mRenderTaskForVert.SetInputEnabled( false ); + mRenderTaskForVert.SetClearEnabled( true ); + mRenderTaskForVert.SetClearColor( mBackgroundColor ); + mRenderTaskForVert.SetTargetFrameBuffer( mBlurredImage ); + mRenderTaskForVert.SetCameraActor( mCameraForBlur ); + + //Perform a blending between the blurred image and the input image + mRenderTaskForBlending = taskList.CreateTask(); + mRenderTaskForBlending.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForBlending.SetSourceActor( mActorForBlending ); + mRenderTaskForBlending.SetExclusive(true); + mRenderTaskForBlending.SetInputEnabled( false ); + mRenderTaskForBlending.SetClearEnabled( true ); + mRenderTaskForBlending.SetClearColor( mBackgroundColor ); + mRenderTaskForBlending.SetTargetFrameBuffer( mOutputImage ); + mRenderTaskForBlending.SetCameraActor( mCameraForBlur ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/filters/blur-two-pass-filter.h b/dali-toolkit/internal/filters/blur-two-pass-filter.h new file mode 100644 index 0000000..8c6f53b --- /dev/null +++ b/dali-toolkit/internal/filters/blur-two-pass-filter.h @@ -0,0 +1,121 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_BLUR_TWO_PASS_FILTER_H__ +#define __DALI_TOOLKIT_INTERNAL_BLUR_TWO_PASS_FILTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include "image-filter.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * A two pass blur filter, pass one performs a horizontal blur and pass two performs a + * vertical blur on the result of pass one. + */ +class BlurTwoPassFilter : public ImageFilter +{ +public: + /** + * Default constructor + */ + BlurTwoPassFilter(); + + /** + * Destructor + */ + virtual ~BlurTwoPassFilter(); + +public: // From ImageFilter + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Enable + virtual void Enable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Disable + virtual void Disable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Refresh + virtual void Refresh(); + + /** + * Get the property index that controls the strength of the blur applied to the image. Useful for animating this property. + * This property represents a value in the range [0.0 - 1.0] where 0.0 is no blur and 1.0 is full blur. + */ + Property::Index GetBlurStrengthPropertyIndex() const {return mBlurStrengthPropertyIndex;} + + /** + * Retrieve the constrainable object to animate or constrain the blur strength property + * @return the constrainable object which blend the output image according to the blur strength + */ + Constrainable GetHandleForAnimateBlurStrength(); + +private: + /** + * Setup position and parameters for camera + */ + void SetupCamera(); + + /** + * Setup render tasks for blur + */ + void CreateRenderTasks(); + +private: + BlurTwoPassFilter( const BlurTwoPassFilter& ); + BlurTwoPassFilter& operator=( const BlurTwoPassFilter& ); + +private: // Attributes + + CameraActor mCameraForBlur; + + // To perform horizontal blur from mInputImage to mImageForHorz + RenderTask mRenderTaskForHorz; + ImageActor mActorForInput; + FrameBufferImage mImageForHorz; + ShaderEffect mShaderForHorz; + + // To perform vertical blur from mImageForHorz to mOutputImage + RenderTask mRenderTaskForVert; + ImageActor mActorForHorz; + ShaderEffect mShaderForVert; + FrameBufferImage mBlurredImage; + + // To blend the blurred image and input image according to the blur strength + RenderTask mRenderTaskForBlending; + ImageActor mActorForBlending; + Actor mRootActorForBlending; + ShaderEffect mShaderForBlending; + Property::Index mBlurStrengthPropertyIndex; + +}; // class BlurTwoPassFilter + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_BLUR_TWO_PASS_FILTER_H__ + diff --git a/dali-toolkit/internal/filters/emboss-filter.cpp b/dali-toolkit/internal/filters/emboss-filter.cpp new file mode 100644 index 0000000..4993cdf --- /dev/null +++ b/dali-toolkit/internal/filters/emboss-filter.cpp @@ -0,0 +1,293 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "emboss-filter.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; + +const char* EMBOSS_FRAGMENT_SOURCE1 = +{ + "uniform vec2 uTexScale;\n" + "\n" + "void main()\n" + "{\n" + " vec4 color;\n" + "# ifdef DEBUG_RENDER\n" + " if( vTexCoord.s < 0.495 )\n" + " {\n" + "# endif //def DEBUG_RENDER\n" + " color = 2.0 * texture2D( sTexture, vTexCoord + vec2(0.0, -uTexScale.y) );\n" + " color += -1.0 * texture2D( sTexture, vTexCoord );\n" + " color += -1.0 * texture2D( sTexture, vTexCoord + vec2(0.0, uTexScale.y) );\n" + "# ifdef DEBUG_RENDER\n" + " }\n" + " else if( vTexCoord.s > 0.505 )\n" + " {\n" + " color = texture2D( sTexture, vTexCoord );\n" + " }\n" + " else\n" + " {\n" + " color = vec4( 1.0, 0.0, 0.0, 1.0 );\n" + " }\n" + "# endif //def DEBUG_RENDER\n" + " gl_FragColor = uColor * color;\n" + "}\n" +}; + +const char* EMBOSS_FRAGMENT_SOURCE2 = +{ + "uniform vec2 uTexScale;\n" + "\n" + "void main()\n" + "{\n" + " vec4 color;\n" + "# ifdef DEBUG_RENDER\n" + " if( vTexCoord.s < 0.495 )\n" + " {\n" + "# endif //def DEBUG_RENDER\n" + " color = -1.0 * texture2D( sTexture, vTexCoord + vec2(0.0, -uTexScale.y) );\n" + " color += -1.0 * texture2D( sTexture, vTexCoord );\n" + " color += 2.0 * texture2D( sTexture, vTexCoord + vec2(0.0, uTexScale.y) );\n" + "# ifdef DEBUG_RENDER\n" + " }\n" + " else if( vTexCoord.s > 0.505 )\n" + " {\n" + " color = texture2D( sTexture, vTexCoord );\n" + " }\n" + " else\n" + " {\n" + " color = vec4( 1.0, 0.0, 0.0, 1.0 );\n" + " }\n" + "# endif //def DEBUG_RENDER\n" + " gl_FragColor = uColor * color;\n" + "}\n" +}; + +const char* const COMPOSITE_FRAGMENT_SOURCE = + "void main()\n" + "{\n" + " gl_FragColor = uColor;\n" + " gl_FragColor.a *= texture2D( sTexture, vTexCoord).a;\n" + "}\n"; + +} // namespace + +EmbossFilter::EmbossFilter() +: ImageFilter() +{ +} + +EmbossFilter::~EmbossFilter() +{ +} + +void EmbossFilter::Enable() +{ + // Create camera + mCameraActor = CameraActor::New(); + mCameraActor.SetParentOrigin(ParentOrigin::CENTER); + + mImageForEmboss1 = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Image::Unused ); + mImageForEmboss2 = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Image::Unused ); + + // create actor to render input with applied emboss effect + mActorForInput1 = ImageActor::New( mInputImage ); + mActorForInput1.SetParentOrigin( ParentOrigin::CENTER ); + mActorForInput1.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForInput1.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + mActorForInput1.SetColor( Color::WHITE ); + + mActorForInput2 = ImageActor::New( mInputImage ); + mActorForInput2.SetParentOrigin( ParentOrigin::CENTER ); + mActorForInput2.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForInput2.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + mActorForInput2.SetColor( Color::WHITE ); + + mActorForEmboss1 = ImageActor::New( mImageForEmboss1 ); + mActorForEmboss1.SetParentOrigin( ParentOrigin::CENTER ); + mActorForEmboss1.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForEmboss1.SetColor( Color::BLACK ); + mActorForEmboss1.SetShaderEffect( ShaderEffect::New( "", COMPOSITE_FRAGMENT_SOURCE ) ); + + mActorForEmboss2 = ImageActor::New( mImageForEmboss2 ); + mActorForEmboss2.SetParentOrigin( ParentOrigin::CENTER ); + mActorForEmboss2.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForEmboss2.SetColor( Color::WHITE ); + mActorForEmboss2.SetShaderEffect( ShaderEffect::New( "", COMPOSITE_FRAGMENT_SOURCE ) ); + + mActorForComposite = Actor::New(); + mActorForComposite.SetParentOrigin( ParentOrigin::CENTER ); + mActorForComposite.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForComposite.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + // create custom shader effect + std::ostringstream embossFragmentSource1; + if( mDebugRender ) + { + embossFragmentSource1 << "#define DEBUG_RENDER\n"; + } + embossFragmentSource1 << EMBOSS_FRAGMENT_SOURCE1; + ShaderEffect effect1 = ShaderEffect::New( "", embossFragmentSource1.str() ); + mActorForInput1.SetShaderEffect( effect1 ); + effect1.SetUniform( "uTexScale", Vector2( 1.0f/mTargetSize.width, 1.0f/mTargetSize.height) * 1.5f ); + + std::ostringstream embossFragmentSource2; + if( mDebugRender ) + { + embossFragmentSource2 << "#define DEBUG_RENDER\n"; + } + embossFragmentSource2 << EMBOSS_FRAGMENT_SOURCE2; + ShaderEffect effect2 = ShaderEffect::New( "", embossFragmentSource2.str() ); + mActorForInput2.SetShaderEffect( effect2 ); + effect2.SetUniform( "uTexScale", Vector2( 1.0f/mTargetSize.width, 1.0f/mTargetSize.height) * 1.5f ); + + SetupCamera(); + CreateRenderTasks(); + + mRootActor.Add( mActorForInput1 ); + mRootActor.Add( mActorForInput2 ); + mRootActor.Add( mActorForComposite ); + mActorForComposite.Add( mActorForEmboss1 ); + mActorForComposite.Add( mActorForEmboss2 ); + mRootActor.Add( mCameraActor ); +} + +void EmbossFilter::Disable() +{ + if( mRootActor ) + { + if( mCameraActor ) + { + mRootActor.Remove( mCameraActor ); + mCameraActor.Reset(); + } + + if( mActorForInput1 ) + { + mRootActor.Remove( mActorForInput1 ); + mActorForInput1.Reset(); + } + + if( mActorForInput2 ) + { + mRootActor.Remove( mActorForInput2 ); + mActorForInput2.Reset(); + } + + if( mActorForComposite ) + { + mRootActor.Remove( mActorForComposite ); + mActorForComposite.Reset(); + mActorForEmboss1.Reset(); + mActorForEmboss2.Reset(); + } + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + if( mRenderTaskForEmboss1 ) + { + taskList.RemoveTask(mRenderTaskForEmboss1); + } + + if( mRenderTaskForEmboss2 ) + { + taskList.RemoveTask(mRenderTaskForEmboss2); + } + + mRootActor.Reset(); + } +} + +void EmbossFilter::Refresh() +{ + if( mRenderTaskForEmboss1 ) + { + mRenderTaskForEmboss1.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + if( mRenderTaskForEmboss2 ) + { + mRenderTaskForEmboss2.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } +} + +void EmbossFilter::SetupCamera() +{ + // Create and place a camera for the embossing render, corresponding to its render target size + mCameraActor.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + mCameraActor.SetNearClippingPlane(1.0f); + mCameraActor.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mCameraActor.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mCameraActor.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + mCameraActor.SetPosition(0.0f, 0.0f, ((mTargetSize.height * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))); +} + +void EmbossFilter::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + mRenderTaskForEmboss1 = taskList.CreateTask(); + mRenderTaskForEmboss1.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForEmboss1.SetSourceActor( mActorForInput1 ); + mRenderTaskForEmboss1.SetExclusive(true); + mRenderTaskForEmboss1.SetInputEnabled( false ); + mRenderTaskForEmboss1.SetClearColor( Vector4( 0.0f, 0.0f, 0.0f, 0.0f ) ); + mRenderTaskForEmboss1.SetClearEnabled( true ); + mRenderTaskForEmboss1.SetTargetFrameBuffer( mImageForEmboss1 ); + mRenderTaskForEmboss1.SetCameraActor( mCameraActor ); + + mRenderTaskForEmboss2 = taskList.CreateTask(); + mRenderTaskForEmboss2.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForEmboss2.SetSourceActor( mActorForInput2 ); + mRenderTaskForEmboss2.SetExclusive(true); + mRenderTaskForEmboss2.SetInputEnabled( false ); + mRenderTaskForEmboss2.SetClearColor( Vector4( 1.0f, 1.0f, 1.0f, 0.0f ) ); + mRenderTaskForEmboss2.SetClearEnabled( true ); + mRenderTaskForEmboss2.SetTargetFrameBuffer( mImageForEmboss2 ); + mRenderTaskForEmboss2.SetCameraActor( mCameraActor ); + + mRenderTaskForOutput = taskList.CreateTask(); + mRenderTaskForOutput.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForOutput.SetSourceActor( mActorForComposite ); + mRenderTaskForOutput.SetExclusive(true); + mRenderTaskForOutput.SetInputEnabled( false ); + mRenderTaskForOutput.SetClearColor( Vector4( 0.5f, 0.5f, 0.5f, 0.0f ) ); + mRenderTaskForOutput.SetClearEnabled( true ); + mRenderTaskForOutput.SetTargetFrameBuffer( mOutputImage ); + mRenderTaskForOutput.SetCameraActor( mCameraActor ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/filters/emboss-filter.h b/dali-toolkit/internal/filters/emboss-filter.h new file mode 100644 index 0000000..12511cf --- /dev/null +++ b/dali-toolkit/internal/filters/emboss-filter.h @@ -0,0 +1,98 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_EMBOSS_FILTER_H__ +#define __DALI_TOOLKIT_INTERNAL_EMBOSS_FILTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include "image-filter.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * An embossing image filter, implements Dali::Toolkit::Internal::ImageFilter + */ +class EmbossFilter : public ImageFilter +{ +public: + /** + * Construct an empty filter + */ + EmbossFilter(); + + /** + * Destructor + */ + virtual ~EmbossFilter(); + +public: // From ImageFilter + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Enable + virtual void Enable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Disable + virtual void Disable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Refresh + virtual void Refresh(); + +private: + /** + * Setup position and parameters for camera + */ + void SetupCamera(); + + /** + * Setup render tasks for blur + */ + void CreateRenderTasks(); + +private: + EmbossFilter( const EmbossFilter& ); + EmbossFilter& operator=( const EmbossFilter& ); + +private: // Attributes + + RenderTask mRenderTaskForEmboss1; + RenderTask mRenderTaskForEmboss2; + RenderTask mRenderTaskForOutput; + FrameBufferImage mImageForEmboss1; + FrameBufferImage mImageForEmboss2; + CameraActor mCameraActor; + ImageActor mActorForInput1; + ImageActor mActorForInput2; + ImageActor mActorForEmboss1; + ImageActor mActorForEmboss2; + Actor mActorForComposite; +}; // class EmbossFilter + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_EMBOSS_FILTER_H__ + diff --git a/dali-toolkit/internal/filters/image-filter.cpp b/dali-toolkit/internal/filters/image-filter.cpp new file mode 100644 index 0000000..7ecca05 --- /dev/null +++ b/dali-toolkit/internal/filters/image-filter.cpp @@ -0,0 +1,127 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "image-filter.h" + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +} // namespace + +ImageFilter::ImageFilter() +: mBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.0f ) ), + mTargetSize( Vector2::ZERO ), + mPixelFormat( Pixel::RGBA8888 ), + mRefreshOnDemand( false ), + mDebugRender( false ) +{ +} + +ImageFilter::~ImageFilter() +{ +} + +void ImageFilter::SetRefreshOnDemand( bool onDemand ) +{ + mRefreshOnDemand = onDemand; +} + +void ImageFilter::SetInputImage( Image image ) +{ + mInputImage = image; +} + +void ImageFilter::SetOutputImage( FrameBufferImage image ) +{ + mOutputImage = image; +} + +void ImageFilter::SetSize( const Vector2& size ) +{ + mTargetSize = size; +} + +void ImageFilter::SetPixelFormat( Pixel::Format pixelFormat ) +{ + mPixelFormat = pixelFormat; +} + +void ImageFilter::SetKernel( const FilterKernel& kernel ) +{ + mKernel = kernel; +} + +const ImageFilter::FilterKernel& ImageFilter::GetKernel() const +{ + return mKernel; +} + +size_t ImageFilter::GetKernelSize() const +{ + return mKernel.size(); +} + +void ImageFilter::CreateKernel( const float* weights, size_t count ) +{ + if( (mTargetSize.width * mTargetSize.height ) > 0.0f ) + { + Vector2 pixelsToUV( 1.0f / mTargetSize.width, 1.0f / mTargetSize.height ); + + mKernel.clear(); + + mKernel.push_back( Vector3( 0.0f, 0.0f, weights[0] ) ); + for( size_t i = 0; i < count >> 1; ++i ) + { + float offset = 1.5f + (i << 1); + + mKernel.push_back( Vector3( pixelsToUV.x * offset, pixelsToUV.y * offset, weights[(i << 1) + 1] ) ); + mKernel.push_back( Vector3( -pixelsToUV.x * offset, -pixelsToUV.y * offset, weights[(i << 1) + 2] ) ); + } + } +} + +void ImageFilter::SetRootActor( Actor rootActor ) +{ + mRootActor = rootActor; +} + +void ImageFilter::SetBackgroundColor( const Vector4& color ) +{ + mBackgroundColor = color; +} + +void ImageFilter::RenderDebug( bool flag ) +{ + mDebugRender = flag; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/filters/image-filter.h b/dali-toolkit/internal/filters/image-filter.h new file mode 100644 index 0000000..d836173 --- /dev/null +++ b/dali-toolkit/internal/filters/image-filter.h @@ -0,0 +1,164 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_IMAGE_FILTER_H__ +#define __DALI_TOOLKIT_INTERNAL_IMAGE_FILTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * An interface class that provides a interface for image filters that perform + * a simple shader effect on an input image, rendering the output to a FrameBufferImage. + */ +class ImageFilter +{ +public: + typedef std::vector< Vector3 > FilterKernel; + +public: + + /** + * Default constructor + */ + ImageFilter(); + + /** + * Destructor + */ + virtual ~ImageFilter(); + + /** + * Enable effect, allocates any necessary resources + */ + virtual void Enable() = 0; + + /** + * Disable effect, releases any allocated resources + */ + virtual void Disable() = 0; + + /** + * Refresh the filter output + */ + virtual void Refresh() = 0; + + /** + * @copydoc Dali::Toolkit::EffectsView::SetRefreshOnDemand + */ + void SetRefreshOnDemand( bool onDemand ); + + /** + * Set the input image + * @param[in] The input/original image. + */ + void SetInputImage( Image image ); + + /** + * Set the output image + * @return The output image. + */ + void SetOutputImage( FrameBufferImage image ); + + /** + * Set size of ImageFilter. Used to create internal offscreen buffers + * @param[in] size THe size. + */ + void SetSize( const Vector2& size ); + + /** + * Set the pixel format for internal offscreen buffers + * @param[in] pixelFormat The pixel format. + */ + void SetPixelFormat( Pixel::Format pixelFormat ); + + /** + * Set the filter kernel + * @param[in] The filter kernel + */ + void SetKernel( const FilterKernel& kernel ); + + /** + * Get a const reference to the internal filter kernel + * @Return A a const reference to the internal filter kernel + */ + const FilterKernel& GetKernel() const; + + /** + * Get the number of steps/elements in the kernel + * @return The number of steps/elements in the kernel + */ + size_t GetKernelSize() const; + + /** + * Create a kernel from an array of weights + * @param[in] weights + * @param[in] count + */ + void CreateKernel( const float* weights, size_t count); + + /** + * Set the actor which acts as the root actor for all internal actors for connection to stage + * @param[in] rootActor An actor which acts as the root actor for any internal actors that need + * to be created + */ + void SetRootActor( Actor rootActor ); + + /** + * Set the background / clear color + * @param[in] color The background / clear color + */ + void SetBackgroundColor( const Vector4& color ); + + /** + * Enable optional debug output in the shader + * @param[in] flag Set true to enable, dalse to disable. + */ + void RenderDebug( bool flag ); + +protected: + Image mInputImage; + FrameBufferImage mOutputImage; + FilterKernel mKernel; + Actor mRootActor; + Vector4 mBackgroundColor; + Vector2 mTargetSize; + Pixel::Format mPixelFormat; + bool mRefreshOnDemand; + bool mDebugRender; +}; // class Imagefilter + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_IMAGE_FILTER_H__ + diff --git a/dali-toolkit/internal/filters/spread-filter.cpp b/dali-toolkit/internal/filters/spread-filter.cpp new file mode 100644 index 0000000..0dab8c6 --- /dev/null +++ b/dali-toolkit/internal/filters/spread-filter.cpp @@ -0,0 +1,223 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "spread-filter.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f; + +const char* const SPREAD_FRAGMENT_SOURCE = +{ + "uniform float uSpread;\n" + "uniform vec2 uTexScale;\n" + "void main()\n" + "{\n" + " vec4 color = texture2D( sTexture, vTexCoord);\n" + "# ifdef DEBUG_RENDER\n" + " if( vTexCoord.s < 0.495 )\n" + " {\n" + "# endif //def DEBUG_RENDER\n" + " int spread = int(uSpread);\n" + " for( int i = 1; i <= spread; ++i )\n" + " {\n" + " vec2 offset = uTexScale * float(i);\n" + " color = max( texture2D( sTexture, vTexCoord + offset), color );\n" + " color = max( texture2D( sTexture, vTexCoord - offset), color );\n" + " }\n" + "# ifdef DEBUG_RENDER\n" + " }\n" + " else if( vTexCoord.s <= 0.505 )\n" + " {\n" + " color = vec4( 1.0, 0.0, 0.0, 1.0 );\n" + " }\n" + "# endif //def DEBUG_RENDER\n" + " gl_FragColor = color;\n" + "}\n" +}; + +} // namespace + + +SpreadFilter::SpreadFilter() +: ImageFilter(), + mSpread(2) +{ +} + +SpreadFilter::~SpreadFilter() +{ +} + +void SpreadFilter::SetSpread( float spread ) +{ + mSpread = spread; +} + +void SpreadFilter::Enable() +{ + mCameraActor = CameraActor::New(); + mCameraActor.SetParentOrigin(ParentOrigin::CENTER); + + // create actor to render input with applied emboss effect + mActorForInput = ImageActor::New( mInputImage ); + mActorForInput.SetParentOrigin( ParentOrigin::CENTER ); + mActorForInput.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForInput.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + // create internal offscreen for result of horizontal pass + mImageForHorz = FrameBufferImage::New( mTargetSize.width, mTargetSize.height, mPixelFormat, Image::Unused ); + + // create an actor to render mImageForHorz for vertical blur pass + mActorForHorz = ImageActor::New( mImageForHorz ); + mActorForHorz.SetParentOrigin( ParentOrigin::CENTER ); + mActorForHorz.ApplyConstraint( Constraint::New( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) ); + mActorForHorz.ScaleBy( Vector3(1.0f, -1.0f, 1.0f) ); + + mRootActor.Add( mActorForInput ); + mRootActor.Add( mActorForHorz ); + mRootActor.Add( mCameraActor ); + + std::ostringstream fragmentSource; + if( mDebugRender ) + { + fragmentSource << "#define DEBUG_RENDER\n"; + } + fragmentSource << SPREAD_FRAGMENT_SOURCE; + + mShaderForHorz = ShaderEffect::New( "", fragmentSource.str() ); + mActorForInput.SetShaderEffect( mShaderForHorz ); + mShaderForHorz.SetUniform( "uSpread", mSpread ); + mShaderForHorz.SetUniform( "uTexScale", Vector2( 1.0f / mTargetSize.width, 0.0f ) ); + + mShaderForVert = ShaderEffect::New( "", fragmentSource.str() ); + mActorForHorz.SetShaderEffect( mShaderForVert ); + mShaderForVert.SetUniform( "uSpread", mSpread ); + mShaderForVert.SetUniform( "uTexScale", Vector2( 0.0f, 1.0f / mTargetSize.height ) ); + + SetupCamera(); + CreateRenderTasks(); +} + +void SpreadFilter::Disable() +{ + if( mRootActor ) + { + if( mCameraActor ) + { + mRootActor.Remove( mCameraActor ); + mCameraActor.Reset(); + } + + if( mActorForInput ) + { + mRootActor.Remove( mActorForInput ); + mActorForInput.Reset(); + } + + if( mActorForHorz ) + { + mRootActor.Remove( mActorForHorz ); + mActorForHorz.Reset(); + } + + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + if( mRenderTaskForHorz ) + { + taskList.RemoveTask(mRenderTaskForHorz); + } + if( mRenderTaskForVert ) + { + taskList.RemoveTask(mRenderTaskForVert); + } + + mRootActor.Reset(); + } +} + +void SpreadFilter::Refresh() +{ + if( mRenderTaskForHorz ) + { + mRenderTaskForHorz.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } + + if( mRenderTaskForVert ) + { + mRenderTaskForVert.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + } +} + +void SpreadFilter::SetupCamera() +{ + // Create and place a camera for the embossing render, corresponding to its render target size + mCameraActor.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW); + mCameraActor.SetNearClippingPlane(1.0f); + mCameraActor.SetAspectRatio(mTargetSize.width / mTargetSize.height); + mCameraActor.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor + mCameraActor.SetRotation(Quaternion(M_PI, Vector3::YAXIS)); + mCameraActor.SetPosition(0.0f, 0.0f, ((mTargetSize.height * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))); +} + +void SpreadFilter::CreateRenderTasks() +{ + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + + // perform a horizontal blur targeting the internal buffer + mRenderTaskForHorz = taskList.CreateTask(); + mRenderTaskForHorz.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForHorz.SetSourceActor( mActorForInput ); + mRenderTaskForHorz.SetExclusive(true); + mRenderTaskForHorz.SetInputEnabled( false ); + mRenderTaskForHorz.SetClearEnabled( true ); + mRenderTaskForHorz.SetClearColor( mBackgroundColor ); + mRenderTaskForHorz.SetTargetFrameBuffer( mImageForHorz ); + mRenderTaskForHorz.SetCameraActor( mCameraActor ); + + // use the internal buffer and perform a horizontal blur targeting the output buffer + mRenderTaskForVert = taskList.CreateTask(); + mRenderTaskForVert.SetRefreshRate( mRefreshOnDemand ? RenderTask::REFRESH_ONCE : RenderTask::REFRESH_ALWAYS ); + mRenderTaskForVert.SetSourceActor( mActorForHorz ); + mRenderTaskForVert.SetExclusive(true); + mRenderTaskForVert.SetInputEnabled( false ); + mRenderTaskForVert.SetClearEnabled( true ); + mRenderTaskForVert.SetClearColor( mBackgroundColor ); + mRenderTaskForVert.SetTargetFrameBuffer( mOutputImage ); + mRenderTaskForVert.SetCameraActor( mCameraActor ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/filters/spread-filter.h b/dali-toolkit/internal/filters/spread-filter.h new file mode 100644 index 0000000..a4c02b2 --- /dev/null +++ b/dali-toolkit/internal/filters/spread-filter.h @@ -0,0 +1,105 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_SPREAD_FILTER_H__ +#define __DALI_TOOLKIT_INTERNAL_SPREAD_FILTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include "image-filter.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * A spread/thicken filter. Expands an image into transparent areas. + */ +class SpreadFilter : public ImageFilter +{ +public: + /** + * Default constructor + */ + SpreadFilter(); + + /** + * Destructor + */ + virtual ~SpreadFilter(); + + /** + * Set the amount of spread in pixels. + * @param[in] spread The amount of spread + */ + void SetSpread( float spread ); + +public: // From ImageFilter + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Enable + virtual void Enable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Disable + virtual void Disable(); + + /// @copydoc Dali::Toolkit::Internal::ImageFilter::Refresh + virtual void Refresh(); + +private: + /** + * Setup position and parameters for camera + */ + void SetupCamera(); + + /** + * Setup render tasks for blur + */ + void CreateRenderTasks(); + +private: + SpreadFilter( const SpreadFilter& ); + SpreadFilter& operator=( const SpreadFilter& ); + +private: // Attributes + int mSpread; + CameraActor mCameraActor; + + // To perform horizontal spread from mInputImage to mImageForHorz + RenderTask mRenderTaskForHorz; + ImageActor mActorForInput; + FrameBufferImage mImageForHorz; + ShaderEffect mShaderForHorz; + + // To perform vertical spread from mImageForHorz to mOutputImage + RenderTask mRenderTaskForVert; + ImageActor mActorForHorz; + ShaderEffect mShaderForVert; +}; // class SpreadFilter + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_SPREAD_FILTER_H__ + diff --git a/dali-toolkit/internal/focus-manager/focus-manager-impl.cpp b/dali-toolkit/internal/focus-manager/focus-manager-impl.cpp new file mode 100644 index 0000000..ad3a8e8 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/focus-manager-impl.cpp @@ -0,0 +1,917 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "focus-manager-impl.h" + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_FOCUS_MANAGER"); +#endif + +const char* FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "B16-8_TTS_focus.png"; +const Vector4 FOCUS_BORDER_IMAGE_BORDER = Vector4(7.0f, 7.0f, 7.0f, 7.0f); + +/** + * The function to be used in the hit-test algorithm to check whether the actor is hittable. + */ +bool IsActorFocusableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type) +{ + bool hittable = false; + + switch (type) + { + case Dali::HitTestAlgorithm::CHECK_ACTOR: + { + // Check whether the actor is visible and not fully transparent. + if( actor.IsVisible() + && actor.GetCurrentWorldColor().a > 0.01f) // not FULLY_TRANSPARENT + { + // Check whether the actor is focusable + Property::Index propertyActorFocusable = actor.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + if(propertyActorFocusable != Property::INVALID_INDEX) + { + hittable = actor.GetProperty(propertyActorFocusable); + } + } + break; + } + case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE: + { + if( actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible. + { + hittable = true; + } + break; + } + default: + { + break; + } + } + + return hittable; +}; + +} + +FocusManager::FocusManager() +: mIsWrapped(false), + mIsFocusWithinGroup(false), + mCurrentFocusActor(FocusIDPair(0, 0)), + mFocusIndicatorActor(Actor()), + mRecursiveFocusMoveCounter(0), + mIsAccessibilityTtsEnabled(false), + mIsFocusIndicatorEnabled(false) +{ + CreateDefaultFocusIndicatorActor(); + + AccessibilityManager manager = AccessibilityManager::Get(); + manager.SetActionHandler(*this); + manager.SetGestureHandler(*this); + + ChangeAccessibilityStatus(); +} + +FocusManager::~FocusManager() +{ +} + +FocusManager::ActorAdditionalInfo FocusManager::GetActorAdditionalInfo(const unsigned int actorID) const +{ + ActorAdditionalInfo data; + IDAdditionalInfoConstIter iter = mIDAdditionalInfoContainer.find(actorID); + if(iter != mIDAdditionalInfoContainer.end()) + { + data = (*iter).second; + } + + return data; +} + +void FocusManager::SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order) +{ + ActorAdditionalInfo actorInfo = GetActorAdditionalInfo(actorID); + actorInfo.mFocusOrder = order; + mIDAdditionalInfoContainer.erase(actorID); + mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, actorInfo)); +} + +void FocusManager::SetAccessibilityAttribute(Actor actor, Toolkit::FocusManager::AccessibilityAttribute type, const std::string& text) +{ + if(actor) + { + unsigned int actorID = actor.GetId(); + + ActorAdditionalInfo info = GetActorAdditionalInfo(actorID); + info.mAccessibilityAttributes[type] = text; + + mIDAdditionalInfoContainer.erase(actorID); + mIDAdditionalInfoContainer.insert(IDAdditionalInfoPair(actorID, info)); + } +} + +std::string FocusManager::GetAccessibilityAttribute(Actor actor, Toolkit::FocusManager::AccessibilityAttribute type) const +{ + std::string text; + + if(actor) + { + ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId()); + text = data.mAccessibilityAttributes[type]; + } + + return text; +} + +void FocusManager::SetFocusOrder(Actor actor, const unsigned int order) +{ + // Do nothing if the focus order of the actor is not changed. + if(actor && GetFocusOrder(actor) != order) + { + // Firstly delete the actor from the focus chain if it's already there with a different focus order. + mFocusIDContainer.erase(GetFocusOrder(actor)); + + // Create actor focusable property if not already created. + Property::Index propertyActorFocusable = actor.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + if(propertyActorFocusable == Property::INVALID_INDEX) + { + propertyActorFocusable = actor.RegisterProperty(Toolkit::FocusManager::ACTOR_FOCUSABLE, true); + } + + if(order == 0) + { + // The actor is not focusable without a defined focus order. + actor.SetProperty(propertyActorFocusable, false); + + // If the actor is currently being focused, it should clear the focus + if(actor == GetCurrentFocusActor()) + { + ClearFocus(); + } + } + else // Insert the actor to the focus chain + { + // Check whether there is another actor in the focus chain with the same focus order already. + FocusIDIter focusIDIter = mFocusIDContainer.find(order); + if(focusIDIter != mFocusIDContainer.end()) + { + // We need to increase the focus order of that actor and all the actors followed it + // in the focus chain. + FocusIDIter lastIter = mFocusIDContainer.end(); + --lastIter;//We want forward iterator to the last element here + mFocusIDContainer.insert(FocusIDPair((*lastIter).first + 1, (*lastIter).second)); + + // Update the actor's focus order in its additional data + SynchronizeActorAdditionalInfo((*lastIter).second, (*lastIter).first + 1); + + for(FocusIDIter iter = lastIter; iter != focusIDIter; iter--) + { + FocusIDIter previousIter = iter; + --previousIter;//We want forward iterator to the previous element here + unsigned int actorID = (*previousIter).second; + (*iter).second = actorID; + + // Update the actor's focus order in its additional data + SynchronizeActorAdditionalInfo(actorID, (*iter).first); + } + + mFocusIDContainer.erase(order); + } + + // The actor is focusable + actor.SetProperty(propertyActorFocusable, true); + + // Now we insert the actor into the focus chain with the specified focus order + mFocusIDContainer.insert(FocusIDPair(order, actor.GetId())); + } + + // Update the actor's focus order in its additional data + SynchronizeActorAdditionalInfo(actor.GetId(), order); + } +} + +unsigned int FocusManager::GetFocusOrder(Actor actor) const +{ + unsigned int focusOrder = 0; + + if(actor) + { + ActorAdditionalInfo data = GetActorAdditionalInfo(actor.GetId()); + focusOrder = data.mFocusOrder; + } + + return focusOrder; +} + +unsigned int FocusManager::GenerateNewFocusOrder() const +{ + unsigned int order = 1; + FocusIDContainer::const_reverse_iterator iter = mFocusIDContainer.rbegin(); + + if(iter != mFocusIDContainer.rend()) + { + order = (*iter).first + 1; + } + + return order; +} + +Actor FocusManager::GetActorByFocusOrder(const unsigned int order) +{ + Actor actor = Actor(); + + FocusIDIter focusIDIter = mFocusIDContainer.find(order); + if(focusIDIter != mFocusIDContainer.end()) + { + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + actor = rootActor.FindChildById(mFocusIDContainer[order]); + } + + return actor; +} + +bool FocusManager::SetCurrentFocusActor(Actor actor) +{ + if(actor) + { + return DoSetCurrentFocusActor(actor.GetId()); + } + + return false; +} + +bool FocusManager::DoSetCurrentFocusActor(const unsigned int actorID) +{ + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + + // If the group mode is enabled, check which focus group the current focused actor belongs to + Actor focusGroup; + if(mIsFocusWithinGroup) + { + focusGroup = GetFocusGroup(GetCurrentFocusActor()); + } + + if(!focusGroup) + { + focusGroup = rootActor; + } + + Actor actor = focusGroup.FindChildById(actorID); + + // Check whether the actor is in the stage + if(actor) + { + // Check whether the actor is focusable + bool actorFocusable = false; + Property::Index propertyActorFocusable = actor.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + if(propertyActorFocusable != Property::INVALID_INDEX) + { + actorFocusable = actor.GetProperty(propertyActorFocusable); + } + + // Go through the actor's hierarchy to check whether the actor is visible + bool actorVisible = actor.IsVisible(); + Actor parent = actor.GetParent(); + while (actorVisible && parent && parent != rootActor) + { + actorVisible = parent.IsVisible(); + parent = parent.GetParent(); + } + + // Check whether the actor is fully transparent + bool actorOpaque = actor.GetCurrentWorldColor().a > 0.01f; + + // Set the focus only when the actor is focusable and visible and not fully transparent + if(actorVisible && actorFocusable && actorOpaque) + { + // Draw the focus indicator upon the focused actor + if(mIsFocusIndicatorEnabled && mFocusIndicatorActor) + { + actor.Add(mFocusIndicatorActor); + } + + // Send notification for the change of focus actor + mFocusChangedSignalV2.Emit( GetCurrentFocusActor(), actor ); + + // Save the current focused actor + mCurrentFocusActor = FocusIDPair(GetFocusOrder(actor), actorID); + + if(mIsAccessibilityTtsEnabled) + { + // Play the accessibility attributes with the TTS player. + Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER); + + // Combine attribute texts to one text + std::string informationText; + for(int i = 0; i < Toolkit::FocusManager::ACCESSIBILITY_ATTRIBUTE_NUM; i++) + { + if(!GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i].empty()) + { + if( i > 0 ) + { + informationText += ", "; // for space time between each information + } + informationText += GetActorAdditionalInfo(actorID).mAccessibilityAttributes[i]; + } + } + player.Play(informationText); + } + + return true; + } + } + + DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__); + return false; +} + +Actor FocusManager::GetCurrentFocusActor() +{ + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + return rootActor.FindChildById(mCurrentFocusActor.second); +} + +Actor FocusManager::GetCurrentFocusGroup() +{ + return GetFocusGroup(GetCurrentFocusActor()); +} + +unsigned int FocusManager::GetCurrentFocusOrder() +{ + return mCurrentFocusActor.first; +} + +bool FocusManager::MoveFocusForward() +{ + bool ret = false; + mRecursiveFocusMoveCounter = 0; + + FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first); + if(focusIDIter != mFocusIDContainer.end()) + { + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first); + ret = DoMoveFocus(focusIDIter, true, mIsWrapped); + } + else + { + // TODO: if there is not focused actor, move first actor + if(!mFocusIDContainer.empty()) + { + //if there is not focused actor, move 1st actor + focusIDIter = mFocusIDContainer.begin(); // TODO: I'm not sure it was sorted. + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first); + ret = DoSetCurrentFocusActor((*focusIDIter).second); + } + } + + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!"); + + return ret; +} + +bool FocusManager::MoveFocusBackward() +{ + bool ret = false; + mRecursiveFocusMoveCounter = 0; + + FocusIDIter focusIDIter = mFocusIDContainer.find(mCurrentFocusActor.first); + if(focusIDIter != mFocusIDContainer.end()) + { + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first); + ret = DoMoveFocus(focusIDIter, false, mIsWrapped); + } + else + { + // TODO: if there is not focused actor, move last actor + if(!mFocusIDContainer.empty()) + { + //if there is not focused actor, move last actor + focusIDIter = mFocusIDContainer.end(); + --focusIDIter;//We want forward iterator to the last element here + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first); + ret = DoSetCurrentFocusActor((*focusIDIter).second); + } + } + + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s] %s\n", __FUNCTION__, ret?"SUCCEED!!!":"FAILED!!!"); + + return ret; +} + +void FocusManager::DoActivate(Actor actor) +{ + if(actor) + { + Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor); + if(control) + { + // Notify the control that it is activated + control.GetImplementation().OnActivated(); + } + + // Send notification for the activation of focused actor + mFocusedActorActivatedSignalV2.Emit(actor); + } +} + +void FocusManager::ClearFocus() +{ + Actor actor = GetCurrentFocusActor(); + if(actor) + { + actor.Remove(mFocusIndicatorActor); + } + + mCurrentFocusActor = FocusIDPair(0, 0); + + // Send notification for the change of focus actor + mFocusChangedSignalV2.Emit(actor, Actor()); + + if(mIsAccessibilityTtsEnabled) + { + // Stop the TTS playing if any + Dali::TtsPlayer player = Dali::TtsPlayer::Get(Dali::TtsPlayer::SCREEN_READER); + player.Stop(); + } +} + +void FocusManager::Reset() +{ + ClearFocus(); + mFocusIDContainer.clear(); + mIDAdditionalInfoContainer.clear(); +} + +void FocusManager::SetFocusGroup(Actor actor, bool isFocusGroup) +{ + if(actor) + { + // Create focus group property if not already created. + Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(Toolkit::FocusManager::IS_FOCUS_GROUP); + if(propertyIsFocusGroup == Property::INVALID_INDEX) + { + propertyIsFocusGroup = actor.RegisterProperty(Toolkit::FocusManager::IS_FOCUS_GROUP, isFocusGroup); + } + else + { + actor.SetProperty(propertyIsFocusGroup, isFocusGroup); + } + } +} + +bool FocusManager::IsFocusGroup(Actor actor) const +{ + // Check whether the actor is a focus group + bool isFocusGroup = false; + + if(actor) + { + Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(Toolkit::FocusManager::IS_FOCUS_GROUP); + if(propertyIsFocusGroup != Property::INVALID_INDEX) + { + isFocusGroup = actor.GetProperty(propertyIsFocusGroup); + } + } + + return isFocusGroup; +} + +Actor FocusManager::GetFocusGroup(Actor actor) +{ + // Go through the actor's hierarchy to check which focus group the actor belongs to + while (actor && !IsFocusGroup(actor)) + { + actor = actor.GetParent(); + } + + return actor; +} + +void FocusManager::SetGroupMode(bool enabled) +{ + mIsFocusWithinGroup = enabled; +} + +bool FocusManager::GetGroupMode() const +{ + return mIsFocusWithinGroup; +} + +void FocusManager::SetWrapMode(bool wrapped) +{ + mIsWrapped = wrapped; +} + +bool FocusManager::GetWrapMode() const +{ + return mIsWrapped; +} + +void FocusManager::SetFocusIndicatorActor(Actor indicator) +{ + mFocusIndicatorActor = indicator; +} + +Actor FocusManager::GetFocusIndicatorActor() +{ + return mFocusIndicatorActor; +} + +bool FocusManager::DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped) +{ + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] %d focusable actors\n", __FUNCTION__, __LINE__, mFocusIDContainer.size()); + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] focus order : %d\n", __FUNCTION__, __LINE__, (*focusIDIter).first); + + if( (forward && ++focusIDIter == mFocusIDContainer.end()) + || (!forward && focusIDIter-- == mFocusIDContainer.begin()) ) + { + if(wrapped) + { + if(forward) + { + focusIDIter = mFocusIDContainer.begin(); + } + else + { + focusIDIter = mFocusIDContainer.end(); + --focusIDIter;//We want forward iterator to the last element here + } + } + else + { + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Overshot\n", __FUNCTION__, __LINE__); + // Send notification for handling overshooted situation + mFocusOvershotSignalV2.Emit(GetCurrentFocusActor(), forward ? Toolkit::FocusManager::OVERSHOT_NEXT : Toolkit::FocusManager::OVERSHOT_PREVIOUS); + + return false; // Try to move the focus out of the scope + } + } + + if((focusIDIter != mFocusIDContainer.end()) && !DoSetCurrentFocusActor((*focusIDIter).second)) + { + mRecursiveFocusMoveCounter++; + if(mRecursiveFocusMoveCounter > mFocusIDContainer.size()) + { + // We've attempted to focus all the actors in the whole focus chain and no actor + // can be focused successfully. + + DALI_LOG_WARNING("[%s] There is no more focusable actor in %d focus chains\n", __FUNCTION__, mRecursiveFocusMoveCounter); + + return false; + } + else + { + return DoMoveFocus(focusIDIter, forward, wrapped); + } + } + + return true; +} + +void FocusManager::SetFocusable(Actor actor, bool focusable) +{ + if(actor) + { + // Create actor focusable property if not already created. + Property::Index propertyActorFocusable = actor.GetPropertyIndex(Toolkit::FocusManager::ACTOR_FOCUSABLE); + if(propertyActorFocusable == Property::INVALID_INDEX) + { + propertyActorFocusable = actor.RegisterProperty(Toolkit::FocusManager::ACTOR_FOCUSABLE, focusable); + } + else + { + actor.SetProperty(propertyActorFocusable, focusable); + } + } +} + +void FocusManager::CreateDefaultFocusIndicatorActor() +{ + // Create a focus indicator actor shared by all the focusable actors + Image borderImage = Image::New(FOCUS_BORDER_IMAGE_PATH); + + ImageActor focusIndicator = ImageActor::New(borderImage); + focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION ); + focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH ); + focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER); + focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f)); + + // Apply size constraint to the focus indicator + Constraint constraint = Constraint::New(Actor::SIZE, + ParentSource(Actor::SIZE), + EqualToConstraint()); + focusIndicator.ApplyConstraint(constraint); + + SetFocusIndicatorActor(focusIndicator); +} + +bool FocusManager::ChangeAccessibilityStatus() +{ + AccessibilityManager manager = AccessibilityManager::Get(); + mIsAccessibilityTtsEnabled = manager.IsEnabled(); + + if(mIsAccessibilityTtsEnabled) + { + // Show indicator when tts turned on if there is focused actor. + Actor actor = GetCurrentFocusActor(); + if(actor) + { + if(mFocusIndicatorActor) + { + actor.Add(mFocusIndicatorActor); + } + } + mIsFocusIndicatorEnabled = true; + } + else + { + // Hide indicator when tts turned off + Actor actor = GetCurrentFocusActor(); + if(actor) + { + actor.Remove(mFocusIndicatorActor); + } + mIsFocusIndicatorEnabled = false; + } + + return true; +} + +bool FocusManager::AccessibilityActionNext() +{ + if(mIsAccessibilityTtsEnabled) + { + return MoveFocusForward(); + } + else + { + return false; + } +} + +bool FocusManager::AccessibilityActionPrevious() +{ + if(mIsAccessibilityTtsEnabled) + { + return MoveFocusBackward(); + } + else + { + return false; + } +} + +bool FocusManager::AccessibilityActionActivate() +{ + bool ret = false; + + Actor actor = GetCurrentFocusActor(); + if(actor) + { + DoActivate(actor); + ret = true; + } + + return ret; +} + +bool FocusManager::AccessibilityActionRead(bool allowReadAgain) +{ + bool ret = false; + + if(mIsAccessibilityTtsEnabled) + { + // Find the focusable actor at the read position + AccessibilityManager manager = AccessibilityManager::Get(); + Dali::HitTestAlgorithm::Results results; + Dali::HitTestAlgorithm::HitTest( Stage::GetCurrent(), manager.GetReadPosition(), results, IsActorFocusableFunction ); + + FocusIDIter focusIDIter = mFocusIDContainer.find(GetFocusOrder(results.actor)); + if(focusIDIter != mFocusIDContainer.end()) + { + if( allowReadAgain || (results.actor != GetCurrentFocusActor()) ) + { + // Move the focus to the actor + ret = SetCurrentFocusActor(results.actor); + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SetCurrentFocusActor returns %s\n", __FUNCTION__, __LINE__, ret?"TRUE":"FALSE"); + } + } + } + + return ret; +} + +bool FocusManager::AccessibilityActionReadNext() +{ + if(mIsAccessibilityTtsEnabled) + { + return MoveFocusForward(); + } + else + { + return false; + } +} + +bool FocusManager::AccessibilityActionReadPrevious() +{ + if(mIsAccessibilityTtsEnabled) + { + return MoveFocusBackward(); + } + else + { + return false; + } +} + +bool FocusManager::AccessibilityActionUp() +{ + bool ret = false; + + if(mIsAccessibilityTtsEnabled) + { + Actor actor = GetCurrentFocusActor(); + if(actor) + { + Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor); + if(control) + { + // Notify the control that it is activated + ret = control.GetImplementation().OnAccessibilityValueChange(true); + } + } + } + + return ret; +} + +bool FocusManager::AccessibilityActionDown() +{ + bool ret = false; + + if(mIsAccessibilityTtsEnabled) + { + Actor actor = GetCurrentFocusActor(); + if(actor) + { + Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor); + if(control) + { + // Notify the control that it is activated + ret = control.GetImplementation().OnAccessibilityValueChange(false); + } + } + } + + return ret; +} + +bool FocusManager::ClearAccessibilityFocus() +{ + if(mIsAccessibilityTtsEnabled) + { + ClearFocus(); + return true; + } + else + { + return false; + } +} + +bool FocusManager::AccessibilityActionBack() +{ + // TODO: Back to previous view + + return mIsAccessibilityTtsEnabled; +} + +bool FocusManager::HandlePanGesture(const Integration::PanGestureEvent& panEvent) +{ + bool handled = false; + + Actor currentGesturedActor = GetCurrentFocusActor(); + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + + Dali::PanGesture pan(panEvent.state); + pan.time = panEvent.time; + pan.numberOfTouches = panEvent.numberOfTouches; + pan.screenPosition = panEvent.currentPosition; + pan.screenDisplacement = panEvent.previousPosition - panEvent.currentPosition; + pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta; + pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta; + + // Only handle the pan gesture when the current focused actor is scrollable or within a scrollable actor + while(currentGesturedActor && currentGesturedActor != rootActor && !handled) + { + Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(currentGesturedActor); + if(control) + { + Vector2 localCurrent; + control.ScreenToLocal( localCurrent.x, localCurrent.y, panEvent.currentPosition.x, panEvent.currentPosition.y ); + pan.position = localCurrent; + + Vector2 localPrevious; + control.ScreenToLocal( localPrevious.x, localPrevious.y, panEvent.previousPosition.x, panEvent.previousPosition.y ); + + pan.displacement = localCurrent - localPrevious; + pan.velocity.x = pan.displacement.x / panEvent.timeDelta; + pan.velocity.y = pan.displacement.y / panEvent.timeDelta; + + handled = control.GetImplementation().OnAccessibilityPan(pan); + } + + // If the gesture is not handled by the control, check its parent + if(!handled) + { + currentGesturedActor = currentGesturedActor.GetParent(); + } + else + { + // If handled, then update the pan gesture properties + PanGestureDetector::SetPanGestureProperties( pan ); + } + } + + return handled; +} + +Toolkit::FocusManager::FocusChangedSignalV2& FocusManager::FocusChangedSignal() +{ + return mFocusChangedSignalV2; +} + +Toolkit::FocusManager::FocusOvershotSignalV2& FocusManager::FocusOvershotSignal() +{ + return mFocusOvershotSignalV2; +} + +Toolkit::FocusManager::FocusedActorActivatedSignalV2& FocusManager::FocusedActorActivatedSignal() +{ + return mFocusedActorActivatedSignalV2; +} + +bool FocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + FocusManager* manager = dynamic_cast(object); + + if( Dali::Toolkit::FocusManager::SIGNAL_FOCUS_CHANGED == signalName ) + { + manager->FocusChangedSignal().Connect( tracker, functor ); + } + else if( Dali::Toolkit::FocusManager::SIGNAL_FOCUS_OVERSHOT == signalName ) + { + manager->FocusOvershotSignal().Connect( tracker, functor ); + } + else if( Dali::Toolkit::FocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED== signalName ) + { + manager->FocusedActorActivatedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/focus-manager/focus-manager-impl.h b/dali-toolkit/internal/focus-manager/focus-manager-impl.h new file mode 100644 index 0000000..aefd0f6 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/focus-manager-impl.h @@ -0,0 +1,406 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_INTERNAL_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class FocusManager; + +/** + * @copydoc Toolkit::FocusManager + */ +class FocusManager : public Dali::BaseObject, Dali::AccessibilityActionHandler, Dali::AccessibilityGestureHandler +{ +public: + + struct ActorAdditionalInfo + { + ActorAdditionalInfo() + : mFocusOrder(0) + { + } + + unsigned int mFocusOrder; ///< The focus order of the actor. It is undefined by default. + + std::string mAccessibilityAttributes[Toolkit::FocusManager::ACCESSIBILITY_ATTRIBUTE_NUM]; ///< The array of attribute texts + }; + + typedef std::pair FocusIDPair; + typedef std::map FocusIDContainer; + typedef FocusIDContainer::iterator FocusIDIter; + typedef FocusIDContainer::const_iterator FocusIDConstIter; + + typedef std::pair IDAdditionalInfoPair; + typedef std::map IDAdditionalInfoContainer; + typedef IDAdditionalInfoContainer::iterator IDAdditionalInfoIter; + typedef IDAdditionalInfoContainer::const_iterator IDAdditionalInfoConstIter; + + /** + * Construct a new FocusManager. + */ + FocusManager(); + + /** + * @copydoc Toolkit::FocusManager::SetAccessibilityAttribute + */ + void SetAccessibilityAttribute(Actor actor, Toolkit::FocusManager::AccessibilityAttribute type, const std::string& text); + + /** + * @copydoc Toolkit::FocusManager::GetAccessibilityAttribute + */ + std::string GetAccessibilityAttribute(Actor actor, Toolkit::FocusManager::AccessibilityAttribute type) const; + + /** + * @copydoc Toolkit::FocusManager::SetFocusOrder + */ + void SetFocusOrder(Actor actor, const unsigned int order); + + /** + * @copydoc Toolkit::FocusManager::GetFocusOrder + */ + unsigned int GetFocusOrder(Actor actor) const; + + /** + * @copydoc Toolkit::FocusManager::GenerateNewFocusOrder + */ + unsigned int GenerateNewFocusOrder() const; + + /** + * @copydoc Toolkit::FocusManager::GetActorByFocusOrder + */ + Actor GetActorByFocusOrder(const unsigned int order); + + /** + * @copydoc Toolkit::FocusManager::SetCurrentFocusActor + */ + bool SetCurrentFocusActor(Actor actor); + + /** + * @copydoc Toolkit::FocusManager::GetCurrentFocusActor + */ + Actor GetCurrentFocusActor(); + + /** + * @copydoc Toolkit::FocusManager::GetCurrentFocusGroup + */ + Actor GetCurrentFocusGroup(); + + /** + * @copydoc Toolkit::FocusManager::GetCurrentFocusOrder + */ + unsigned int GetCurrentFocusOrder(); + + /** + * @copydoc Toolkit::FocusManager::MoveFocusForward + */ + bool MoveFocusForward(); + + /** + * @copydoc Toolkit::FocusManager::MoveFocusBackward + */ + bool MoveFocusBackward(); + + /** + * @copydoc Toolkit::FocusManager::ClearFocus + */ + void ClearFocus(); + + /** + * @copydoc Toolkit::FocusManager::Reset + */ + void Reset(); + + /** + * @copydoc Toolkit::FocusManager::SetFocusGroup + */ + void SetFocusGroup(Actor actor, bool isFocusGroup); + + /** + * @copydoc Toolkit::FocusManager::IsFocusGroup + */ + bool IsFocusGroup(Actor actor) const; + + /** + * @copydoc Toolkit::FocusManager::SetGroupMode + */ + void SetGroupMode(bool enabled); + + /** + * @copydoc Toolkit::FocusManager::GetGroupMode + */ + bool GetGroupMode() const; + + /** + * @copydoc Toolkit::FocusManager::SetWrapMode + */ + void SetWrapMode(bool wrapped); + + /** + * @copydoc Toolkit::FocusManager::GetWrapMode + */ + bool GetWrapMode() const; + + /** + * @copydoc Toolkit::FocusManager::SetFocusIndicatorActor + */ + void SetFocusIndicatorActor(Actor indicator); + + /** + * @copydoc Toolkit::FocusManager::GetFocusIndicatorActor + */ + Actor GetFocusIndicatorActor(); + + /** + * @copydoc Toolkit::FocusManager::GetFocusGroup + */ + Actor GetFocusGroup(Actor actor); + +public: + + /** + * @copydoc Toolkit::FocusManager::FocusChangedSignal() + */ + Toolkit::FocusManager::FocusChangedSignalV2& FocusChangedSignal(); + + /** + * @copydoc Toolkit::FocusManager::FocusOvershotSignal() + */ + Toolkit::FocusManager::FocusOvershotSignalV2& FocusOvershotSignal(); + + /** + * @copydoc Toolkit::FocusManager::FocusedActorActivatedSignal() + */ + Toolkit::FocusManager::FocusedActorActivatedSignalV2& FocusedActorActivatedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: + + /** + * Destructor + */ + virtual ~FocusManager(); + +private: + + /** + * Get the additional information (e.g. focus order and description) of the given actor. + * @param actorID The ID of the actor to be queried + * @return The additional information of the actor + */ + ActorAdditionalInfo GetActorAdditionalInfo(const unsigned int actorID) const; + + /** + * Synchronize the actor's additional information to reflect its latest focus order + * @param actorID The ID of the actor + * @param order The focus order of the actor + * @return The additional information of the actor + */ + void SynchronizeActorAdditionalInfo(const unsigned int actorID, const unsigned int order); + + /** + * Move the focus to the specified actor and send notification for the focus change. + * @param actorID The ID of the actor to be queried + * @return Whether the focus is successful or not + */ + bool DoSetCurrentFocusActor(const unsigned int actorID); + + /** + * Move the focus to the next actor in the focus chain towards the specified direction. + * @param focusIDIter The iterator pointing to the current focused actor + * @param forward Whether the focus movement is forward or not. The focus movement will be backward if this is false. + * @param wrapped Whether the focus shoule be moved wrapped around or not + * @return Whether the focus is successful or not + */ + bool DoMoveFocus(FocusIDIter focusIDIter, bool forward, bool wrapped); + + /** + * Activate the actor. If the actor is control, call OnActivated virtual function. + * This function will emit FocusedActorActivatedSignal. + * @param actor The actor to activate + */ + void DoActivate(Actor actor); + + /** + * Create the default indicator actor to highlight the focused actor. + */ + void CreateDefaultFocusIndicatorActor(); + + /** + * Set whether the actor is focusable or not. A focusable property will be registered for + * the actor if not yet. + * @param actor The actor to be focused + * @param focusable Whether the actor is focusable or not + */ + void SetFocusable(Actor actor, bool focusable); + + /** + * Handle the accessibility pan gesture. + * @param[in] panEvent The pan event to be handled. + * @return whether the gesture is handled successfully or not. + */ + virtual bool HandlePanGesture(const Integration::PanGestureEvent& panEvent); + + /** + * Change the accessibility status when Accessibility feature(screen-reader) turned on or off. + * @return whether the status is changed or not. + */ + virtual bool ChangeAccessibilityStatus(); + + /** + * Clear the accessibility focus from the current focused actor. + * @return whether the focus is cleared or not. + */ + virtual bool ClearAccessibilityFocus(); + + /** + * Perform the accessibility action to move focus to the previous focusable actor (by one finger flick up). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionPrevious(); + + /** + * Perform the accessibility action to move focus to the next focusable actor (by one finger flick down). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionNext(); + + /** + * Perform the accessibility action to move focus to the previous focusable actor (by one finger flick left). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionReadPrevious(); + + /** + * Perform the accessibility action to move focus to the next focusable actor (by one finger flick right). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionReadNext(); + + /** + * Perform the accessibility action to focus and read the actor (by one finger tap or move). + * @param allowReadAgain true if the action read again the same object (i.e. read action) + * false if the action just read when the focus object is changed (i.e. over action) + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionRead(bool allowReadAgain); + + /** + * Perform the accessibility action to activate the current focused actor (by one finger double tap). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionActivate(); + + /** + * Perform the accessibility action to change the value when the current focused actor is a slider + * (by double finger down and move up and right). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionUp(); + + /** + * Perform the accessibility action to change the value when the current focused actor is a slider + * (by double finger down and move down and left). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionDown(); + + /** + * Perform the accessibility action to navigate back (by two fingers circle draw). + * @return whether the accessibility action is performed or not. + */ + virtual bool AccessibilityActionBack(); + +private: + + // Undefined + FocusManager(const FocusManager&); + + FocusManager& operator=(const FocusManager& rhs); + +private: + + Toolkit::FocusManager::FocusChangedSignalV2 mFocusChangedSignalV2; ///< The signal to notify the focus change + Toolkit::FocusManager::FocusOvershotSignalV2 mFocusOvershotSignalV2; ///< The signal to notify the focus overshooted + Toolkit::FocusManager::FocusedActorActivatedSignalV2 mFocusedActorActivatedSignalV2; ///< The signal to notify the activation of focused actor + + bool mIsWrapped; ///< Whether the focus movement is wrapped around or not + bool mIsFocusWithinGroup; ///< Whether the focus movement is limited to the current focus group or not + + FocusIDContainer mFocusIDContainer; ///< The container to look up actor ID by focus order + IDAdditionalInfoContainer mIDAdditionalInfoContainer; ///< The container to look up additional information by actor ID + + FocusIDPair mCurrentFocusActor; ///< The focus order and actor ID of current focused actor + + Actor mFocusIndicatorActor; ///< The focus indicator actor shared by all the focusable actors for highlight + + unsigned int mRecursiveFocusMoveCounter; ///< The counter to count the number of recursive focus movement attempted before the focus movement is successful. + + bool mIsAccessibilityTtsEnabled; ///< Whether accessibility feature(screen-reader) turned on/off + + bool mIsFocusIndicatorEnabled; ///< Whether indicator should be shown / hidden. It could be enabled when TTS enabled or 'Tab' key operated. +}; + +} // namespace Internal + +inline Internal::FocusManager& GetImpl(Dali::Toolkit::FocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::FocusManager& GetImpl(const Dali::Toolkit::FocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_FOCUS_MANAGER_H__ diff --git a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp new file mode 100644 index 0000000..7b1d929 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.cpp @@ -0,0 +1,720 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "keyboard-focus-manager-impl.h" + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace // unnamed namespace +{ + +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_KEYBOARD_FOCUS_MANAGER"); +#endif + +const std::string IS_FOCUS_GROUP_PROPERTY_NAME("is-keyboard-focus-group"); // This property will be replaced by a flag in ControlImpl. + +const char* FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "keyboard_focus.png"; +const Vector4 FOCUS_BORDER_IMAGE_BORDER = Vector4(7.0f, 7.0f, 7.0f, 7.0f); + +BaseHandle Create() +{ + BaseHandle handle = KeyboardFocusManager::Get(); + + if ( !handle && Adaptor::IsAvailable() ) + { + Toolkit::KeyboardFocusManager manager = Toolkit::KeyboardFocusManager( new Internal::KeyboardFocusManager() ); + Adaptor::Get().RegisterSingleton( typeid( manager ), manager ); + handle = manager; + } + + return handle; +} +TypeRegistration KEYBOARD_FOCUS_MANAGER_TYPE( typeid(Dali::Toolkit::KeyboardFocusManager), typeid(Dali::BaseHandle), Create, true /* Create instance at startup */ ); + +} // unnamed namespace + +Toolkit::KeyboardFocusManager KeyboardFocusManager::Get() +{ + Toolkit::KeyboardFocusManager manager; + + if ( Adaptor::IsAvailable() ) + { + // Check whether the keyboard focus manager is already created + Dali::BaseHandle handle = Dali::Adaptor::Get().GetSingleton( typeid( Toolkit::KeyboardFocusManager ) ); + if(handle) + { + // If so, downcast the handle of singleton to keyboard focus manager + manager = Toolkit::KeyboardFocusManager( dynamic_cast< KeyboardFocusManager* >( handle.GetObjectPtr() ) ); + } + } + + return manager; +} + +KeyboardFocusManager::KeyboardFocusManager() +: mCurrentFocusActor(0), + mFocusIndicatorActor(Actor()), + mFocusGroupLoopEnabled(false), + mIsKeyboardFocusEnabled(false), + mIsFocusIndicatorEnabled(false), + mIsWaitingKeyboardFocusChangeCommit(false), + mSlotDelegate(this) +{ + CreateDefaultFocusIndicatorActor(); + + OnPhysicalKeyboardStatusChanged(PhysicalKeyboard::Get()); + + Toolkit::KeyInputFocusManager::Get().UnhandledKeyEventSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnKeyEvent); + Stage::GetCurrent().TouchedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnTouched); + PhysicalKeyboard::Get().StatusChangedSignal().Connect(mSlotDelegate, &KeyboardFocusManager::OnPhysicalKeyboardStatusChanged); +} + +KeyboardFocusManager::~KeyboardFocusManager() +{ +} + +bool KeyboardFocusManager::SetCurrentFocusActor(Actor actor) +{ + DALI_ASSERT_DEBUG( !mIsWaitingKeyboardFocusChangeCommit && "Calling this function in the PreFocusChangeSignal callback?" ); + + if(actor) + { + return DoSetCurrentFocusActor(actor.GetId()); + } + + return false; +} + +bool KeyboardFocusManager::DoSetCurrentFocusActor(const unsigned int actorID) +{ + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + Actor actor = rootActor.FindChildById(actorID); + + // Check whether the actor is in the stage + if(actor) + { + // Set the focus only when the actor is keyboard focusable + if(actor.IsKeyboardFocusable()) + { + // Draw the focus indicator upon the focused actor + if(mIsFocusIndicatorEnabled && mFocusIndicatorActor) + { + actor.Add(mFocusIndicatorActor); + } + + // Send notification for the change of focus actor + if( !mFocusChangedSignalV2.Empty() ) + { + mFocusChangedSignalV2.Emit(GetCurrentFocusActor(), actor); + } + + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] Focus Changed\n", __FUNCTION__, __LINE__); + + // Save the current focused actor + mCurrentFocusActor = actorID; + + // Move the accessibility focus to the same actor +// Toolkit::FocusManager focusManager = Toolkit::FocusManager::Get(); +// focusManager.SetCurrentFocusActor(actor); + + DALI_LOG_INFO( gLogFilter, Debug::General, "[%s:%d] SUCCEED\n", __FUNCTION__, __LINE__); + return true; + } + } + + DALI_LOG_WARNING("[%s:%d] FAILED\n", __FUNCTION__, __LINE__); + return false; +} + +Actor KeyboardFocusManager::GetCurrentFocusActor() +{ + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + return rootActor.FindChildById(mCurrentFocusActor); +} + +Actor KeyboardFocusManager::GetCurrentFocusGroup() +{ + return GetFocusGroup(GetCurrentFocusActor()); +} + +bool KeyboardFocusManager::IsLayoutControl(Actor actor) const +{ + Toolkit::Control control = Toolkit::Control::DownCast(actor); + return control && control.GetImplementation().IsKeyboardNavigationSupported(); +} + +Toolkit::Control KeyboardFocusManager::GetParentLayoutControl(Actor actor) const +{ + // Get the actor's parent layout control that supports two dimensional keyboard navigation + Actor rootActor = Stage::GetCurrent().GetRootLayer(); + Actor parent; + if(actor) + { + parent = actor.GetParent(); + } + + while( parent && !IsLayoutControl(parent) && parent != rootActor ) + { + parent = parent.GetParent(); + } + + return Toolkit::Control::DownCast(parent); +} + +bool KeyboardFocusManager::MoveFocus(Toolkit::Control::KeyboardFocusNavigationDirection direction) +{ + Actor currentFocusActor = GetCurrentFocusActor(); + + bool succeed = false; + + // Go through the actor's hierarchy until we find a layout control that knows how to move the focus + Toolkit::Control parentLayoutControl = GetParentLayoutControl(currentFocusActor); + while(parentLayoutControl && !succeed) + { + succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, currentFocusActor, direction); + parentLayoutControl = GetParentLayoutControl(parentLayoutControl); + } + + if(!succeed && !mPreFocusChangeSignalV2.Empty()) + { + // Don't know how to move the focus further. The application needs to tell us which actor to move the focus to + mIsWaitingKeyboardFocusChangeCommit = true; + Actor nextFocusableActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, Actor(), direction); + mIsWaitingKeyboardFocusChangeCommit = false; + + if ( nextFocusableActor && nextFocusableActor.IsKeyboardFocusable() ) + { + // Whether the next focusable actor is a layout control + if(IsLayoutControl(nextFocusableActor)) + { + // If so, move the focus inside it. + Toolkit::Control layoutControl = Toolkit::Control::DownCast(nextFocusableActor); + succeed = DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction); + } + else + { + // Otherwise, just set focus to the next focusable actor + succeed = SetCurrentFocusActor(nextFocusableActor); + } + } + } + + return succeed; +} + +bool KeyboardFocusManager::DoMoveFocusWithinLayoutControl(Toolkit::Control control, Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction) +{ + // Ask the control for the next actor to focus + Actor nextFocusableActor = control.GetImplementation().GetNextKeyboardFocusableActor(actor, direction, mFocusGroupLoopEnabled); + if(nextFocusableActor) + { + if(!nextFocusableActor.IsKeyboardFocusable()) + { + // If the actor is not focusable, ask the same layout control for the next actor to focus + return DoMoveFocusWithinLayoutControl(control, nextFocusableActor, direction); + } + else + { + Actor currentFocusActor = GetCurrentFocusActor(); + Actor committedFocusActor = nextFocusableActor; + + // We will try to move the focus to the actor. Emit a signal to notify the proposed actor to focus + // Signal handler can check the proposed actor and return a different actor if it wishes. + if( !mPreFocusChangeSignalV2.Empty() ) + { + mIsWaitingKeyboardFocusChangeCommit = true; + committedFocusActor = mPreFocusChangeSignalV2.Emit(currentFocusActor, nextFocusableActor, direction); + mIsWaitingKeyboardFocusChangeCommit = false; + } + + if (committedFocusActor && committedFocusActor.IsKeyboardFocusable()) + { + // Whether the commited focusable actor is a layout control + if(IsLayoutControl(committedFocusActor)) + { + // If so, move the focus inside it. + Toolkit::Control layoutControl = Toolkit::Control::DownCast(committedFocusActor); + return DoMoveFocusWithinLayoutControl(layoutControl, currentFocusActor, direction); + } + else + { + // Otherwise, just set focus to the next focusable actor + if(committedFocusActor == nextFocusableActor) + { + // If the application hasn't changed our proposed actor, we informs the layout control we will + // move the focus to what the control returns. The control might wish to perform some actions + // before the focus is actually moved. + control.GetImplementation().OnKeyboardFocusChangeCommitted(committedFocusActor); + } + + return SetCurrentFocusActor(committedFocusActor); + } + } + else + { + return false; + } + } + } + else + { + // No more actor can be focused in the given direction within the same layout control. + return false; + } +} + +bool KeyboardFocusManager::DoMoveFocusToNextFocusGroup(bool forward) +{ + bool succeed = false; + + // Get the parent layout control of the current focus group + Toolkit::Control parentLayoutControl = GetParentLayoutControl(GetCurrentFocusGroup()); + + while(parentLayoutControl && !succeed) + { + // If the current focus group has a parent layout control, we can probably automatically + // move the focus to the next focus group in the forward or backward direction. + Toolkit::Control::KeyboardFocusNavigationDirection direction = forward ? Toolkit::Control::Right : Toolkit::Control::Left; + succeed = DoMoveFocusWithinLayoutControl(parentLayoutControl, GetCurrentFocusActor(), direction); + parentLayoutControl = GetParentLayoutControl(parentLayoutControl); + } + + if(!mFocusGroupChangedSignalV2.Empty()) + { + // Emit a focus group changed signal. The applicaton can move the focus to a new focus group + mFocusGroupChangedSignalV2.Emit(GetCurrentFocusActor(), forward); + } + + return succeed; +} + +void KeyboardFocusManager::DoActivate(Actor actor) +{ + if(actor) + { + Toolkit::Control control = Toolkit::Control::DownCast(actor); + if(control) + { + // Notify the control that it is activated + control.GetImplementation().OnActivated(); + } + + // Send notification for the activation of focused actor + if( !mFocusedActorActivatedSignalV2.Empty() ) + { + mFocusedActorActivatedSignalV2.Emit(actor); + } + } +} + +void KeyboardFocusManager::ClearFocus() +{ + Actor actor = GetCurrentFocusActor(); + if(actor) + { + actor.Remove(mFocusIndicatorActor); + + // Send notification for the change of focus actor + if( !mFocusChangedSignalV2.Empty() ) + { + mFocusChangedSignalV2.Emit(actor, Actor()); + } + } + + mCurrentFocusActor = 0; + mIsFocusIndicatorEnabled = false; +} + +void KeyboardFocusManager::SetFocusGroupLoop(bool enabled) +{ + mFocusGroupLoopEnabled = enabled; +} + +bool KeyboardFocusManager::GetFocusGroupLoop() const +{ + return mFocusGroupLoopEnabled; +} + +void KeyboardFocusManager::SetAsFocusGroup(Actor actor, bool isFocusGroup) +{ + if(actor) + { + // Create focus group property if not already created. + Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME); + if(propertyIsFocusGroup == Property::INVALID_INDEX) + { + propertyIsFocusGroup = actor.RegisterProperty(IS_FOCUS_GROUP_PROPERTY_NAME, isFocusGroup); + } + else + { + actor.SetProperty(propertyIsFocusGroup, isFocusGroup); + } + } +} + +bool KeyboardFocusManager::IsFocusGroup(Actor actor) const +{ + // Check whether the actor is a focus group + bool isFocusGroup = false; + + if(actor) + { + Property::Index propertyIsFocusGroup = actor.GetPropertyIndex(IS_FOCUS_GROUP_PROPERTY_NAME); + if(propertyIsFocusGroup != Property::INVALID_INDEX) + { + isFocusGroup = actor.GetProperty(propertyIsFocusGroup); + } + } + + return isFocusGroup; +} + +Actor KeyboardFocusManager::GetFocusGroup(Actor actor) +{ + // Go through the actor's hierarchy to check which focus group the actor belongs to + while (actor && !IsFocusGroup(actor)) + { + actor = actor.GetParent(); + } + + return actor; +} + +void KeyboardFocusManager::SetFocusIndicatorActor(Actor indicator) +{ + mFocusIndicatorActor = indicator; +} + +Actor KeyboardFocusManager::GetFocusIndicatorActor() +{ + return mFocusIndicatorActor; +} + +void KeyboardFocusManager::CreateDefaultFocusIndicatorActor() +{ + // Create a focus indicator actor shared by all the keyboard focusable actors + Image borderImage = Image::New(FOCUS_BORDER_IMAGE_PATH); + + ImageActor focusIndicator = ImageActor::New(borderImage); + focusIndicator.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION_PLUS_LOCAL_POSITION ); + focusIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH ); + focusIndicator.SetNinePatchBorder(FOCUS_BORDER_IMAGE_BORDER); + focusIndicator.SetPosition(Vector3(0.0f, 0.0f, 1.0f)); + + // Apply size constraint to the focus indicator + Constraint constraint = Constraint::New(Actor::SIZE, + ParentSource(Actor::SIZE), + EqualToConstraint()); + focusIndicator.ApplyConstraint(constraint); + + SetFocusIndicatorActor(focusIndicator); +} + +void KeyboardFocusManager::OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard) +{ + mIsKeyboardFocusEnabled = keyboard.IsAttached(); + + if(mIsKeyboardFocusEnabled) + { + // Show indicator when keyboard focus turned on if there is focused actor. + Actor actor = GetCurrentFocusActor(); + if(actor) + { + if(mFocusIndicatorActor) + { + actor.Add(mFocusIndicatorActor); + } + } + mIsFocusIndicatorEnabled = true; + } + else + { + // Hide indicator when keyboard focus turned off + Actor actor = GetCurrentFocusActor(); + if(actor) + { + actor.Remove(mFocusIndicatorActor); + } + mIsFocusIndicatorEnabled = false; + } +} + +void KeyboardFocusManager::OnKeyEvent(const KeyEvent& event) +{ + if(!mIsKeyboardFocusEnabled) + { + return; + } + + AccessibilityManager accessibilityManager = AccessibilityManager::Get(); + bool isAccessibilityEnabled = accessibilityManager.IsEnabled(); + + Toolkit::FocusManager accessibilityFocusManager = Toolkit::FocusManager::Get(); + + std::string keyName = event.keyPressedName; + + bool isFocusStartableKey = false; + + if(event.state == KeyEvent::Down) + { + if (keyName == "Left") + { + if(!isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // Move the focus towards left + MoveFocus(Toolkit::Control::Left); + } + + isFocusStartableKey = true; + } + else + { + // Move the accessibility focus backward + accessibilityFocusManager.MoveFocusBackward(); + } + } + else if (keyName == "Right") + { + if(!isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // Move the focus towards right + MoveFocus(Toolkit::Control::Right); + } + + isFocusStartableKey = true; + } + else + { + // Move the accessibility focus forward + accessibilityFocusManager.MoveFocusForward(); + } + + isFocusStartableKey = true; + } + else if (keyName == "Up" && !isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // Move the focus towards up + MoveFocus(Toolkit::Control::Up); + } + + isFocusStartableKey = true; + } + else if (keyName == "Down" && !isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // Move the focus towards down + MoveFocus(Toolkit::Control::Down); + } + + isFocusStartableKey = true; + } + else if (keyName == "Tab" && !isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // "Tab" key changes the focus group in the forward direction and + // "Shift-Tab" key changes it in the backward direction. + DoMoveFocusToNextFocusGroup(!event.IsShiftModifier()); + } + + isFocusStartableKey = true; + } + else if (keyName == "space" && !isAccessibilityEnabled) + { + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + + isFocusStartableKey = true; + } + else if (keyName == "" && !isAccessibilityEnabled) + { + // Check the fake key event for evas-plugin case + if(!mIsFocusIndicatorEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + + isFocusStartableKey = true; + } + else if (keyName == "Backspace" && !isAccessibilityEnabled) + { + // Emit signal to go back to the previous view??? + } + } + else if(event.state == KeyEvent::Up) + { + if (keyName == "Return") + { + if(!mIsFocusIndicatorEnabled && !isAccessibilityEnabled) + { + // Show focus indicator + mIsFocusIndicatorEnabled = true; + } + else + { + // Activate the focused actor + Actor actor; + if(!isAccessibilityEnabled) + { + actor = GetCurrentFocusActor(); + } + else + { + actor = accessibilityFocusManager.GetCurrentFocusActor(); + } + + if(actor) + { + DoActivate(actor); + } + } + + isFocusStartableKey = true; + } + } + + if(isFocusStartableKey && mIsFocusIndicatorEnabled && !isAccessibilityEnabled) + { + Actor actor = GetCurrentFocusActor(); + if( !actor ) + { + // No actor is focused but keyboard focus is activated by the key press + // Let's try to move the initial focus + MoveFocus(Toolkit::Control::Right); + } + else if(mFocusIndicatorActor) + { + // Make sure the focused actor is highlighted + actor.Add(mFocusIndicatorActor); + } + } +} + +void KeyboardFocusManager::OnTouched(const TouchEvent& touchEvent) +{ + // Clear the focus when user touch the screen + ClearFocus(); +} + +Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2& KeyboardFocusManager::PreFocusChangeSignal() +{ + return mPreFocusChangeSignalV2; +} + +Toolkit::KeyboardFocusManager::FocusChangedSignalV2& KeyboardFocusManager::FocusChangedSignal() +{ + return mFocusChangedSignalV2; +} + +Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2& KeyboardFocusManager::FocusGroupChangedSignal() +{ + return mFocusGroupChangedSignalV2; +} + +Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2& KeyboardFocusManager::FocusedActorActivatedSignal() +{ + return mFocusedActorActivatedSignalV2; +} + +bool KeyboardFocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + KeyboardFocusManager* manager = dynamic_cast(object); + + if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_PRE_FOCUS_CHANGE == signalName ) + { + manager->PreFocusChangeSignal().Connect( tracker, functor ); + } + if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_CHANGED == signalName ) + { + manager->FocusChangedSignal().Connect( tracker, functor ); + } + if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUS_GROUP_CHANGED == signalName ) + { + manager->FocusGroupChangedSignal().Connect( tracker, functor ); + } + else if( Dali::Toolkit::KeyboardFocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED== signalName ) + { + manager->FocusedActorActivatedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h new file mode 100644 index 0000000..a30b562 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h @@ -0,0 +1,285 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_KEYBOARD_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_INTERNAL_KEYBOARD_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * @copydoc Toolkit::KeyboardFocusManager + */ +class KeyboardFocusManager : public Dali::BaseObject +{ +public: + + /** + * @copydoc Toolkit::KeyboardFocusManager::Get + */ + static Toolkit::KeyboardFocusManager Get(); + + /** + * Construct a new KeyboardFocusManager. + */ + KeyboardFocusManager(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::SetCurrentFocusActor + */ + bool SetCurrentFocusActor(Actor actor); + + /** + * @copydoc Toolkit::KeyboardFocusManager::GetCurrentFocusActor + */ + Actor GetCurrentFocusActor(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::MoveFocus + */ + bool MoveFocus(Toolkit::Control::KeyboardFocusNavigationDirection direction); + + /** + * @copydoc Toolkit::KeyboardFocusManager::ClearFocus + */ + void ClearFocus(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::SetAsFocusGroup + */ + void SetAsFocusGroup(Actor actor, bool isFocusGroup); + + /** + * @copydoc Toolkit::KeyboardFocusManager::IsFocusGroup + */ + bool IsFocusGroup(Actor actor) const; + + /** + * @copydoc Toolkit::KeyboardFocusManager::GetFocusGroup + */ + Actor GetFocusGroup(Actor actor); + + /** + * @copydoc Toolkit::KeyboardFocusManager::SetFocusGroupLoop + */ + void SetFocusGroupLoop(bool enabled); + + /** + * @copydoc Toolkit::KeyboardFocusManager::GetFocusGroupLoop + */ + bool GetFocusGroupLoop() const; + + /** + * @copydoc Toolkit::KeyboardFocusManager::SetFocusIndicatorActor + */ + void SetFocusIndicatorActor(Actor indicator); + + /** + * @copydoc Toolkit::KeyboardFocusManager::GetFocusIndicatorActor + */ + Actor GetFocusIndicatorActor(); + +public: + + /** + * @copydoc Toolkit::KeyboardFocusManager::PreFocusChangeSignal() + */ + Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2& PreFocusChangeSignal(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::FocusChangedSignal() + */ + Toolkit::KeyboardFocusManager::FocusChangedSignalV2& FocusChangedSignal(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::FocusGroupChangedSignal() + */ + Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2& FocusGroupChangedSignal(); + + /** + * @copydoc Toolkit::KeyboardFocusManager::FocusedActorActivatedSignal() + */ + Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2& FocusedActorActivatedSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: + + /** + * Destructor + */ + virtual ~KeyboardFocusManager(); + +private: + + /** + * Get the focus group of current focused actor. + * @pre The FocusManager has been initialized. + * @return A handle to the parent of the current focused actor which is a focus group, + * or an empty handle if no actor is focused. + */ + Actor GetCurrentFocusGroup(); + + /** + * Move the focus to the specified actor and send notification for the focus change. + * @param actorID The ID of the actor to be queried + * @return Whether the focus is successful or not + */ + bool DoSetCurrentFocusActor(const unsigned int actorID); + + /** + * Move the focus to the next actor towards the specified direction within the layout control + * @param control The layout control to move the focus in + * @param actor The current focused actor + * @param direction The direction of focus movement + * @return Whether the focus is successful or not + */ + bool DoMoveFocusWithinLayoutControl(Toolkit::Control control, Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction); + + /** + * Move the focus to the first focusable actor in the next focus group in the forward + * or backward direction. The "Tab" key changes the focus group in the forward direction + * and the "Shift-Tab" key changes it in the backward direction. + * @param forward Whether the direction of focus group change is forward or backward + * @return Whether the focus group change is successful or not + */ + bool DoMoveFocusToNextFocusGroup(bool forward); + + /** + * Activate the actor. If the actor is control, call OnActivated virtual function. + * This function will emit FocusedActorActivatedSignal. + * @param actor The actor to activate + */ + void DoActivate(Actor actor); + + /** + * Create the default indicator actor to highlight the focused actor. + */ + void CreateDefaultFocusIndicatorActor(); + + /** + * Check whether the actor is a layout control that supports two dimensional keyboard navigation. + * The layout control needs to internally set the focus order for the child actor and be able to + * tell KeyboardFocusmanager the next focusable actor in the given direction. + * @pre The KeyboardFocusManager has been initialized. + * @pre The Actor has been initialized. + * @param actor The actor to be checked. + * @return Whether the actor is a layout control or not. + */ + bool IsLayoutControl(Actor actor) const; + + /** + * Returns the closest ancestor of the given actor that is a layout control. + * @param actor The actor to be checked for its parent layout control + * @return The parent layout control the given actor belongs to or an empty handle if the given actor doesn't belong to a layout control + */ + Toolkit::Control GetParentLayoutControl(Actor actor) const; + + /** + * Callback for the key event when no actor in the stage has gained the key input focus + * @param[in] event The KeyEvent event. + */ + void OnKeyEvent(const KeyEvent& event); + + /** + * Callback for the touch event when the screen is touched and when the touch ends + * (i.e. the down & up touch events only). + * @param[in] touchEvent The touch event + */ + void OnTouched(const TouchEvent& touchEvent); + + /** + * Change the keyboard focus status when keyboard focus feature turned on or off. + * @return Whether the status is changed or not. + */ + void OnPhysicalKeyboardStatusChanged(PhysicalKeyboard keyboard); + +private: + + // Undefined + KeyboardFocusManager(const KeyboardFocusManager&); + + KeyboardFocusManager& operator=(const KeyboardFocusManager& rhs); + +private: + + Toolkit::KeyboardFocusManager::PreFocusChangeSignalV2 mPreFocusChangeSignalV2; ///< The signal to notify the focus will be changed + Toolkit::KeyboardFocusManager::FocusChangedSignalV2 mFocusChangedSignalV2; ///< The signal to notify the focus change + Toolkit::KeyboardFocusManager::FocusGroupChangedSignalV2 mFocusGroupChangedSignalV2; ///< The signal to notify the focus group change + Toolkit::KeyboardFocusManager::FocusedActorActivatedSignalV2 mFocusedActorActivatedSignalV2; ///< The signal to notify the activation of focused actor + + unsigned int mCurrentFocusActor; ///< The actor ID of current focused actor + + Actor mFocusIndicatorActor; ///< The focus indicator actor shared by all the keyboard focusable actors for highlight + + bool mFocusGroupLoopEnabled:1; ///< Whether the focus movement is looped within the same focus group + + bool mIsKeyboardFocusEnabled:1; ///< Whether keyboard focus feature turned on/off + + bool mIsFocusIndicatorEnabled:1; ///< Whether indicator should be shown / hidden. It could be enabled when keyboard focus feature enabled and navigation keys or 'Tab' key pressed. + + bool mIsWaitingKeyboardFocusChangeCommit:1; /// A flag to indicate PreFocusChangeSignal emitted but the proposed focus actor is not commited by the application yet. + + SlotDelegate< KeyboardFocusManager > mSlotDelegate; +}; + +} // namespace Internal + +inline Internal::KeyboardFocusManager& GetImpl(Dali::Toolkit::KeyboardFocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::KeyboardFocusManager& GetImpl(const Dali::Toolkit::KeyboardFocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_KEYBOARD_FOCUS_MANAGER_H__ diff --git a/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.cpp b/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.cpp new file mode 100644 index 0000000..8fa9500 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.cpp @@ -0,0 +1,234 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "keyinput-focus-manager-impl.h" + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +KeyInputFocusManager::KeyInputFocusManager() +: mSlotDelegate( this ) +{ + Stage::GetCurrent().KeyEventSignal().Connect(mSlotDelegate, &KeyInputFocusManager::OnKeyEvent); +} + +KeyInputFocusManager::~KeyInputFocusManager() +{ +} + +void KeyInputFocusManager::SetFocus(Toolkit::Control control) +{ + if(!control) + { + //No-op + return; + } + + unsigned int actorID = control.GetId(); + + ActorQueueIterator pos = std::find( mFocusActorsQueue.begin(), mFocusActorsQueue.end(), actorID); + + if((!mFocusActorsQueue.empty()) && (pos == mFocusActorsQueue.begin())) + { + //Actor allready in front, so No-op + return; + } + + if(pos != mFocusActorsQueue.end()) + { + //A previously focused actor wants to regain focus + mFocusActorsQueue.erase(pos); + } + else + { + control.OffStageSignal().Connect( mSlotDelegate, &KeyInputFocusManager::OnFocusActorStageDisconnection ); + } + + Dali::Toolkit::Control previousFocusControl; + if(!mFocusActorsQueue.empty()) + { + previousFocusControl = Dali::Toolkit::Control::DownCast(Stage::GetCurrent().GetRootLayer().FindChildById(mFocusActorsQueue.front())); + if(previousFocusControl) + { + // Notify the control that it has lost key input focus + previousFocusControl.GetImplementation().OnKeyInputFocusLost(); + } + } + + mFocusActorsQueue.push_front(actorID); + + // Tell the new actor that it has gained focus. + control.GetImplementation().OnKeyInputFocusGained(); + + // Emit the signal to inform focus change to the application. + if ( !mKeyInputFocusChangedSignalV2.Empty() ) + { + mKeyInputFocusChangedSignalV2.Emit( control, previousFocusControl ); + } +} + +Control KeyInputFocusManager::GetCurrentFocusControl() const +{ + Control currentFocusControl; + + if(!mFocusActorsQueue.empty()) + { + currentFocusControl = Dali::Toolkit::Control::DownCast(Stage::GetCurrent().GetRootLayer().FindChildById(mFocusActorsQueue.front())); + } + + return currentFocusControl; +} + +void KeyInputFocusManager::RemoveFocus(Toolkit::Control control) +{ + if(control) + { + unsigned int actorId = control.GetId(); + ActorQueueIterator pos = std::find( mFocusActorsQueue.begin(), mFocusActorsQueue.end(), actorId); + + if(pos != mFocusActorsQueue.end()) + { + control.OffStageSignal().Disconnect( mSlotDelegate, &KeyInputFocusManager::OnFocusActorStageDisconnection ); + + // Notify the control that it has lost key input focus + control.GetImplementation().OnKeyInputFocusLost(); + + if(pos == mFocusActorsQueue.begin()) + { + Actor previousFocusActor; + + mFocusActorsQueue.erase(pos); + if(!mFocusActorsQueue.empty()) + { + previousFocusActor = Stage::GetCurrent().GetRootLayer().FindChildById(mFocusActorsQueue.front()); + } + + Dali::Toolkit::Control previouscontrol = Dali::Toolkit::Control::DownCast(previousFocusActor); + if(previouscontrol) + { + // Tell the new actor that it has gained focus. + previouscontrol.GetImplementation().OnKeyInputFocusGained(); + } + } + else + { + //If the removed actor is not currently focused, then no need to emit signal. + mFocusActorsQueue.erase(pos); + } + + } + } +} + +bool KeyInputFocusManager::IsKeyboardListener(Toolkit::Control control) const +{ + bool result = false; + + if(!mFocusActorsQueue.empty()) + { + unsigned int actorId = control.GetId(); + ActorQueueConstIterator pos = std::find(mFocusActorsQueue.begin(), mFocusActorsQueue.end(), actorId); + + if(pos != mFocusActorsQueue.end()) + { + result = true; + } + } + + return result; +} + +Toolkit::KeyInputFocusManager::KeyInputFocusChangedSignalV2& KeyInputFocusManager::KeyInputFocusChangedSignal() +{ + return mKeyInputFocusChangedSignalV2; +} + +Toolkit::KeyInputFocusManager::UnhandledKeyEventSignalV2& KeyInputFocusManager::UnhandledKeyEventSignal() +{ + return mUnhandledKeyEventSignalV2; +} + +void KeyInputFocusManager::OnKeyEvent(const KeyEvent& event) +{ + bool consumed = false; + + ActorQueueIterator iter = mFocusActorsQueue.begin(); + + Layer rootLayer = Stage::GetCurrent().GetRootLayer(); + + while(!mFocusActorsQueue.empty() && !consumed && (iter != mFocusActorsQueue.end())) + { + Actor actor = rootLayer.FindChildById(*iter); + Dali::Toolkit::Control control = Dali::Toolkit::Control::DownCast(actor); + if(control) + { + // Notify the control about the key event + consumed = control.GetImplementation().EmitKeyEventSignal(event); + } + iter++; + } + + if(!consumed) + { + // Emit signal to inform that a key event is not consumed. + if( !mUnhandledKeyEventSignalV2.Empty() ) + { + mUnhandledKeyEventSignalV2.Emit(event); + } + } +} + +void KeyInputFocusManager::OnFocusActorStageDisconnection( Dali::Actor actor ) +{ + RemoveFocus(Dali::Toolkit::Control::DownCast(actor)); +} + +bool KeyInputFocusManager::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + KeyInputFocusManager* manager = dynamic_cast(object); + + if( Dali::Toolkit::KeyInputFocusManager::SIGNAL_KEY_INPUT_FOCUS_CHANGED == signalName ) + { + manager->KeyInputFocusChangedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.h b/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.h new file mode 100644 index 0000000..6622b73 --- /dev/null +++ b/dali-toolkit/internal/focus-manager/keyinput-focus-manager-impl.h @@ -0,0 +1,165 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_KEYINPUT_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_INTERNAL_KEYINPUT_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class KeyInputFocusManager; + +/** + * @copydoc Toolkit::KeyInputFocusManager + */ +class KeyInputFocusManager : public Dali::BaseObject +{ +public: + + typedef std::deque< unsigned int > ActorQueue; + typedef std::deque< unsigned int >::iterator ActorQueueIterator; + typedef std::deque< unsigned int >::const_iterator ActorQueueConstIterator; + + /** + * Construct a new KeyInputFocusManager. + */ + KeyInputFocusManager(); + + /** + * @copydoc Toolkit::SetFocus + */ + void SetFocus(Toolkit::Control control); + + /** + * @copydoc Toolkit::GetCurrentFocusControl + */ + Toolkit::Control GetCurrentFocusControl() const; + + /** + * @copydoc Toolkit::RemoveFocus + */ + void RemoveFocus(Toolkit::Control control); + + /** + * @copydoc Toolkit::IsKeyboardListener + */ + bool IsKeyboardListener(Toolkit::Control control) const; + +public: + + /** + * @copydoc Toolkit::KeyInputFocusManager::KeyInputFocusChangedSignal() + */ + Toolkit::KeyInputFocusManager::KeyInputFocusChangedSignalV2& KeyInputFocusChangedSignal(); + + /** + * @copydoc Toolkit::KeyInputFocusManager::UnhandledKeyEventSignal() + */ + Toolkit::KeyInputFocusManager::UnhandledKeyEventSignalV2& UnhandledKeyEventSignal(); + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: + + /** + * Destructor + */ + virtual ~KeyInputFocusManager(); + +private: + + /** + * Callback for the key event when no actor in the stage has gained the key input focus + * @param[in] event The KeyEvent event. + */ + void OnKeyEvent(const KeyEvent& event); + + /** + * Signal handler called when a focused Actor is removed from Stage. + * @param[in] actor The actor removed from stage. + */ + void OnFocusActorStageDisconnection( Dali::Actor actor ); + +private: + + // Undefined + KeyInputFocusManager(const KeyInputFocusManager&); + + KeyInputFocusManager& operator=(const KeyInputFocusManager& rhs); + +private: + + // The key input focus change signal + Toolkit::KeyInputFocusManager::KeyInputFocusChangedSignalV2 mKeyInputFocusChangedSignalV2; + + // The un-handled key event signal + Toolkit::KeyInputFocusManager::UnhandledKeyEventSignalV2 mUnhandledKeyEventSignalV2; + + // Keyboard events are sent to the current focus actor, which will be the actor on the top of the focus actors stack. + ActorQueue mFocusActorsQueue; + + SlotDelegate< KeyInputFocusManager > mSlotDelegate; +}; + +} // namespace Internal + +inline Internal::KeyInputFocusManager& GetImpl(Dali::Toolkit::KeyInputFocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::KeyInputFocusManager& GetImpl(const Dali::Toolkit::KeyInputFocusManager& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_KEYINPUT_FOCUS_MANAGER_H__ diff --git a/dali-toolkit/internal/shader-effects/page-turn-effect-impl.cpp b/dali-toolkit/internal/shader-effects/page-turn-effect-impl.cpp new file mode 100644 index 0000000..275356f --- /dev/null +++ b/dali-toolkit/internal/shader-effects/page-turn-effect-impl.cpp @@ -0,0 +1,471 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "page-turn-effect-impl.h" + +// EXTERNAL HEADERS +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +#define MAKE_STRING(A)#A + +const std::string CURRENT_CENTER_PROPERTY_NAME("uCurrentCenter"); +const std::string ORIGINAL_CENTER_PROPERTY_NAME("uOriginalCenter"); +const std::string PAGE_SIZE_PROPERTY_NAME("uPageSize"); +const std::string IS_TURNING_BACK_PROPERTY_NAME("uIsTurningBack"); +const std::string SHADOW_WIDTH_PROPERTY_NAME("uShadowWidth"); +const std::string SPINE_SHADOW_PARAMETER_PROPERTY_NAME("uSpineShadowParameter"); + +// fake shadow is used to enhance the effect, with its default maximum width to be pageSize * 0.15 +const float DEFAULT_SHADOW_WIDTH(0.15f); + +// the major&minor radius (in pixels) to form an ellipse shape +// the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow +const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f); + +// when the vanishing point is very far away(pageHeight*THRESHOLD), make it infinitely, in this case, the page bent horizontally +const float THRESHOLD(20.0); + +struct CommonParametersConstraint +{ + Matrix operator()( const Matrix& current, + const PropertyInput& originalCenterProperty, + const PropertyInput& currentCenterProperty, + const PropertyInput& pageSizeProperty) + { + const Vector2& originalCenter = originalCenterProperty.GetVector2(); + Vector2 currentCenter = currentCenterProperty.GetVector2(); + const Vector2& pageSize = pageSizeProperty.GetVector2(); + + // calculate the curve direction and the vanishing point + // here, the vanishing point is the intersection of spine with the line passing through original center and vertical to curve direction + Vector2 curveDirection( currentCenter - originalCenter ); + curveDirection.Normalize(); + if( fabs(curveDirection.y) < 0.01f) // eliminate the possibility of division by zero in the next step + { + curveDirection.y = 0.01f; + } + float vanishingPointY = originalCenter.y + curveDirection.x * originalCenter.x / curveDirection.y; + + float curveEndY, cosTheta ,sinTheta ,translateX, translateY; + // when the vanishing point is very far away, make it infinitely, in this case, the page bent horizontally + if( fabs(vanishingPointY-pageSize.y*0.5f) >= pageSize.y*THRESHOLD ) + { + curveDirection = Vector2(-1.f,0.f); + currentCenter.y = originalCenter.y; + + curveEndY = originalCenter.y; + cosTheta = 1.f; + sinTheta = 0.f; + translateX = currentCenter.x - originalCenter.x; + translateY = vanishingPointY; + } + else + { + curveEndY = currentCenter.y - curveDirection.y * (currentCenter.x/curveDirection.x) ; + Vector2 v1( currentCenter.x, currentCenter.y - vanishingPointY ); + v1.Normalize(); + Vector2 v2( originalCenter.x, originalCenter.y - vanishingPointY ); + v2.Normalize(); + cosTheta = v1.x*v2.x + v1.y*v2.y; + sinTheta = ( vanishingPointY > pageSize.y*0.5f ) ? sqrt(1.0-cosTheta*cosTheta) : -sqrt(1.0-cosTheta*cosTheta); + translateX = currentCenter.x - cosTheta*originalCenter.x - sinTheta*( originalCenter.y-vanishingPointY ); + translateY = currentCenter.y + sinTheta*originalCenter.x - cosTheta*( originalCenter.y-vanishingPointY ); + } + + float originalLength = fabs(originalCenter.x/curveDirection.x); + float currentLength = fabs(currentCenter.x/curveDirection.x); + float curveHeight = 0.45f*sqrt(originalLength*originalLength - currentLength*currentLength); + + Matrix commonParameters( false ); + float* parameterArray = commonParameters.AsFloat(); + parameterArray[0] = cosTheta; + parameterArray[1] = -sinTheta; + parameterArray[2] = originalCenter.x; + parameterArray[3] = originalCenter.y; + parameterArray[4] = sinTheta; + parameterArray[5] = cosTheta; + parameterArray[6] = currentCenter.x; + parameterArray[7] = currentCenter.y; + parameterArray[8] = translateX; + parameterArray[9] = translateY; + parameterArray[10] = vanishingPointY; + parameterArray[11] = curveEndY; + parameterArray[12] = curveDirection.x; + parameterArray[13] = curveDirection.y; + parameterArray[14] = curveHeight; + parameterArray[15] = currentLength; + + return commonParameters; + } +}; + +}//namespace + +PageTurnEffect::PageTurnEffect() +{ +} + +PageTurnEffect::~PageTurnEffect() +{ +} + +Toolkit::PageTurnEffect PageTurnEffect::CreateShaderEffect( bool enableBlending ) +{ + std::string vertexShader = MAKE_STRING( + /* + * The common parameters for all the vertices, calculate in CPU then pass into the shader as uniforms + * + * first part of the page, (outside the the line passing through original center and vertical to curve direction) + * no Z change, only 2D rotation and translation + * ([0][0],[0][1],[1][0],[1][1]) mat2 rotateMatrix + * ([2][0],[2][1]) vec2 translationVector + * + * ([0][2],[0][3]) vec2 originalCenter: Typically the press down position of the Pan Gesture + * ([1][2],[1][3]) vec2 currentCenter: Typically the current position of the Pan Gesture + * ([3][0],[3][1]) vec2 curveDirection: The normalized vector pointing from original center to current center + * ([2][2]) float vanishingPointY: The Y coordinate of the intersection of the spine + * and the line which goes through the original center and is vertical to the curveDirection + * ([2][3]) float curveEndY: The Y coordinate of intersection of the spine and the line through both original and current center + * ([3][2]) float curveHeight: The height of the interpolated hermite curve. + * ([3][3]) float currentLength: The length from the current center to the curveEnd. + */ + uniform mat4 uCommonParameters;\n + \n + uniform vec2 uPageSize;\n + uniform float uIsTurningBack;\n + uniform float uShadowWidth;\n + varying vec3 vNormal;\n + varying vec4 vPosition;\n + varying float vEdgeShadow;\n + \n + void main()\n + {\n + vec4 position = vec4( aPosition.xy, 0.0, 1.0);\n + vec2 currentCenter = vec2( uCommonParameters[1][2], uCommonParameters[1][3]);\n + vec2 originalCenter = vec2( uCommonParameters[0][2], uCommonParameters[0][3]);\n + vec3 normal = vec3(0.0,0.0,1.0);\n + \n + if(currentCenter.x < originalCenter.x)\n + {\n + // change the coordinate origin from the center of the page to its top-left + position.xy += uPageSize * 0.5;\n + vec2 curveDirection = vec2( uCommonParameters[3]);\n + vec3 vanishingPoint = vec3(0.0, uCommonParameters[2][2], 0.0);\n + // first part of the page, (outside the the line passing through original center and vertical to curve direction) + //no Z change, only 2D rotation and translation + if( dot(curveDirection, position.xy - originalCenter) < 0.0 ) + {\n + position.y -= vanishingPoint.y;\n + position.xy = mat2(uCommonParameters)*position.xy + vec2( uCommonParameters[2]);\n + }\n + // second part of the page, bent as a ruled surface + else\n + {\n + // calculate on the flat plane, between + // the first line passing through current vertex and vanishing point + // the second line passing through original center and current center + vec2 curveEnd = vec2( 0.0, uCommonParameters[2][3] );\n + vec2 curFlatDirection = vec2(0.0,1.0);\n + float lengthFromCurve = position.y - originalCenter.y;\n + float lengthOnCurve = position.x;\n + if(currentCenter.y != originalCenter.y)\n + {\n + curFlatDirection = normalize(position.xy - vanishingPoint.xy);\n + lengthFromCurve = (curveEnd.x*curveDirection.y-curveEnd.y*curveDirection.x-position.x*curveDirection.y+position.y*curveDirection.x) + / (curFlatDirection.x*curveDirection.y-curFlatDirection.y*curveDirection.x);\n + lengthOnCurve = length(position.xy+lengthFromCurve*curFlatDirection-curveEnd);\n + }\n + \n + // define the control points of hermite curve, composed with two segments + // calulation is carried out on the 2D plane which is passing through both current and original center and vertical to the image plane + float currentLength = uCommonParameters[3][3];\n + float originalLength = abs(originalCenter.x/curveDirection.x);\n + float height = uCommonParameters[3][2];\n + float percentage = currentLength/originalLength;\n + //vec2 SegmentOneControlPoint0 = vec2(0.0, 0.0); + vec2 SegmentOneControlPoint1 = vec2((0.65*percentage - 0.15)*originalLength, (0.8 + 0.2 * percentage)*height); \n + vec2 SegmentTwoControlPoint0 = SegmentOneControlPoint1;\n + vec2 SegmentTwoControlPoint1 = vec2(currentLength, 0.0); \n + vec2 SegmentOneTangentVector0 = SegmentOneControlPoint1;\n + vec2 SegmentOneTangentVector1 = vec2(0.5*originalLength,0.0);\n + vec2 SegmentTwoTangentVector0 = SegmentOneTangentVector1;\n + vec2 SegmentTwoTangentVector1 = SegmentOneTangentVector1;\n + \n + // calulate the corresponding curve point position and its tangent vector + // it is a linear mapping onto nonlinear curves, might cause some unwanted deformation + // but as there are no analytical method to calculate the curve length on arbitrary segment + // no efficient way to solve this nonlinear mapping, Numerical approximation would cost too much computation in shader + vec2 curvePoint2D;\n + vec2 tangent;\n + float t0 = lengthOnCurve / originalLength;\n + if(t0<=0.5)\n + {\n + float t = 2.0*t0;\n + float t_2 = t*t;\n + float t_3 = t*t_2;\n + curvePoint2D = (-2.0*t_3+3.0*t_2)*SegmentOneControlPoint1 + + (t_3-2.0*t_2+t)*SegmentOneTangentVector0 + (t_3-t_2)*SegmentOneTangentVector1;\n + tangent = (-6.0*t_2+6.0*t)*SegmentOneControlPoint1 + + (3.0*t_2-4.0*t+1.0)*SegmentOneTangentVector0 + (3.0*t_2-2.0*t)*SegmentOneTangentVector1;\n + }\n + else\n + {\n + float t = 2.0*t0-1.0;\n + float t_2 = t*t;\n + float t_3 = t*t_2;\n + curvePoint2D = (2.0*t_3-3.0*t_2+1.0)*SegmentTwoControlPoint0 + (-2.0*t_3+3.0*t_2)*SegmentTwoControlPoint1 + + (t_3-2.0*t_2+t)*SegmentTwoTangentVector0 + (t_3-t_2)*SegmentTwoTangentVector1;\n + tangent = (6.0*t_2-6.0*t)*SegmentTwoControlPoint0 + (-6.0*t_2+6.0*t)*SegmentTwoControlPoint1 + + (3.0*t_2-4.0*t+1.0)*SegmentTwoTangentVector0 + (3.0*t_2-2.0*t)*SegmentTwoTangentVector1;\n + // a trick to eliminate some optical illusion caused by the gradient matter of normal in per-fragment shading + // which is caused by linear interpolation of normal vs. nonlinear lighting + // will notice some artifact in the areas with dramatically normal changes, so compress the normal differences here + tangent.y *= min(1.0, length(position.xyz - vanishingPoint) / uPageSize.y ); \n + }\n + vec3 curvePoint = vec3(curveEnd - curvePoint2D.x*curveDirection,max(0.0,curvePoint2D.y));\n + vec3 tangentVector = vec3(-tangent.x*curveDirection,tangent.y);\n + \n + // locate the new vertex position on the line passing through both vanishing point and the calculated curve point position + vec3 curLiftDirection = vec3(0.0,-1.0,0.0);\n + if(currentCenter.y != originalCenter.y)\n + {\n + curLiftDirection = normalize(curvePoint - vanishingPoint);\n + tangentVector *= (curveDirection.y > 0.0) ? -1.0 : 1.0;\n + // an heuristic adjustment here, to compensate the linear parameter mapping onto the nonlinear curve + float Y0 = position.y - curveDirection.y * (position.x/curveDirection.x); \n + float proportion; + float refLength;\n + if(abs(Y0-vanishingPoint.y) > abs(curveEnd.y-vanishingPoint.y)) \n + {\n + proportion = abs(curveEnd.y - Y0) / (abs(curveEnd.y-Y0)+abs(curveEnd.y - vanishingPoint.y)); \n + refLength = proportion*length(originalCenter-vanishingPoint.xy) / (proportion-1.0); \n + }\n + else\n + {\n + proportion = abs(curveEnd.y - Y0) / abs(curveEnd.y - vanishingPoint.y);\n + refLength = proportion*length(originalCenter-vanishingPoint.xy); \n + }\n + float Y1 = currentCenter.y - (normalize(currentCenter-vanishingPoint.xy)).y * refLength; \n + position.y = mix(Y0, Y1, t0); \n + }\n + position.xz = curvePoint.xz - lengthFromCurve*curLiftDirection.xz;\n + // calculate the normal vector, will be used for lighting + normal = cross(curLiftDirection, normalize(tangentVector));\n + // the signature of Z is decided by the page turning direction: + // from left to right(negative); from right to left (positive) + position.z *= -uIsTurningBack;\n + normal.xy *= -uIsTurningBack;\n + }\n + // change the coordinate origin from the top-left of the page to its center + position.xy -= uPageSize * 0.5; \n + }\n + position.z += aPosition.z;\n + gl_Position = uMvpMatrix * position;\n + // varying parameters for fragment shader + vTexCoord = aTexCoord; + vNormal = uNormalMatrix*normal;\n + vPosition = uModelView * position;\n + ); + + std::string vertexShaderWithFakedShadow = MAKE_STRING( + // display shadow, the fake shadow value is calculated according to the height and the distance from page edge + vTexCoord.x = (aTexCoord.x-sTextureRect.s) /( 1.0 - uShadowWidth ) + sTextureRect.s;\n + vTexCoord.y = ( aTexCoord.y-sTextureRect.t-0.5*uShadowWidth*(sTextureRect.q-sTextureRect.t) )/( 1.0 - uShadowWidth ) + sTextureRect.t;\n + float heightCoef = (1.0 + position.z*uIsTurningBack*3.0 / uPageSize.x) * 0.6; + vEdgeShadow = clamp(0.9 - heightCoef, 0.0, 0.9 ); \n + if( vTexCoord.y >= sTextureRect.q || vTexCoord.y <= sTextureRect.t || vTexCoord.x >= sTextureRect.p )\n + {\n + float inversedShadowWidth = (1.0-uShadowWidth) / uShadowWidth ;\n + float alpha1 = (vTexCoord.x-sTextureRect.p) * inversedShadowWidth / (sTextureRect.p - sTextureRect.s);\n + inversedShadowWidth = 2.0 * inversedShadowWidth / (sTextureRect.q - sTextureRect.t); \n + float alpha2 = (vTexCoord.y-sTextureRect.q) * inversedShadowWidth;\n + float alpha3 = (sTextureRect.t-vTexCoord.y) * inversedShadowWidth;\n + float alpha;\n + if(alpha1 > 0.0 && alpha2 > 0.0) alpha = sqrt(alpha2*alpha2+alpha1*alpha1)/sqrt(1.0 + max(alpha1,alpha2)*max(alpha1,alpha2));\n //bottom-right corner + else if(alpha1 > 0.0 && alpha3 > 0.0) alpha = sqrt(alpha3*alpha3+alpha1*alpha1)/sqrt(1.0+max(alpha1,alpha3)*max(alpha1,alpha3));\n //top-right corner + else alpha = max(alpha1,max(alpha2,alpha3)); \n + alpha = 0.9 - alpha*0.9;\n + vEdgeShadow = clamp(alpha - heightCoef, 0.0, 0.9 ); \n + }\n + ); + + std::string vertexShaderEnd("}"); + + std::string fragmentShaderPartOne = MAKE_STRING( + uniform vec2 uPageSize;\n + uniform vec2 uSpineShadowParameter;\n + varying vec3 vNormal;\n + varying vec4 vPosition;\n + varying float vEdgeShadow;\n + \n + void main()\n + {\n + // need to re-normalize the interpolated normal + vec3 normal = normalize(vNormal);\n + vec4 texel;\n + float spineShadowCoef = 1.0; \n + ); + + std::string fragmentShaderWithFakedShadow = MAKE_STRING( + if( vTexCoord.y > sTextureRect.q || vTexCoord.y < sTextureRect.t || vTexCoord.x > sTextureRect.p )\n + texel = vec4(0.0,0.0,0.0,vEdgeShadow); + else \n + ); + + std::string fragmentShaderPartTwo = MAKE_STRING( + { \n + // display page content + // display back image of the page, flip the texture + if( dot(vPosition.xyz, normal) > 0.0 ) texel = texture2D( sTexture, vec2( sTextureRect.p+sTextureRect.s-vTexCoord.x, vTexCoord.y ) );\n + // display front image of the page + else texel = texture2D( sTexture, vTexCoord );\n + // display book spine, a stripe of shadowed texture + float pixelPos = (vTexCoord.x-sTextureRect.s)*uPageSize.x; \n + if(pixelPos < uSpineShadowParameter.x) \n + {\n + float x = pixelPos - uSpineShadowParameter.x;\n + float y = sqrt( uSpineShadowParameter.x*uSpineShadowParameter.x - x*x);\n + spineShadowCoef = normalize( vec2( uSpineShadowParameter.y*x/uSpineShadowParameter.x, y ) ).y;\n + }\n + }\n + // calculate the lighting + // set the ambient color as vec3(0.4); + float lightColor = abs( normal.z ) * 0.6 + 0.4;\n + gl_FragColor = vec4( ( spineShadowCoef* lightColor)* texel.rgb , texel.a ) * uColor;\n + } + ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom; + std::ostringstream vertexShaderStringStream; + std::ostringstream fragmentShaderStringStream; + if( enableBlending ) + { + vertexShaderStringStream<< vertexShader << vertexShaderWithFakedShadow << vertexShaderEnd; + fragmentShaderStringStream<< fragmentShaderPartOne << fragmentShaderWithFakedShadow << fragmentShaderPartTwo; + shaderEffectCustom = Dali::ShaderEffect::New( vertexShaderStringStream.str(), fragmentShaderStringStream.str(), GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_DEPTH_BUFFER | ShaderEffect::HINT_BLENDING) ); + } + else + { + vertexShaderStringStream<< vertexShader << vertexShaderEnd; + fragmentShaderStringStream<< fragmentShaderPartOne << fragmentShaderPartTwo; + shaderEffectCustom = Dali::ShaderEffect::New( vertexShaderStringStream.str(), fragmentShaderStringStream.str(), GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_DEPTH_BUFFER ) ); + } + + PageTurnEffect* shaderImpl = new PageTurnEffect(); + Dali::Toolkit::PageTurnEffect handle = Toolkit::PageTurnEffect( shaderEffectCustom, shaderImpl ); + + shaderImpl->Initialize( handle ); + + Vector2 defaultPageSize = Dali::Stage::GetCurrent().GetSize(); + Matrix zeroMatrix(true); + handle.SetUniform( "uCommonParameters", zeroMatrix ); + handle.SetUniform( PAGE_SIZE_PROPERTY_NAME, defaultPageSize/(1.f-DEFAULT_SHADOW_WIDTH) ); + handle.SetUniform( SHADOW_WIDTH_PROPERTY_NAME, DEFAULT_SHADOW_WIDTH ); + handle.SetUniform( SPINE_SHADOW_PARAMETER_PROPERTY_NAME, DEFAULT_SPINE_SHADOW_PARAMETER ); + + shaderImpl->mOriginalCenterPropertyIndex = handle.RegisterProperty( ORIGINAL_CENTER_PROPERTY_NAME, Vector2( defaultPageSize[0], defaultPageSize[1]*0.5f ) ); + shaderImpl->mCurrentCenterPropertyIndex = handle.RegisterProperty( CURRENT_CENTER_PROPERTY_NAME, Vector2( defaultPageSize[0], defaultPageSize[1]*0.5f ) ); + shaderImpl->mInternalConstraint = Constraint::New( handle.GetPropertyIndex( "uCommonParameters" ), + LocalSource( shaderImpl->mOriginalCenterPropertyIndex ), + LocalSource( shaderImpl->mCurrentCenterPropertyIndex ), + LocalSource( handle.GetPropertyIndex( PAGE_SIZE_PROPERTY_NAME ) ), + CommonParametersConstraint() ); + handle.ApplyConstraint( shaderImpl->mInternalConstraint ); + + // setting isTurningBack to -1.0f here means turning page forward + handle.SetUniform( IS_TURNING_BACK_PROPERTY_NAME, -1.0f ); + + return handle; +} + +void PageTurnEffect::SetPageSize(const Vector2& pageSize) +{ + mShaderEffect.SetUniform(PAGE_SIZE_PROPERTY_NAME, pageSize); +} + +void PageTurnEffect::SetOriginalCenter(const Vector2& originalCenter) +{ + mShaderEffect.SetProperty( mOriginalCenterPropertyIndex, originalCenter ); +} + +void PageTurnEffect::SetCurrentCenter(const Vector2& currentCenter) +{ + mShaderEffect.SetProperty( mCurrentCenterPropertyIndex, currentCenter ); +} + +void PageTurnEffect::SetIsTurningBack(bool isTurningBack) +{ + float direction = isTurningBack ? 1.0f : -1.0f; + mShaderEffect.SetUniform(IS_TURNING_BACK_PROPERTY_NAME, direction); +} + +void PageTurnEffect::SetShadowWidth(float shadowWidth) +{ + mShaderEffect.SetUniform( SHADOW_WIDTH_PROPERTY_NAME, shadowWidth ); +} + +void PageTurnEffect::SetSpineShadowParameter(const Vector2& spineShadowParameter) +{ + mShaderEffect.SetUniform( SPINE_SHADOW_PARAMETER_PROPERTY_NAME, spineShadowParameter); +} + +void PageTurnEffect::ApplyInternalConstraint() +{ + mShaderEffect.ApplyConstraint( mInternalConstraint ); +} + +const std::string& PageTurnEffect::GetPageSizePropertyName() const +{ + return PAGE_SIZE_PROPERTY_NAME; +} + +const std::string& PageTurnEffect::GetOriginalCenterPropertyName() const +{ + return ORIGINAL_CENTER_PROPERTY_NAME; +} + +const std::string& PageTurnEffect::GetCurrentCenterPropertyName() const +{ + return CURRENT_CENTER_PROPERTY_NAME; +} + +void PageTurnEffect::Initialize( Dali::ShaderEffect shaderEffect ) +{ + // Save a reference to the shader handle + mShaderEffect = shaderEffect; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/shader-effects/page-turn-effect-impl.h b/dali-toolkit/internal/shader-effects/page-turn-effect-impl.h new file mode 100644 index 0000000..21f522c --- /dev/null +++ b/dali-toolkit/internal/shader-effects/page-turn-effect-impl.h @@ -0,0 +1,143 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_PAGE_TURN_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_PAGE_TURN_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * PageTurnEffect implementation class + */ +class PageTurnEffect : public ShaderEffect::Extension +{ +public: + + PageTurnEffect(); + /** + * Virtual destructor. + */ + virtual ~PageTurnEffect(); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::New + */ + static Toolkit::PageTurnEffect CreateShaderEffect( bool enableBlending ); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::SetPageSize + */ + void SetPageSize(const Vector2& pageSize); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::SetOriginalCenter + */ + void SetOriginalCenter(const Vector2& originalCenter); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::SetCurrentCenter + */ + void SetCurrentCenter(const Vector2& currentCenter); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::SetIsTurningBack + */ + void SetIsTurningBack(bool isTurningBack); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::SetShadowWidth + */ + void SetShadowWidth(float shadowWidth); + + /** + *@copydoc Dali::Toolkit::PageTurnEffect::SetSpineShadowParameter + */ + void SetSpineShadowParameter(const Vector2& spineShadowParameter); + + /** + * The internal constraint uses the OriginalCenter property and the CurrentCenter Property + * to update the variety of common parameters which are with the same value for all the vertices. + * Note: For each actor, the constraints are applied in the same order as the calls to Actor::ApplyConstraint(). + * So if there are other contraints applied to the OriginalCenter or CurrentCenter while when using this effect, + * call this method to get the internal constraints and re-apply it afterwards. + */ + void ApplyInternalConstraint(); + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::GetPageSizePropertyName + */ + const std::string& GetPageSizePropertyName() const; + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::GetOriginalCenterPropertyName + */ + const std::string& GetOriginalCenterPropertyName() const; + + /** + * @copydoc Dali::Toolkit::PageTurnEffect::GetCurrentCenterPropertyName + */ + const std::string& GetCurrentCenterPropertyName() const; + +private: + + void Initialize( ShaderEffect shaderEffect ); + +private: + ShaderEffect mShaderEffect; + + Property::Index mOriginalCenterPropertyIndex; + Property::Index mCurrentCenterPropertyIndex; + Constraint mInternalConstraint; + +private: + //undefined copy constructor. + PageTurnEffect( const PageTurnEffect& ); + + //Undefined assignment operator. + PageTurnEffect& operator=( const PageTurnEffect& ); + +}; + +} // namespace Internal + +//Helpers for public-api forwarding methods +inline Toolkit::Internal::PageTurnEffect& GetImpl( Toolkit::PageTurnEffect& effect ) +{ + DALI_ASSERT_ALWAYS( effect ); + return static_cast( effect.GetExtension() ); +} + +inline const Toolkit::Internal::PageTurnEffect& GetImpl( const Toolkit::PageTurnEffect& effect ) +{ + DALI_ASSERT_ALWAYS( effect ); + return static_cast( effect.GetExtension() ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_PAGE_TURN_EFFECT_H__*/ diff --git a/dali-toolkit/internal/shader-effects/water-effect-impl.cpp b/dali-toolkit/internal/shader-effects/water-effect-impl.cpp new file mode 100644 index 0000000..2527ef2 --- /dev/null +++ b/dali-toolkit/internal/shader-effects/water-effect-impl.cpp @@ -0,0 +1,198 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "water-effect-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +static const unsigned int LIGHT_MAP_SIZE = 512; ///< Size of the bitmap created for the pre-calculated wave function +static const float MAX_WAVE_RADIUS = 80.0f; ///< Maximum radius of the wave in percentage of the texture coordinates + +} // namespace + +WaterEffect::WaterEffect( unsigned int numberOfWaves ) +: mNumberOfWaves( numberOfWaves ) +{ +} + +WaterEffect::~WaterEffect() +{ +} + +unsigned int WaterEffect::GetNumberOfWaves() const +{ + return mNumberOfWaves; +} + + +Dali::Toolkit::WaterEffect WaterEffect::CreateShaderEffect( unsigned int numberOfWaves ) +{ + std::ostringstream vertexShaderStringStream; + vertexShaderStringStream << "#define NUMBER_OF_DROPS " << numberOfWaves << "\n"; + vertexShaderStringStream << "#define MAX_WAVE_RADIUS " << std::setprecision(1) << MAX_WAVE_RADIUS << "\n"; + + std::string vertexShader( + "mediump vec4 position = vec4( aPosition, 1.0 );\n" + "\n" + "struct Drops\n" + "{\n" + " mediump vec2 center;\n" + " mediump float radius;\n" + " mediump float amplitude;\n" + "};\n" + "uniform Drops uDrops[NUMBER_OF_DROPS];\n" + "varying mediump vec4 vColor;\n" + "void main()\n" + "{\n" + " position = uModelView * position;\n" + " mediump float refraction = 0.0;\n" + " for (int i=0; iInitialize( handle ); + + for ( unsigned int index = 0; index < shaderImpl->mNumberOfWaves; ++index ) + { + handle.SetUniform( shaderImpl->GetAmplitudePropertyName( index ), 0.0f ); + handle.SetUniform( shaderImpl->GetCenterPropertyName( index ), Vector2(0.0f, 0.0f), ShaderEffect::COORDINATE_TYPE_VIEWPORT_POSITION ); + handle.SetUniform( shaderImpl->GetPropagationPropertyName( index ), 0.0f ); + } + + return handle; +} + +void WaterEffect::SetAmplitude( unsigned int index, float amplitude ) +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + mShaderEffect.SetUniform( GetAmplitudePropertyName( index ), amplitude ); +} + +void WaterEffect::SetCenter( unsigned int index, const Vector2& center ) +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + mShaderEffect.SetUniform( GetCenterPropertyName( index ), center, ShaderEffect::COORDINATE_TYPE_VIEWPORT_POSITION ); +} + +void WaterEffect::SetPropagation( unsigned int index, float radius ) +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + mShaderEffect.SetUniform( GetPropagationPropertyName( index ) , radius ); +} + +float WaterEffect::GetAmplitude( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + Property::Index propertyIndex = mShaderEffect.GetPropertyIndex( GetAmplitudePropertyName( index ) ); + return mShaderEffect.GetProperty( propertyIndex ).Get(); +} + +Vector2 WaterEffect::GetCenter( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + Property::Index propertyIndex = mShaderEffect.GetPropertyIndex( GetCenterPropertyName( index ) ); + return mShaderEffect.GetProperty( propertyIndex ).Get(); +} + +float WaterEffect::GetPropagation( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + Property::Index propertyIndex = mShaderEffect.GetPropertyIndex( GetPropagationPropertyName( index ) ); + return mShaderEffect.GetProperty( propertyIndex ).Get(); +} + +std::string WaterEffect::GetAmplitudePropertyName( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + std::ostringstream oss; + oss << "uDrops[" << index << "].amplitude"; + return oss.str(); +} + +std::string WaterEffect::GetCenterPropertyName( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + std::ostringstream oss; + oss << "uDrops[" << index << "].center"; + return oss.str(); +} + +std::string WaterEffect::GetPropagationPropertyName( unsigned int index ) const +{ + DALI_ASSERT_ALWAYS( index < mNumberOfWaves ); + std::ostringstream oss; + oss << "uDrops[" << index << "].radius"; + return oss.str(); +} + +void WaterEffect::Initialize( Dali::ShaderEffect shaderEffect ) +{ + // Save a reference to the shader handle + mShaderEffect = shaderEffect; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/shader-effects/water-effect-impl.h b/dali-toolkit/internal/shader-effects/water-effect-impl.h new file mode 100644 index 0000000..d8d7e88 --- /dev/null +++ b/dali-toolkit/internal/shader-effects/water-effect-impl.h @@ -0,0 +1,146 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_WATER_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_WATER_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * WaterEffect implementation class + */ +class WaterEffect : public ShaderEffect::Extension +{ +public: + + /** + * @copydoc Dali::Toolkit::WaterEffect::WaterEffect + */ + WaterEffect( unsigned int numberOfWaves ); + + /** + * @copydoc Dali::Toolkit::WaterEffect::~WaterEffect + */ + virtual ~WaterEffect(); + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetNumberOfWaves + */ + unsigned int GetNumberOfWaves() const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::CreateShaderEffect + */ + static Dali::Toolkit::WaterEffect CreateShaderEffect( unsigned int numberOfWaves ); + + /** + * @copydoc Dali::Toolkit::WaterEffect::SetAmplitude + */ + void SetAmplitude( unsigned int index, float amplitude ); + + /** + * @copydoc Dali::Toolkit::WaterEffect::SetCenter + */ + void SetCenter( unsigned int index, const Vector2& center ); + + /** + * @copydoc Dali::Toolkit::WaterEffect::SetPropagation + */ + void SetPropagation( unsigned int index, float radius ); + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetPropagation + */ + float GetPropagation( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetAmplitude + */ + float GetAmplitude( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetCenter + */ + Vector2 GetCenter( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetAmplitudePropertyName + */ + std::string GetAmplitudePropertyName( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetCenterPropertyName + */ + std::string GetCenterPropertyName( unsigned int index ) const; + + /** + * @copydoc Dali::Toolkit::WaterEffect::GetPropagationPropertyName + */ + std::string GetPropagationPropertyName( unsigned int index ) const; + +private: + + void Initialize( ShaderEffect shaderEffect ); + +private: + + ShaderEffect mShaderEffect; + + unsigned int mNumberOfWaves; + + +private: + + // Undefined copy constructor. + WaterEffect( const WaterEffect& ); + + // Undefined assignment operator. + WaterEffect& operator=( const WaterEffect& ); + +}; + +} // namespace Internal + +inline Internal::WaterEffect& GetImpl( Toolkit::WaterEffect& waterEffect ) +{ + return static_cast( waterEffect.GetExtension() ); +} + +inline const Internal::WaterEffect& GetImpl( const Toolkit::WaterEffect& waterEffect ) +{ + return static_cast( waterEffect.GetExtension() ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_INTERNAL_WATER_EFFECT_H__ diff --git a/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.cpp b/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.cpp new file mode 100644 index 0000000..227812f --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.cpp @@ -0,0 +1,139 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-cross-effect-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +CubeTransitionCrossEffect::CubeTransitionCrossEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ) +: CubeTransitionEffect( numRows, numColumns, viewAreaSize) +{ +} + +Toolkit::CubeTransitionCrossEffect CubeTransitionCrossEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + // Create the implementation + CubeTransitionCrossEffect* internalCubeTransEffect = new CubeTransitionCrossEffect( numRows, numColumns, viewAreaSize ); + + // Pass ownership to CustomActor handle + Toolkit::CubeTransitionCrossEffect cubeTransEffect( internalCubeTransEffect ); + + //Initialization + internalCubeTransEffect->Initialize(); + + return cubeTransEffect; +} + +void CubeTransitionCrossEffect::OnInitialize() +{ + float offsetX = -mTileSize.width*0.5f; + float offsetY = -mTileSize.height*0.5f; + unsigned int idx; + for( unsigned int y = 0; y < mNumRows; y++ ) + { + idx = y*mNumColumns; + for( unsigned int x = y%2; x < mNumColumns; x=x+2) + { + mBoxes[idx+x].SetZ( offsetY ); + mTiles[0][idx+x].SetZ( -offsetY ); + mTiles[1][idx+x].SetY( offsetY ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) + { + mTiles[0][idx+x].SetZ( -offsetX ); + mTiles[1][idx+x].SetX( offsetX ); + } + } +} + +void CubeTransitionCrossEffect::OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ) +{ + float angle = mRotateIndex * Math::PI_2 ; + Vector3 translation0 = mTiles[mContainerIndex][ 0 ].GetCurrentPosition()*(-2.f); + Vector3 translation1 = mTiles[mContainerIndex][ mNumColumns ].GetCurrentPosition()*(-2.f); + + unsigned int idx; + mDisplacementRatio = 1.f + mCubeDisplacement / (mTileSize.width + mTileSize.height); + + for( unsigned int y = 0; y < mNumRows; y++ ) + { + for( unsigned int x = y%2; x < mNumColumns; x=x+2) // rotate vertically + { + idx = y*mNumColumns + x; + SetupAnimation( idx, -angle, Vector3::XAXIS, translation0 ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) // rotate horizontally + { + idx = y*mNumColumns + x; + SetupAnimation( idx, angle, Vector3::YAXIS, translation1 ); + } + } + + mAnimation.Play(); + mIsAnimating = true; +} + +void CubeTransitionCrossEffect::OnStopTransition() +{ + float angle = mRotateIndex * Math::PI_2 ; + unsigned int idx; + for( unsigned int y = 0; y < mNumRows; y++ ) + { + for( unsigned int x = y%2; x < mNumColumns; x=x+2) + { + idx = y*mNumColumns + x; + mBoxes[idx].SetRotation( Radian(angle), Vector3::XAXIS ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) + { + idx = y*mNumColumns + x; + mBoxes[idx].SetRotation( Radian(-angle), Vector3::YAXIS ); + } + } +} + +void CubeTransitionCrossEffect::SetupAnimation(unsigned int actorIndex, float angle, + const Vector3 axis, Vector3 resetTranslation) +{ + if ( mFirstTransition && (!mIsToNextImage) ) // for the first transition and it is going to previous image + { + mTiles[mContainerIndex][actorIndex].SetRotation( Radian( angle), axis ); + } + else if( !mChangeTurningDirection ) // reset rotation, translation and color + { + mTiles[mContainerIndex][actorIndex].MoveBy( resetTranslation ); + mTiles[mContainerIndex][actorIndex].SetRotation( Radian( angle), axis ); + } + mAnimation.RotateTo( mBoxes[actorIndex], Radian( -angle ), axis, AlphaFunctions::EaseInOutSine ); + Vector3 position(mBoxes[actorIndex].GetCurrentPosition()); + mAnimation.MoveTo(mBoxes[actorIndex], position*mDisplacementRatio+Vector3(0.f,0.f,mCubeDisplacement), AlphaFunctions::Bounce); + mAnimation.ColorTo( mTiles[mContainerIndex^1][actorIndex], HALF_BRIGHTNESS, AlphaFunctions::EaseOut ); + mAnimation.ColorTo( mTiles[mContainerIndex][actorIndex], FULL_BRIGHTNESS, AlphaFunctions::EaseIn ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.h b/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.h new file mode 100644 index 0000000..dfe8eca --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-cross-effect-impl.h @@ -0,0 +1,118 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_CROSS_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_CROSS_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class CubeTransitionCrossEffect; + +namespace Internal +{ + +class CubeTransitionEffect; + +class CubeTransitionCrossEffect : public CubeTransitionEffect +{ + +public: + + /** + * @copydoc Toolkit::CubeTransitionCrossEffect::New + */ + static Toolkit::CubeTransitionCrossEffect New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize); + +protected: + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnInitialize + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnStartTransition + */ + virtual void OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ); + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnStopTransition + */ + virtual void OnStopTransition(); + +private: + + /** + * Construct a new CubeTransitionCrossEffect object + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + */ + CubeTransitionCrossEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + /** + * Set up animation to an Actor + * @param[in] actorIndex The index of the cube in the cube array + * @param[in] angle The angle of the rotation animation + * @param[in] axis The axis of the rotation animation + * @param[in] resetTranslation The translation used to reset the actor position before animation + */ + + void SetupAnimation( unsigned int actorIndex, float angle, const Vector3 axis, Vector3 resetTranslation ); + +private: + + float mDisplacementRatio; + +}; //class CubeTransitionCrossEffect + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::CubeTransitionCrossEffect& GetImpl(Dali::Toolkit::CubeTransitionCrossEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::CubeTransitionCrossEffect& GetImpl(const Dali::Toolkit::CubeTransitionCrossEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_CROSS_EFFECT_H_ */ diff --git a/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.cpp b/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.cpp new file mode 100644 index 0000000..ce9efce --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.cpp @@ -0,0 +1,346 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-effect-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +const Vector4 CubeTransitionEffect::FULL_BRIGHTNESS( 1.0f, 1.0f, 1.0f, 1.0f ); +const Vector4 CubeTransitionEffect::HALF_BRIGHTNESS( 0.5f, 0.5f, 0.5f, 1.0f ); + +CubeTransitionEffect::CubeTransitionEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ) +: mNumRows( numRows ), + mNumColumns( numColumns ), + mViewAreaSize( viewAreaSize ), + mRotateIndex( 0 ), + mContainerIndex( 0 ), + mChangeTurningDirection( false ), + mIsToNextImage( true ), + mIsImageLoading( false ), + mAnimationDuration( 1.f ), + mIsAnimating( false ), + mIsPaused( false ), + mCubeDisplacement( 0.f ), + mFirstTransition( true ), + mBufferIndex( 0 ) +{ +} + +CubeTransitionEffect::~CubeTransitionEffect() +{ +} + +void CubeTransitionEffect::Initialize() +{ + //create root actor for the cube transition effect, only visible during the transition + mRoot = Actor::New(); + mRoot.SetParentOrigin( ParentOrigin::CENTER ); + mRoot.SetAnchorPoint( AnchorPoint::CENTER ); + mRoot.SetVisible(false); + + // create two groups of tiles, + // and one group of actors (cubes) serving as parents of every two tiles (one from each image). + unsigned int totalNum = mNumColumns* mNumRows; + mBoxes.resize( totalNum ); + mTiles[0].resize( totalNum ); + mTiles[1].resize( totalNum ); + mTileSize = Vector2( mViewAreaSize.width / mNumColumns, mViewAreaSize.height / mNumRows ); + const Vector3 basePosition( (-mViewAreaSize.width + mTileSize.width) * 0.5f, + (-mViewAreaSize.height + mTileSize.height) * 0.5f, + -mTileSize.width * 0.5f ); + + Image placeHolder = BitmapImage::WHITE(); + for( unsigned int y = 0; y < mNumRows; y++ ) + { + float positionY = y * mTileSize.height + basePosition.y; + for( unsigned int x = 0; x < mNumColumns; x++) + { + unsigned int idx = y*mNumColumns + x; + Actor actor( Actor::New() ); + mBoxes[idx] = actor; + actor.SetParentOrigin( ParentOrigin::CENTER ); + actor.SetAnchorPoint( AnchorPoint::CENTER ); + actor.SetPosition( x * mTileSize.width + basePosition.x, + positionY, + basePosition.z ); + mRoot.Add( actor ); + + mTiles[ 0 ][idx] = CreateTile( placeHolder, FULL_BRIGHTNESS ); + actor.Add( mTiles[ 0 ][idx] ); + + mTiles[ 1 ][idx] = CreateTile( placeHolder, HALF_BRIGHTNESS ); + actor.Add( mTiles[ 1 ][idx] ); + } + } + + // helper actor to create a off-screen image using shader effect + mEmptyImage = ImageActor::New( placeHolder ); + mEmptyImage.SetSize(Stage::GetCurrent().GetSize()); + mEmptyImage.SetParentOrigin( ParentOrigin::CENTER ); + mEmptyImage.SetAnchorPoint( AnchorPoint::CENTER ); + mFullImageCreator = FullAreaImageCreator::New(); + mEmptyImage.SetShaderEffect( mFullImageCreator ); + Stage::GetCurrent().Add(mEmptyImage); + + // set up off-screen render task + RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList(); + mOffScreenTask = taskList.CreateTask(); + mOffScreenTask.SetSourceActor(mEmptyImage); + mOffScreenTask.SetExclusive(true); + mOffScreenBuffer[0] = FrameBufferImage::New(mViewAreaSize.x, mViewAreaSize.y); + mOffScreenBuffer[1] = FrameBufferImage::New(mViewAreaSize.x, mViewAreaSize.y); + mOffScreenTask.SetTargetFrameBuffer(mOffScreenBuffer[mBufferIndex]); + mOffScreenTask.SetRefreshRate(RenderTask::REFRESH_ONCE); + + OnInitialize(); +} + +ImageActor CubeTransitionEffect::CreateTile( Image image, const Vector4& color ) +{ + ImageActor tile = ImageActor::New( image ); + tile.SetParentOrigin( ParentOrigin::CENTER ); + tile.SetAnchorPoint( AnchorPoint::CENTER ); + tile.SetSize( mTileSize ); + tile.SetColorMode( Dali::USE_OWN_COLOR ); + tile.SetColor( color ); + + return tile; +} + +void CubeTransitionEffect::SetTransitionDuration( float duration ) +{ + mAnimationDuration = duration; +} + +float CubeTransitionEffect::GetTransitionDuration( ) const +{ + return mAnimationDuration; +} + +void CubeTransitionEffect::SetCubeDisplacement( float displacement ) +{ + mCubeDisplacement = displacement; +} + +float CubeTransitionEffect::GetCubeDisplacement() const +{ + return mCubeDisplacement; +} + +Actor CubeTransitionEffect::GetRoot() +{ + return mRoot; +} + +bool CubeTransitionEffect::IsTransiting() +{ + return mIsImageLoading || mIsAnimating; +} + +void CubeTransitionEffect::SetCurrentImage( ImageActor imageActor ) +{ + mContainerIndex = std::abs(mRotateIndex) % 2; + SetImage( imageActor ); +} + +void CubeTransitionEffect::SetTargetImage( ImageActor imageActor ) +{ + mContainerIndex = std::abs( mRotateIndex+1 ) % 2; + SetImage( imageActor ); +} + +void CubeTransitionEffect::SetImage( ImageActor imageActor ) +{ + mCurrentImage = imageActor; + mIsImageLoading = true; + + Image image = imageActor.GetImage(); + mBufferIndex = mBufferIndex^1; + + //must make sure the image is already loaded before using its attributes + if( image.GetLoadingState() == ResourceLoadingSucceeded ) + { + OnImageLoaded( image ); + } + else + { + image.LoadingFinishedSignal().Connect( this, &CubeTransitionEffect::OnImageLoaded ); + } +} + +void CubeTransitionEffect::StartTransition( bool toNextImage ) +{ + if( toNextImage ) + { + StartTransition( Vector2( mViewAreaSize.width, mViewAreaSize.height*0.5f ), Vector2( -10.f, 0.f ) ); + } + else + { + StartTransition( Vector2( 0, mViewAreaSize.height*0.5f ), Vector2( 10.f, 0.f )); + } +} + +void CubeTransitionEffect::StartTransition( Vector2 panPosition, Vector2 panDisplacement ) +{ + mRoot.SetVisible( true ); + mCurrentImage.SetVisible( false ); + bool toNextImage = ( panDisplacement.x < 0 ) ? true : false; + if( mIsToNextImage != toNextImage ) + { + mChangeTurningDirection = true; + } + else + { + mChangeTurningDirection = false; + } + mIsToNextImage = toNextImage; + + if( mIsToNextImage ) + { + mRotateIndex += 1.f; + } + else + { + mRotateIndex -= 1.f; + } + + if(mAnimation) + { + mAnimation.Clear(); + mAnimation.Reset(); + } + mAnimation = Animation::New( mAnimationDuration ); + mAnimation.FinishedSignal().Connect(this, &CubeTransitionEffect::OnTransitionFinished); + + OnStartTransition( panPosition, panDisplacement ); +} + +void CubeTransitionEffect::PauseTransition() +{ + if( mIsAnimating && !mIsPaused ) + { + mAnimation.Pause(); + mIsPaused = true; + } +} + +void CubeTransitionEffect::ResumeTransition() +{ + if( mIsAnimating && mIsPaused) + { + mAnimation.Play(); + mIsPaused = false; + } +} + +void CubeTransitionEffect::StopTransition() +{ + if( mIsAnimating ) + { + mAnimation.Clear(); + mAnimation.Reset(); + mIsPaused = false; + + //reset the position of the cubes + //reset the color of the tiles + //all these status should be the same as the final state when the transition animation is finished completely + const Vector3 basePosition( (-mViewAreaSize.width + mTileSize.width) * 0.5f, + (-mViewAreaSize.height + mTileSize.height) * 0.5f, + -mTileSize.width * 0.5f ); + unsigned int anotherIndex = mContainerIndex^1; + for( unsigned int y = 0; y < mNumRows; y++ ) + { + float positionY = y * mTileSize.height + basePosition.y; + for( unsigned int x = 0; x < mNumColumns; x++) + { + unsigned int idx = y*mNumColumns + x; + mBoxes[idx].SetPosition( x * mTileSize.width + basePosition.x, + positionY, + basePosition.z ); + mTiles[mContainerIndex][idx].SetColor( FULL_BRIGHTNESS ); + mTiles[anotherIndex][idx].SetColor( HALF_BRIGHTNESS); + } + } + + // reset the rotation of the cubes, which is different process for different derived classes + OnStopTransition(); + + mRoot.SetVisible(false); + mCurrentImage.SetVisible(true); + mIsAnimating = false; + mFirstTransition = false; + } +} + +void CubeTransitionEffect::OnImageLoaded(Image image) +{ + // Fit the image to view area, while keeping the aspect; FitKeepAspectRatio(imageSize, viewAreaSize) + ImageAttributes attributes( image.GetAttributes() ); + float scale = std::min( mViewAreaSize.width / attributes.GetWidth(), mViewAreaSize.height / attributes.GetHeight() ); + Vector2 imageSize(attributes.GetWidth()*scale, attributes.GetHeight()*scale); + + mFullImageCreator.SetEffectImage(image); + mFullImageCreator.SetRegionSize(mViewAreaSize, imageSize); + + mOffScreenTask.SetTargetFrameBuffer(mOffScreenBuffer[mBufferIndex]); + mOffScreenTask.SetRefreshRate(RenderTask::REFRESH_ONCE); + + ImageActor::PixelArea pixelArea( 0, 0, mViewAreaSize.x / mNumColumns, mViewAreaSize.y / mNumRows); + + for( unsigned int y = 0; y < mNumRows; y++ ) + { + pixelArea.y = y * pixelArea.height; + for( unsigned int x = 0; x < mNumColumns; x++) + { + pixelArea.x = x * pixelArea.width; + unsigned int idx = y*mNumColumns + x; + mTiles[mContainerIndex][idx].SetImage( mOffScreenBuffer[mBufferIndex]); + mTiles[mContainerIndex][idx].SetPixelArea( pixelArea ); + } + } + mIsImageLoading = false; +} + +void CubeTransitionEffect::OnTransitionFinished(Animation& source) +{ + mRoot.SetVisible(false); + mCurrentImage.SetVisible(true); + mIsAnimating = false; + mFirstTransition = false; + + //Emit signal + Toolkit::CubeTransitionEffect handle( this ); + mTransitionCompletedSignalV2.Emit( handle, mCurrentImage ); +} + +Toolkit::CubeTransitionEffect::TransitionCompletedSignalV2& CubeTransitionEffect::TransitionCompletedSignal() +{ + return mTransitionCompletedSignalV2; +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.h b/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.h new file mode 100644 index 0000000..3a69422 --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-effect-impl.h @@ -0,0 +1,343 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class CubeTransitionEffect; + +namespace Internal +{ + +/** + * Create a image with size of viewAreaSize + * with the effect image as its center part and (0,0,0,1) at other parts + */ +class FullAreaImageCreator : public ShaderEffect +{ + +public: + + /** + * Create an uninitialized FullAreaImageCreator + * this can be initialized with FullAreaImageCreator::New() + */ + FullAreaImageCreator(){} + + /** + * virtual destructor + */ + virtual ~FullAreaImageCreator(){} + + /** + * Create an initialized FullAreaImageCreator. + * @return A handle to a newly allocated Dali resource. + */ + static FullAreaImageCreator New() + { + std::string vertexShader( + "uniform vec4 uRegion; \n" + "void main() \n" + "{\n" + " gl_Position = uProjection * uModelView * vec4(aPosition, 1.0);\n" + " vTexCoord.s = (aTexCoord.s - uRegion.s) / uRegion.p;" + " vTexCoord.t = ( 1.0 - aTexCoord.t - uRegion.t) / uRegion.q;" + "}\n" + ); + + std::string fragmentShader( + "uniform vec4 uRegion; \n" + "void main() \n" + "{\n" + " if( vTexCoord.s > 0.0 && vTexCoord.s < 1.0 && vTexCoord.t > 0.0 && vTexCoord.t < 1.0) \n" + " { \n" + " gl_FragColor = texture2D( sEffect, vTexCoord ) * uColor ; \n" + " } \n" + " else \n" + " { \n" + " gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); \n" + " } \n" + "}\n" + ); + + ShaderEffect shaderEffectCustom = ShaderEffect::New(vertexShader, fragmentShader); + FullAreaImageCreator handle( shaderEffectCustom ); + + return handle; + } + + /** + * Set up the position and size of the effect texture + * @param[in] viewArea the size of full-area image to create + * @param[in] size the size of effect texture + */ + void SetRegionSize( const Vector2& viewArea, const Vector2& size ) + { + Vector2 sizeRatio( std::min(1.f, size.x / viewArea.x), std::min(1.f, size.y / viewArea.y) ); + Vector4 region( (1.f-sizeRatio.x)*0.5f, + (1.f-sizeRatio.y)*0.5f, + sizeRatio.x, + sizeRatio.y ); + SetUniform( "uRegion", region ); + } + +private: + + FullAreaImageCreator( ShaderEffect handle ) + : ShaderEffect( handle ) + {} + +}; + + + +/** + * CubeTransitionEffect implementation class + */ +class CubeTransitionEffect : public Dali::BaseObject, public ConnectionTracker +{ + +public: + + /** + * Destructor + */ + ~CubeTransitionEffect(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::SetTransitionDuration + */ + void SetTransitionDuration( float duration ); + + /** + * @copydoc Toolkit::CubeTransitionEffect::GetTransitionDuration + */ + float GetTransitionDuration() const; + + /** + * @copydoc Toolkit::CubeTransitionEffect::SetCubeDisplacement + */ + void SetCubeDisplacement( float displacement ); + + /** + * @copydoc Toolkit::CubeTransitionEffect::GetCubeDisplacement + */ + float GetCubeDisplacement() const; + + /** + * @copydoc Toolkit::CubeTransitionEffect::GetRoot + */ + Actor GetRoot(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::IsTransiting + */ + bool IsTransiting(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::SetFirstImage + */ + void SetCurrentImage(ImageActor imageActor); + + /** + * @copydoc Toolkit::CubeTransitionEffect::SetTargetImage + */ + void SetTargetImage(ImageActor imageActor); + + /** + * @copydoc Toolkit::CubeTransitionEffect::StartTransition(bool) + */ + void StartTransition( bool toNextImage = true ); + + /** + * @copydoc Toolkit::CubeTransitionEffect::StartTransition(Vector2, Vector2) + */ + void StartTransition( Vector2 panPosition, Vector2 panDisplacement ); + + /** + * @copydoc Toolkit::CubeTransitionEffect::PauseTransition() + */ + void PauseTransition(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::ResumeTransition() + */ + void ResumeTransition(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::StopTransition() + */ + void StopTransition(); + +public: //Signal + + /** + * @copydoc Toolkit::CubeTransitionEffect::TransitionCompletedSignal() + */ + Toolkit::CubeTransitionEffect::TransitionCompletedSignalV2& TransitionCompletedSignal(); + +protected: + + /** + * Construct a new CubeTransitionEffect object + * Called in the constructor of subclasses + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + */ + CubeTransitionEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + /** + * Initialization steps: creating a layer, two groups of tiles, + * and one group of actors (cubes) serving as parents of every two tiles (one from each image). + */ + void Initialize(); + +private: + + /** + * Create an image actor to serve as a face of the cube + * @param[in] image The image to display. + * @param[in] color The color to set to the actor + * @return The tile actor created + */ + ImageActor CreateTile( Image image, const Vector4& color ); + + /** + * Set Image content to tiles + * As only when the image ready, can we get correct image attributes + * so inside this function, the process needs to be passed to callBack of image resource loading succeed. + * @param[in] imageActor The imageActor whose image content will be set to the tiles + */ + void SetImage(ImageActor imageActor); + + /** + * Callback function of image resource loading succeed + * Set image and pixelArea to tiles + * @param[in] image The image content of the imageActor for transition + */ + void OnImageLoaded(Image image); + + /** + * Callback function of transition animation finished + * Hide transition layer, show current imageActor, and set isAnimating flag to false + * @param[in] source The cube transition animation + */ + void OnTransitionFinished(Animation& source); + + /** + * This method is called after the CubeTransitionEffect has been initialized. Derived classes should do + * any second phase initialization by overriding this method. + */ + virtual void OnInitialize() { } + + /** + * This method is called after the a new transition is activated. + * Derived classes should do any specialized transition process by overriding this method. + * @param[in] panPosition The press down position of panGesture + * @param[in] panDisplacement The displacement vector of panGesture + */ + virtual void OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ) {} + + /** + * This method is called when the transition is forced stop in the middle of animation. + * Derived classed should set the rotation status of the cubes to the same as the final state when the animation is finished completely. + * So that the next transition would be started correctly. + */ + virtual void OnStopTransition() {} + + +protected: + + unsigned int mNumRows; + unsigned int mNumColumns; + Size mViewAreaSize; + ActorContainer mBoxes; + std::vector< ImageActor > mTiles[2]; + int mRotateIndex; + Size mTileSize; + Actor mRoot; + + ImageActor mCurrentImage; + unsigned int mContainerIndex; //have the value 0 or 1, refer to mTiles[0] or mTiles[1] + + bool mChangeTurningDirection; + bool mIsToNextImage; //if true, cubes rotate counter-clockwise; else clockwise + bool mIsImageLoading; + + float mAnimationDuration; + Animation mAnimation; + bool mIsAnimating; + bool mIsPaused; + + float mCubeDisplacement; + + bool mFirstTransition; + + RenderTask mOffScreenTask; + FrameBufferImage mOffScreenBuffer[2]; + ImageActor mEmptyImage; + FullAreaImageCreator mFullImageCreator; + unsigned int mBufferIndex; + + static const Vector4 FULL_BRIGHTNESS; + static const Vector4 HALF_BRIGHTNESS; + +private: + + Toolkit::CubeTransitionEffect::TransitionCompletedSignalV2 mTransitionCompletedSignalV2; + +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::CubeTransitionEffect& GetImpl(Dali::Toolkit::CubeTransitionEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::CubeTransitionEffect& GetImpl(const Dali::Toolkit::CubeTransitionEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_EFFECT_H__ */ diff --git a/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.cpp b/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.cpp new file mode 100644 index 0000000..74260d8 --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.cpp @@ -0,0 +1,138 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-fold-effect-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ +const float CubeTransitionFoldEffect::mDisplacementRatio = 1.4142f; //sqrt(2) + +CubeTransitionFoldEffect::CubeTransitionFoldEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ) +: CubeTransitionEffect( numRows, numColumns, viewAreaSize) +{ +} + +Toolkit::CubeTransitionFoldEffect CubeTransitionFoldEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + // Create the implementation + CubeTransitionFoldEffect* internalCubeTransEffect = new CubeTransitionFoldEffect( numRows, numColumns, viewAreaSize ); + + // Pass ownership to CustomActor handle + Toolkit::CubeTransitionFoldEffect cubeTransEffect( internalCubeTransEffect ); + + //Initialization + internalCubeTransEffect->Initialize(); + + return cubeTransEffect; +} + +void CubeTransitionFoldEffect::OnInitialize() +{ + float offset = mTileSize.width*0.5f; + unsigned int idx; + for( unsigned int y = 0; y < mNumRows; y++ ) + { + idx = y*mNumColumns; + for( unsigned int x = y%2; x < mNumColumns; x=x+2) + { + mTiles[0][idx+x].SetZ( offset ); + mTiles[1][idx+x].SetX( offset ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) + { + mTiles[0][idx+x].SetZ( offset ); + mTiles[1][idx+x].SetX( -offset ); + } + } +} + +void CubeTransitionFoldEffect::OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ) +{ + float angle = mRotateIndex * Math::PI_2 ; + Vector3 translation0 = mTiles[mContainerIndex][ 0 ].GetCurrentPosition()*(-2.f); + Vector3 translation1 = mTiles[mContainerIndex][ mNumColumns ].GetCurrentPosition()*(-2.f); + + unsigned int idx; + + for( unsigned int y = 0; y < mNumRows; y++ ) + { + for( unsigned int x = y%2; x < mNumColumns; x=x+2) + { + idx = y*mNumColumns + x; + SetupAnimation( idx, -angle, translation0 ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) + { + idx = y*mNumColumns + x; + SetupAnimation( idx, angle, translation1 ); + } + } + + mAnimation.Play(); + mIsAnimating = true; +} + +void CubeTransitionFoldEffect::OnStopTransition() +{ + float angle = mRotateIndex * Math::PI_2 ; + unsigned int idx; + for( unsigned int y = 0; y < mNumRows; y++ ) + { + idx = y*mNumColumns; + for( unsigned int x = y%2; x < mNumColumns; x=x+2) + { + mBoxes[idx+x].SetRotation( Radian(angle), Vector3::YAXIS ); + } + for( unsigned int x = (y+1)%2; x < mNumColumns; x=x+2) + { + mBoxes[idx+x].SetRotation( Radian(-angle), Vector3::YAXIS ); + } + } +} + +void CubeTransitionFoldEffect::SetupAnimation(unsigned int actorIndex, float angle, Vector3 resetTranslation) +{ + Actor currentCube = mBoxes[actorIndex]; + ImageActor sideTile = mTiles[mContainerIndex][actorIndex]; + ImageActor frontTile = mTiles[mContainerIndex^1][actorIndex]; + if ( mFirstTransition && (!mIsToNextImage) ) // for the first transition, it is going to previous image + { + sideTile.SetRotation( Radian( angle), Vector3::YAXIS ); + } + else if( !mChangeTurningDirection ) // reset rotation, translation and color + { + sideTile.MoveBy( resetTranslation ); + sideTile.SetRotation( Radian( angle), Vector3::YAXIS ); + } + mAnimation.RotateTo( currentCube, Radian( -angle ), Vector3::YAXIS, AlphaFunctions::Linear ); + Vector3 position(currentCube.GetCurrentPosition()); + mAnimation.MoveTo(currentCube, Vector3( position.x*mDisplacementRatio, position.y, position.z ), AlphaFunctions::Bounce); + mAnimation.ColorTo( frontTile, HALF_BRIGHTNESS, AlphaFunctions::EaseOut ); + mAnimation.ColorTo( sideTile, FULL_BRIGHTNESS, AlphaFunctions::EaseIn ); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.h b/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.h new file mode 100644 index 0000000..3b003dc --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-fold-effect-impl.h @@ -0,0 +1,116 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_FOLD_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_FOLD_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class CubeTransitionFoldEffect; + +namespace Internal +{ + +class CubeTransitionEffect; + +class CubeTransitionFoldEffect : public CubeTransitionEffect +{ + +public: + + /** + * @copydoc Toolkit::CubeTransitionFoldEffect::New + */ + static Toolkit::CubeTransitionFoldEffect New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize); + +protected: + + /** + * @copydoc Toolkit::CubeTransitionEffect::OnInitialize + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::CubeTransitionEffect::OnStartTransition + */ + virtual void OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ); + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnStopTransition + */ + virtual void OnStopTransition(); +private: + + /** + * Construct a new CubeTransitionFoldEffect object + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + */ + CubeTransitionFoldEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + /** + * Set up animation to an Actor + * @param[in] actorIndex The index of the cube in the cube array + * @param[in] angle The angle of the rotation animation + * @param[in] resetTranslation The translation used to reset the actor position before animation + */ + + void SetupAnimation( unsigned int actorIndex, float angle, Vector3 resetTranslation ); + +private: + + static const float mDisplacementRatio; + +}; //class CubeTransitionFoldEffect + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::CubeTransitionFoldEffect& GetImpl(Dali::Toolkit::CubeTransitionFoldEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::CubeTransitionFoldEffect& GetImpl(const Dali::Toolkit::CubeTransitionFoldEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_FOLD_EFFECT_H_ */ diff --git a/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.cpp b/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.cpp new file mode 100644 index 0000000..4754938 --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.cpp @@ -0,0 +1,201 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-wave-effect-impl.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +CubeTransitionWaveEffect::CubeTransitionWaveEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ) +: CubeTransitionEffect( numRows, numColumns, viewAreaSize) +{ +} + +Toolkit::CubeTransitionWaveEffect CubeTransitionWaveEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + // Create the implementation + CubeTransitionWaveEffect* internalCubeTransEffect = new CubeTransitionWaveEffect( numRows, numColumns, viewAreaSize ); + + // Pass ownership to CustomActor handle + Toolkit::CubeTransitionWaveEffect cubeTransEffect( internalCubeTransEffect ); + + //Initialization + internalCubeTransEffect->Initialize(); + + return cubeTransEffect; +} + +void CubeTransitionWaveEffect::OnInitialize() +{ + float offset = -mTileSize.width * 0.5f; + unsigned int totalNum = mNumColumns* mNumRows; + for( unsigned int idx = 0; idx < totalNum; idx++ ) + { + mTiles[ 0 ][idx].SetZ( -offset ); + mTiles[ 1 ][idx].SetX( offset ); + } +} + +void CubeTransitionWaveEffect::OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ) +{ + float direc = mIsToNextImage ? 1.f : -1.f; + CalculateSaddleSurfaceParameters( panPosition, panDisplacement*direc ); + + float angle = mRotateIndex * 90.0f ; + Vector3 translation = mTiles[mContainerIndex][ 0 ].GetCurrentPosition()*(-2.f); + + unsigned int idx; + unsigned int totalNum = mNumColumns* mNumRows; + if( mFirstTransition && (!mIsToNextImage) ) // the first transition is transiting to previous image + { + for( unsigned int idx = 0; idx < totalNum; idx++ ) + { + mTiles[mContainerIndex][idx].SetRotation( Degree( angle), Vector3::YAXIS ); + } + } + else if(!mChangeTurningDirection) // reset rotation, translation + { + for( unsigned int idx = 0; idx < totalNum; idx++ ) + { + mTiles[mContainerIndex][idx].MoveBy( translation ); + mTiles[mContainerIndex][idx].SetRotation( Degree( angle), Vector3::YAXIS ); + } + } + + float thirdAnimationDuration = mAnimationDuration / 3.f; + unsigned int anotherIndex = mContainerIndex^1; + + for( unsigned int y = 0; y < mNumRows; y++ ) + { + for( unsigned int x = 0; x < mNumColumns; x++) + { + idx = y*mNumColumns + x; + // the delay value is within 0.f ~ 2.f*thirdAnimationDuration + float delay = thirdAnimationDuration * CalculateDelay(x*mTileSize.width,y*mTileSize.height); + + mAnimation.RotateTo( mBoxes[idx], Degree( -angle ), Vector3::YAXIS, + AlphaFunctions::EaseOutSine, delay, thirdAnimationDuration ); + mAnimation.MoveBy( mBoxes[idx], Vector3(0.f,0.f,-mCubeDisplacement), + AlphaFunctions::Bounce, delay, thirdAnimationDuration ); + mAnimation.ColorTo( mTiles[anotherIndex][idx], HALF_BRIGHTNESS, + AlphaFunctions::EaseOut, delay, thirdAnimationDuration ); + mAnimation.ColorTo( mTiles[mContainerIndex][idx], FULL_BRIGHTNESS, + AlphaFunctions::EaseIn, delay, thirdAnimationDuration ); + } + } + + mAnimation.Play(); + mIsAnimating = true; +} + +void CubeTransitionWaveEffect::OnStopTransition() +{ + float angle = - mRotateIndex * 90.0f ; + unsigned int totalNum = mNumRows * mNumColumns; + for( unsigned int idx = 0; idx < totalNum; idx++ ) + { + mBoxes[idx].SetRotation( Degree( angle ), Vector3::YAXIS ); + } +} + +void CubeTransitionWaveEffect::CalculateSaddleSurfaceParameters( Vector2 position, Vector2 displacement ) +{ + // the line passes through 'position' and has the direction of 'displacement' + float coefA, coefB, coefC; //line equation: Ax+By+C=0; + coefA = displacement.y; + coefB = -displacement.x; + coefC = -displacement.y*position.x + displacement.x*position.y; + + float inversedAABB = 1.f / (coefA*coefA+coefB*coefB); + float inversedSqrtAABB = sqrtf(inversedAABB); + float saddleA; + + if(displacement.y > 0) + { + //distance from (0,0) to the line + float distanceTopLeft = fabsf(coefC) * inversedSqrtAABB; + //distance from (viewAreaSize.x, viewAreaSize.y) to the line + float distanceBottomRight = fabsf(coefA*mViewAreaSize.x+coefB*mViewAreaSize.y+coefC) * inversedSqrtAABB; + saddleA = std::max( distanceTopLeft, distanceBottomRight ); + + //foot of a perpendicular: (viewAreaSize.x,0) to the line + float footX1 = ( coefB*coefB*mViewAreaSize.x - coefA*coefC) * inversedAABB; + float footY1 = (-coefA*coefB*mViewAreaSize.x - coefB*coefC) * inversedAABB; + //foot of a perpendicular: (0,viewAreaSize.y) to the line + float footX2 = (-coefA*coefB*mViewAreaSize.y - coefA*coefC) * inversedAABB; + float footY2 = ( coefA*coefA*mViewAreaSize.y - coefB*coefC) * inversedAABB; + mSaddleBB = (footX1-footX2)*(footX1-footX2) + (footY1-footY2)*(footY1-footY2); + mTranslation = Vector2(-footX2,-footY2); + } + else + { + //distance from(viewAreaSize.x,0) to the line + float distanceTopRight = fabsf(coefA*mViewAreaSize.x+coefC) * inversedSqrtAABB; + //distance from(0,viewAreaSize.y) to the line + float distanceBottomLeft = fabsf(coefB*mViewAreaSize.y+coefC) * inversedSqrtAABB; + saddleA = std::max( distanceTopRight, distanceBottomLeft ); + //foot of a perpendicular: (0,0) to the line + float footX3 = (-coefA*coefC) * inversedAABB; + float footY3 = (-coefB*coefC) * inversedAABB; + //foot of a perpendicular: (viewAreaSize.x,viewAreaSize.y) to the line + float footX4 = ( coefB*coefB*mViewAreaSize.x - coefA*coefB*mViewAreaSize.y - coefA*coefC) * inversedAABB; + float footY4 = (-coefA*coefB*mViewAreaSize.x + coefA*coefA*mViewAreaSize.y - coefB*coefC) * inversedAABB; + mSaddleBB = (footX3-footX4)*(footX3-footX4) + (footY3-footY4)*(footY3-footY4); + mTranslation = Vector2(-footX3, -footY3); + } + + mSaddleB = sqrtf(mSaddleBB); + //prevent high curve shape + if(mSaddleB > 2.f * saddleA) + { + saddleA = mSaddleB * 0.5f; + } + else if(mSaddleB < saddleA) + { + mSaddleB = saddleA; + mSaddleBB = mSaddleB*mSaddleB; + } + mSaddleAA = saddleA*saddleA; + mRotation = Vector2(-displacement.x, displacement.y); + mRotation.Normalize(); +} + +float CubeTransitionWaveEffect::CalculateDelay(float x, float y) +{ + float tx = x + mTranslation.x; + float ty = y + mTranslation.y; + float valueX = mRotation.x * tx - mRotation.y * ty; + float valueY = mRotation.y * tx + mRotation.x * ty; + if(!mIsToNextImage) // to previous image + { + valueX = mSaddleB - valueX; + } + //the return value is a float number between 0.f and 2.f + return (1.f + valueY*valueY / mSaddleAA - valueX*valueX / mSaddleBB); +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.h b/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.h new file mode 100644 index 0000000..9cdfe96 --- /dev/null +++ b/dali-toolkit/internal/transition-effects/cube-transition-wave-effect-impl.h @@ -0,0 +1,132 @@ +#ifndef __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_WAVE_EFFECT_H__ +#define __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_WAVE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class CubeTransitionWaveEffect; + +namespace Internal +{ + +class CubeTransitionEffect; + +class CubeTransitionWaveEffect : public CubeTransitionEffect +{ + +public: + + /** + * @copydoc Toolkit::CubeTransitionWaveEffect::New + */ + static Toolkit::CubeTransitionWaveEffect New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize); + +protected: + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnInitialize + */ + virtual void OnInitialize(); + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnStartTransition + */ + virtual void OnStartTransition( Vector2 panPosition, Vector2 panDisplacement ); + + /** + * @copydoc Toolkit::Internal::CubeTransitionEffect::OnStopTransition + */ + virtual void OnStopTransition(); + +private: + + /** + * Construct a new CubeTransitionWaveEffect object + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + */ + CubeTransitionWaveEffect( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + /** + * The Saddle surface (Hyperbolic paraboloid)function is used to calculate the delay of rotating animation for each cube + * This function calculates the Hyperbolic paraboloid parameters, + * and the translation and rotation params for mapping the current stage coordinate to the function defining coordinate system + * @param[in] position The press down position of panGesture + * @param[in] displacement The displacement vector of panGesture + */ + void CalculateSaddleSurfaceParameters( Vector2 position, Vector2 displacement ); + + /** + * Calculate the delay of the animation for each cube + * @param[in] x The X coordinate of the cube + * @param[in] y The Y coordinate of the cube + * @return The delay time of the animation + */ + float CalculateDelay(float x, float y); + +private: + + //saddle surface(Hyperbolic paraboloid)function, used to calculate the delay time of each cube + //z = 1.0 + y*y/a/a - x*x/b/b + //with our selection of parameters(a and b), this value for any cube is between 0.0 and 2.0 + float mSaddleAA; //a*a + float mSaddleBB; //b*b + float mSaddleB; //b + Vector2 mTranslation; + Vector2 mRotation; + +}; // class CubeTransitionWaveEffect + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::CubeTransitionWaveEffect& GetImpl(Dali::Toolkit::CubeTransitionWaveEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +inline const Internal::CubeTransitionWaveEffect& GetImpl(const Dali::Toolkit::CubeTransitionWaveEffect& obj) +{ + DALI_ASSERT_ALWAYS(obj); + + const Dali::BaseObject& handle = obj.GetBaseObject(); + + return static_cast(handle); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_INTERNAL_CUBE_TRANSITION_WAVE_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/builder/builder.cpp b/dali-toolkit/public-api/builder/builder.cpp new file mode 100644 index 0000000..8613016 --- /dev/null +++ b/dali-toolkit/public-api/builder/builder.cpp @@ -0,0 +1,129 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "builder.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +Builder::Builder() +{ +} + +Builder::~Builder() +{ +} + +Builder Builder::New(void) +{ + return Builder(new Internal::Builder()); +} + +Builder::Builder(Internal::Builder *impl) + : BaseHandle(impl) +{ +} + +void Builder::LoadFromString( const std::string &data, UIFormat rep ) +{ + GetImpl(*this).LoadFromString( data ); +} + +Animation Builder::CreateAnimation( const std::string& animationName ) +{ + return GetImpl(*this).CreateAnimation( animationName ); +} + +BaseHandle Builder::CreateFromStyle( const std::string& styleName ) +{ + return GetImpl(*this).CreateFromStyle( styleName ); +} + +void Builder::ApplyStyle( const std::string& styleName, Handle& handle ) +{ + GetImpl(*this).ApplyStyle( styleName, handle ); +} + +void Builder::AddActors( Actor toActor ) +{ + GetImpl(*this).AddActors( toActor ); +} + +void Builder::AddActors( const std::string §ionName, Actor toActor ) +{ + GetImpl(*this).AddActors( sectionName, toActor ); +} + +Font Builder::GetFont( const std::string &name ) const +{ + return GetImpl(*this).GetFont( name ); +} + +TextStyle Builder::GetTextStyle( const std::string &name ) const +{ + return GetImpl(*this).GetTextStyle( name ); +} + +Image Builder::GetImage( const std::string &name ) const +{ + return GetImpl(*this).GetImage( name ); +} + +Actor Builder::GetActor( const std::string &name ) const +{ + return GetImpl(*this).GetActor( name ); +} + +Animation Builder::GetAnimation( const std::string &name ) const +{ + return GetImpl(*this).GetAnimation( name ); +} + +void Builder::CreateRenderTask( const std::string &name ) +{ + GetImpl(*this).CreateRenderTask( name ); +} + +ShaderEffect Builder::GetShaderEffect( const std::string &name ) +{ + return GetImpl(*this).GetShaderEffect( name ); +} + +FrameBufferImage Builder::GetFrameBufferImage( const std::string &name ) +{ + return GetImpl(*this).GetFrameBufferImage( name ); +} + +ActorContainer Builder::GetTopLevelActors( void ) const +{ + return GetImpl(*this).GetTopLevelActors(); +} + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/builder/builder.h b/dali-toolkit/public-api/builder/builder.h new file mode 100644 index 0000000..8df2619 --- /dev/null +++ b/dali-toolkit/public-api/builder/builder.h @@ -0,0 +1,270 @@ +#ifndef __DALI_TOOLKIT_UIBUILDER_H__ +#define __DALI_TOOLKIT_UIBUILDER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Builder; +} + +/** + * Builder + * This class provides the ability to load an actor tree from a string representation. + * + * The following example is hello world in JSON. + * + * @code + * + * { + * "styles": + * { + * "default-text": + * { + * "type":"TextActor", + * "font":"", + * "parent-origin":[0.5,0.5,0], + * "scale": [50,50,1] + * } + * }, + * "stage": + * [ + * { + * "type":"default-text", + * "text":"Hello World", + * "position":[0,0,0] + * }, + * ] + * } + * + * + * + * @endcode + * + * The following is how to load the json data. + * + * @code + * + * Builder builder = Builder::New(); + * + * std::string json_data(ReadFile("layout.json")); + * + * builder.LoadFromString(json_data); + * + * // 1) load all actors in the "stage" section to the root layer + * builder.AddActors( Stage::GetCurrent().GetRootLayer() ); + * + * // or 2) create an actor from the library "styles" section + * TextActor actor = TextActor::DownCast( builder.CreateFromStyle( "default-text" ) ); + * + * @endcode + */ + class Builder : public BaseHandle + { + public: + /** + * Create an Builder handle; this can be initialised with Builder::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + Builder(); + + /** + * Creates an Builder object. + * @return A handle to the Builder control. + */ + static Builder New(); + + /** + * Virtual destructor. + */ + virtual ~Builder(); + + /** + * UI string data format + */ + enum UIFormat + { + JSON, ///< String is JSON + }; + + /** + * Loads a string representation of an actor tree into memory. + * The Actor is not automatically added to the stage. + * This function will raise an exception for parse and logical structure errors. + * @pre The Builder has been initialized. + * @pre Preconditions have been met for creating dali objects ie Images, Actors etc + * @param data A string represenation of an Actor tree + * @param format The string representation format ie JSON + */ + void LoadFromString( const std::string& data, UIFormat format = JSON ); + + /** + * Creates an animation from the set of known animations + * e.g. + * Animation a = builder.CreateAnimation( "wobble"); + * + * @pre The Builder has been initialized. + * @pre Preconditions have been met for creating dali objects ie Images, Actors etc + * @pre The animationName exists in the animations section of the data representation + * @param animationName The animation name to create + * @returns The base handle of the created object + */ + Animation CreateAnimation( const std::string& animationName ); + + /** + * Creates an object (e.g. an actor) from the set of known styles + * e.g. + * mActor.Add( Actor::DownCast(builder.CreateFromStyle( "default-text")) ); + * + * @pre The Builder has been initialized. + * @pre Preconditions have been met for creating dali objects ie Images, Actors etc + * @pre The styleName has been loaded from the styles section of the data representation + * and contains 'type' property used to create the object. + * @param styleName The set of styles/properties to set on the handle object. + * @returns The base handle of the created object + */ + BaseHandle CreateFromStyle( const std::string& styleName ); + + /** + * Apply a style (a collection of properties) to an actor. + * @pre The Builder has been initialized. + * @pre Preconditions have been met for creating dali objects ie Images, Actors etc + * @param styleName The name of the set of style properties to set on the handle object. + * @param handle Then handle of the object on which to set the properties. + */ + void ApplyStyle( const std::string& styleName, Handle& handle ); + + /** + * Add the actor tree in the "stage" section to the actor toActor. + * ie if the representation has a 'stage' section that contains a tree of + * actors then + * builder.AddActors( Stage::GetCurrent().GetRootLayer() ); + * will create and add the actors to the stage root layer. + * @param toActor The actor to add the created actors to + */ + void AddActors( Actor toActor ); + + /** + * Adds actors in the sectionName to the actor toActor. + * ie if the representation has a sectionName section that contains a tree of + * actors then + * builder.AddActors( sectionName, Stage::GetCurrent().GetRootLayer() ); + * will create and add the actors to the stage root layer. + * @param sectionName The section name to search for the actor tree + * @param toActor The actor to add the created actors to + */ + void AddActors( const std::string §ionName, Actor toActor ); + + /** + * @deprecated Font as a separate asset is no longer supported + * Get's a Font asset previously created at load time + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name given to a Font in the loaded representation + * @return A handle to a Font if found, otherwise empty. + */ + Font GetFont( const std::string &name ) const; + + /** + * Get's a TextStyle asset previously created at load time + * @pre The Builder has been initialized. + * @param name The name given to a TextStyle in the loaded representation + * @return a Created TextStyle if found, otherwise return a TextStyle created by Default constructor + */ + TextStyle GetTextStyle( const std::string &name ) const; + + /** + * @deprecated Images as a separate asset is no longer supported + * Get's an Image asset previously created at load time + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name given to an Image in the loaded representation + * @return A handle to an Image if found, otherwise empty + */ + Image GetImage( const std::string &name ) const; + + /** + * @deprecated Actors no longer held by builder + * Get's an Actor previously created at load time + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name given to an Actor in the loaded representation + * @return A handle to an Actor if found, otherwise empty + */ + Actor GetActor( const std::string &name ) const; + + /** + * @deprecated Animations no longer held by builder + * Get's an Animation previously created at load time + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name given to an Animation in the loaded representation + * @return A handle to an Animation if found, otherwise empty + */ + Animation GetAnimation( const std::string &name ) const; + + /** + * Create a render task set. + * @pre The Builder has been initialized. + * @param name The library name of the render task set. + */ + void CreateRenderTask( const std::string &name ); + + /** + * Get or create ShaderEffect from the ShaderEffect instance library. + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name of a ShaderEffect in the loaded representation + * @return A handle to a ShaderEffect if found, otherwise empty + */ + ShaderEffect GetShaderEffect( const std::string &name ); + + /** + * Get or create FrameBufferImage from the FrameBufferImage instance library. + * An empty handle is returned otherwise. + * @pre The Builder has been initialized. + * @param name The name of a FrameBufferImage in the loaded representation + * @return A handle to a FrameBufferImage if found, otherwise empty + */ + FrameBufferImage GetFrameBufferImage( const std::string &name ); + + /** + * @deprecated. Builder no longer holds actor handles/references + * Provides a list of the top level actors previously created at load time + * @return A container of Actors found at the top level of the loaded representation + */ + ActorContainer GetTopLevelActors( void ) const; + +private: + Builder(Internal::Builder *impl); + +}; // class Builder + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_UIBUILDER_H__ diff --git a/dali-toolkit/public-api/builder/json-parser.cpp b/dali-toolkit/public-api/builder/json-parser.cpp new file mode 100644 index 0000000..aa81362 --- /dev/null +++ b/dali-toolkit/public-api/builder/json-parser.cpp @@ -0,0 +1,114 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +JsonParser JsonParser::New() +{ + Internal::JsonParser* internal = new Internal::JsonParser(); + return JsonParser(internal); +} + +JsonParser JsonParser::New(const TreeNode& tree) +{ + Internal::JsonParser* internal = new Internal::JsonParser(tree); + return JsonParser(internal); +} + +JsonParser::JsonParser() +{ +} + +JsonParser::~JsonParser() +{ +} + +JsonParser DownCast( BaseHandle handle ) +{ + return JsonParser( dynamic_cast(handle.GetObjectPtr()) ); +} + +int JsonParser::Parse(const std::string& source) +{ + return GetImplementation(*this).Parse(source); +} + +void JsonParser::Pack(void) +{ + return GetImplementation(*this).Pack(); +} + +const TreeNode* JsonParser::GetRoot() const +{ + return GetImplementation(*this).GetRoot(); +} + +bool JsonParser::ParseError() const +{ + return GetImplementation(*this).ParseError(); +} + +int JsonParser::GetErrorPosition() const +{ + return GetImplementation(*this).GetErrorPosition(); +} + +std::string JsonParser::GetErrorDescription() const +{ + return GetImplementation(*this).GetErrorDescription(); +} + +int JsonParser::GetErrorLineNumber() const +{ + return GetImplementation(*this).GetErrorLineNumber(); +} + +int JsonParser::GetErrorColumn() const +{ + return GetImplementation(*this).GetErrorColumn(); +} + +void JsonParser::Write(std::ostream& output, int indent) const +{ + return GetImplementation(*this).Write(output, indent); +} + +JsonParser::JsonParser(Internal::JsonParser* internal) + : BaseHandle(internal) +{ +} + +} // namespace toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/builder/json-parser.h b/dali-toolkit/public-api/builder/json-parser.h new file mode 100644 index 0000000..a47ea63 --- /dev/null +++ b/dali-toolkit/public-api/builder/json-parser.h @@ -0,0 +1,146 @@ +#ifndef __DALI_JSON_PARSER_H__ +#define __DALI_JSON_PARSER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class JsonParser; +} + +/* + * Parses JSON + */ +class JsonParser : public BaseHandle +{ +public: + + /* + * Create new parser + * @return JsonParser + */ + static JsonParser New(); + + /* + * Create new parser from the given tree + * This method will deep copy the given tree. + * @return JsonParser + */ + static JsonParser New(const TreeNode& tree); + + /* + * Create empty handle + */ + JsonParser(); + + /* + * virtual Destructor + */ + virtual ~JsonParser(); + + /** + * Downcast an Object handle to JsonParser if it is a JsonParser. + * @param[in] handle Handle to an object + * @return A handle to a JsonParser or an uninitialized handle + */ + static JsonParser DownCast( BaseHandle handle ); + + /* + * Parse the source and construct a node tree. + * Subsequent calls to this function will merge the trees. + * @param source The json source to parse + * @return zero if parsed okay, otherwise an error. + */ + int Parse(const std::string& source); + + /* + * Optimize memory usage by packing strings + */ + void Pack(void); + + /* + * Get the tree root node + */ + const TreeNode* GetRoot() const; + + /* + * Get the parser error flag + * @return true if there was a parse error + */ + bool ParseError() const; + + /* + * Get the last error position + * @return The character position of the most recent Parse() error + */ + int GetErrorPosition() const; + + /* + * Get the last error description + * @return A string description of the error + */ + std::string GetErrorDescription() const; + + /* + * Get the last error line number + * @return the line number of the most recent Parse() error. + */ + int GetErrorLineNumber() const; + + /* + * Get the last error line number + * @return the line number of the most recent Parse() error. + */ + int GetErrorColumn() const; + + /* + * Write to output stream with optional indent + * @param output The stream to write to + * @param indent The indent to use + */ + void Write(std::ostream& output, int indent) const; + +public: // Not intended for application developers + + /** + * This constructor is used by Dali New() methods + * @param [in] parser A pointer to a newly allocated Dali resource + */ + explicit DALI_INTERNAL JsonParser(Internal::JsonParser* parser); +}; + +} // namespace Toolkit + +} // namespace Dali + + +#endif // __DALI_JSON_PARSER_H__ diff --git a/dali-toolkit/public-api/builder/tree-node.cpp b/dali-toolkit/public-api/builder/tree-node.cpp new file mode 100644 index 0000000..a73236c --- /dev/null +++ b/dali-toolkit/public-api/builder/tree-node.cpp @@ -0,0 +1,197 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include "dali-toolkit/public-api/builder/tree-node.h" +#include "dali-toolkit/internal/builder/tree-node-manipulator.h" + +namespace Dali +{ + +namespace Toolkit +{ + +TreeNode::TreeNode() + : mName(NULL), + mParent(NULL), + mNextSibling(NULL), + mFirstChild(NULL), + mLastChild(NULL), + mStringValue(NULL), + mType(TreeNode::IS_NULL), + mSubstituion(false) +{ +} + +TreeNode::~TreeNode() +{ + +} + +const char* TreeNode::GetName() const +{ + return mName; +} + +TreeNode::NodeType TreeNode::GetType() const +{ + return mType; +} + +const char* TreeNode::GetString() const +{ + return mStringValue; +} + +bool TreeNode::HasSubstitution() const +{ + return mSubstituion; +} + +float TreeNode::GetFloat() const +{ + return mFloatValue; +} + +int TreeNode::GetInteger() const +{ + return mIntValue; +} + +bool TreeNode::GetBoolean() const +{ + return mIntValue == 1 ? true : false; +} + + +size_t TreeNode::Size() const +{ + size_t c = 0; + TreeNode* p = mFirstChild; + while(p) + { + c++; + p = p->mNextSibling; + } + return c; +} + +size_t TreeNode::Count(const std::string& childName) const +{ + const TreeNode* c = GetChild(childName); + if(c) + { + return c->Size(); + } + else + { + return 0; + } +} + +const TreeNode* TreeNode::GetChild(const std::string& childName) const +{ + const TreeNode* p = mFirstChild; + while(p) + { + if(p->mName && (std::string(p->mName) == childName) ) + { + return p; + } + p = p->mNextSibling; + } + return NULL; +} + +const TreeNode* TreeNode::Find(const std::string& childName) const +{ + if(mName && std::string(mName) == childName) + { + return this; + } + else + { + return Internal::FindIt(childName, this); + } +} + +TreeNode::ConstIterator TreeNode::CBegin() const +{ + return ConstIterator(mFirstChild); +} + + +TreeNode::ConstIterator TreeNode::CEnd() const +{ + return ConstIterator(NULL); +} + + +TreeNode::ConstIterator::ConstIterator(TreeNode* v) : mNode(v) +{ + +} + +TreeNode::ConstIterator& TreeNode::ConstIterator::operator ++() +{ + if(mNode) + { + mNode = mNode->mNextSibling; + } + else + { + mNode = NULL; + } + return *this; +} + +TreeNode::ConstIterator TreeNode::ConstIterator::operator ++(int) +{ + TreeNode::ConstIterator ret(mNode); + + if(mNode) + { + mNode = mNode->mNextSibling; + } + else + { + mNode = NULL; + } + return ret; +} + +TreeNode::KeyNodePair TreeNode::ConstIterator::operator *() +{ + return KeyNodePair(mNode->mName, *mNode); +} + +TreeNode::KeyNodePair TreeNode::ConstIterator::operator ->() +{ + return KeyNodePair(mNode->mName, *mNode); +} + +bool TreeNode::ConstIterator::operator!=( const TreeNode::ConstIterator& rhs ) const +{ + return mNode != rhs.mNode; +} + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/builder/tree-node.h b/dali-toolkit/public-api/builder/tree-node.h new file mode 100644 index 0000000..44c0671 --- /dev/null +++ b/dali-toolkit/public-api/builder/tree-node.h @@ -0,0 +1,239 @@ +#ifndef __DALI_SCRIPT_TREE_NODE_H__ +#define __DALI_SCRIPT_TREE_NODE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include // pair +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +class TreeNodeManipulator; + +} // namespace Internal + + +/* + * TreeNode describes a tree of nodes. + * TreeNode does not own its string data which is held by a container eg JsonParser + * Modification operations should be done through a container. + */ +class TreeNode +{ +public: + /* + * enum typedef describing the node type + */ + enum NodeType + { + IS_NULL, + OBJECT, + ARRAY, + STRING, + INTEGER, + FLOAT, + BOOLEAN, + }; + + /* + * Non virtual destructor; this class should not be inherited from. + */ + ~TreeNode(); + + typedef std::pair KeyNodePair; + + /* + * Iterator to iterate through children + */ + class ConstIterator + { + public: + typedef KeyNodePair value_type; + typedef KeyNodePair *pointer; + typedef const KeyNodePair *const_pointer; + typedef KeyNodePair &reference; + typedef const KeyNodePair &const_reference; + typedef size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + /* + * constructor + */ + explicit ConstIterator(TreeNode* v); + + /* + * pre increment + */ + ConstIterator& operator ++(); + + /* + * post increment + */ + ConstIterator operator ++(int); + + /* + * != test + */ + bool operator!=( const ConstIterator& rhs ) const; + + /* + * pointer semantics + */ + KeyNodePair operator*(); + + /* + * pointer semantics + */ + KeyNodePair operator->(); + private: + TreeNode* mNode; + }; + + /* + * Iterate begin() over the nodes children + * @return a const interator + */ + ConstIterator CBegin() const; + + /* + * Iterate end() + * @return a const interator + */ + ConstIterator CEnd() const; + + /* + * Size (number of children) + * @return The number of children + */ + size_t Size() const; + + /* + * Count (the number of children of a sub child node) + * @param childName The name of the child to find + * @return the number of children in the found child + */ + size_t Count(const std::string& childName) const; + + /* + * Get the nodes name + * @return The nodes name + */ + const char* GetName() const; + + /* + * Gets the nodes type + * @return The nodes type + */ + NodeType GetType() const; + + /* + * Gets the nodes string value + * Only valid if the type == TreeNode::STRING + * @return The string value + */ + const char* GetString() const; + + /* + * Gets the nodes float value + * Only valid if the type == TreeNode::FLOAT + * @return The float value + */ + float GetFloat() const; + + /* + * Gets the nodes integer value + * Only valid if the type == TreeNode::INTEGER + * @return The integer value + */ + int GetInteger() const; + + /* + * Gets the nodes boolean value + * Only valid if the type == TreeNode::BOOLEAN + * @return The boolean value + */ + bool GetBoolean() const; + + /* + * Gets the substituion flag + * Only valid if the type == TreeNode::STRING + * @return The substitution flag + */ + bool HasSubstitution() const; + + /* + * Gets a child of the node + * @param name The name of the child + * @return The child if found, else NULL + */ + const TreeNode* GetChild(const std::string& name) const; + + /* + * Recursively search for a child of the node + * @param name The name of the child + * @return The child if found, else NULL + */ + const TreeNode* Find(const std::string& name) const; + +private: + friend class Internal::TreeNodeManipulator; + + /* + * Constructor + */ + TreeNode(); + + // non copyable or assignable + TreeNode(TreeNode &); + TreeNode& operator=(const TreeNode&); + + const char* mName; ///< The nodes name (if any) + + TreeNode* mParent; ///< The nodes parent + TreeNode* mNextSibling; ///< The nodes next sibling + TreeNode* mFirstChild; ///< The nodes first child + TreeNode* mLastChild; ///< The nodes last child + + union + { + const char* mStringValue; ///< The node string value + int mIntValue; ///< The node integer value + float mFloatValue; ///< The node float value + }; + + NodeType mType; ///< The nodes type + bool mSubstituion; ///< String substitution flag + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_SCRIPT_TREE_NODE_H__ diff --git a/dali-toolkit/public-api/controls/alignment/alignment.cpp b/dali-toolkit/public-api/controls/alignment/alignment.cpp new file mode 100644 index 0000000..7c5d827 --- /dev/null +++ b/dali-toolkit/public-api/controls/alignment/alignment.cpp @@ -0,0 +1,109 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +Alignment::Alignment() +{ +} + +Alignment Alignment::New( Type horizontal, Type vertical ) +{ + return Internal::Alignment::New( horizontal, vertical ); +} + +Alignment::Alignment(const Alignment& alignment) +: Control( alignment ) +{ +} + +Alignment::~Alignment() +{ +} + +Alignment Alignment::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void Alignment::SetAlignmentType( Type type ) +{ + GetImpl( *this ).SetAlignmentType( type ); +} + +Alignment::Type Alignment::GetAlignmentType() const +{ + return GetImpl( *this ).GetAlignmentType(); +} + +void Alignment::SetScaling( Scaling scaling ) +{ + GetImpl( *this ).SetScaling( scaling ); +} + +Alignment::Scaling Alignment::GetScaling() const +{ + return GetImpl( *this ).GetScaling(); +} + +void Alignment::SetPadding( const Alignment::Padding& padding ) +{ + GetImpl( *this ).SetPadding( padding ); +} + +const Alignment::Padding& Alignment::GetPadding() const +{ + return GetImpl( *this ).GetPadding(); +} + +Alignment::Alignment( Internal::Alignment& implementation ) +: Control( implementation ) +{ +} + +Alignment& Alignment::operator=(const Alignment& alignment) +{ + if( &alignment != this ) + { + Control::operator=( alignment ); + } + return *this; +} + +Alignment::Alignment( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/bloom-view/bloom-view.cpp b/dali-toolkit/public-api/controls/bloom-view/bloom-view.cpp new file mode 100644 index 0000000..32b6fbc --- /dev/null +++ b/dali-toolkit/public-api/controls/bloom-view/bloom-view.cpp @@ -0,0 +1,131 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +BloomView::BloomView() +{ +} + +BloomView::~BloomView() +{ +} + +BloomView::BloomView(const BloomView& handle) + : Control( handle ) +{ +} + +BloomView& BloomView::operator=(const BloomView& rhs) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +BloomView BloomView::New() +{ + return Internal::BloomView::New(); +} + +BloomView BloomView::New( const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale) +{ + return Internal::BloomView::New( numSamples, blurBellCurveWidth, renderTargetPixelFormat, + downsampleWidthScale, downsampleHeightScale); +} + +BloomView::BloomView( Internal::BloomView& implementation ) +: Control( implementation ) +{ +} + +BloomView::BloomView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +BloomView BloomView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void BloomView::Add(Actor child) +{ + GetImpl(*this).Add(child); +} + +void BloomView::Remove(Actor child) +{ + GetImpl(*this).Remove(child); +} + +void BloomView::Activate() +{ + GetImpl(*this).Activate(); +} + +void BloomView::Deactivate() +{ + GetImpl(*this).Deactivate(); +} + +Property::Index BloomView::GetBloomThresholdPropertyIndex() const +{ + return GetImpl(*this).GetBloomThresholdPropertyIndex(); +} + +Property::Index BloomView::GetBlurStrengthPropertyIndex() const +{ + return GetImpl(*this).GetBlurStrengthPropertyIndex(); +} + +Property::Index BloomView::GetBloomIntensityPropertyIndex() const +{ + return GetImpl(*this).GetBloomIntensityPropertyIndex(); +} + +Property::Index BloomView::GetBloomSaturationPropertyIndex() const +{ + return GetImpl(*this).GetBloomSaturationPropertyIndex(); +} + +Property::Index BloomView::GetImageIntensityPropertyIndex() const +{ + return GetImpl(*this).GetImageIntensityPropertyIndex(); +} + +Property::Index BloomView::GetImageSaturationPropertyIndex() const +{ + return GetImpl(*this).GetImageSaturationPropertyIndex(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/bloom-view/bloom-view.h b/dali-toolkit/public-api/controls/bloom-view/bloom-view.h new file mode 100644 index 0000000..42fe6f0 --- /dev/null +++ b/dali-toolkit/public-api/controls/bloom-view/bloom-view.h @@ -0,0 +1,253 @@ +#ifndef __DALI_TOOLKIT_BLOOM_VIEW_H__ +#define __DALI_TOOLKIT_BLOOM_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * BloomView implementation class + */ +class BloomView; + +} // namespace Internal + +/** + * + * BloomView is a class for applying a render process that intensifies and blurs the bright parts of an image, bleeding bright areas into darker ones and making bright + * light look more realistic. + * + * Basic idea:- + * + * 1) The BloomView object will render all its child actors offscreen.\n + * 2) The BloomView object then extract the parts of that image that are brighter than the bloom threshold.\n + * 3) The BloomView object then blurs the result of step 2), which makes the brightness bleed into surrounding areas.\n + * 3) The BloomView object then composites the bloom from step 3) with the child actors image from step 1), using parameters that can be set by the user. + * The compositing is additive (image + bloom).\n + * 4) The BloomView object gets rendered automatically, either to the screen via the default render task, or via a RenderTask the user has created for + * e.g. further offscreen rendering. + * + * Fundamentally, the BloomView is simply an Actor in the normal actor tree that affects all of its children. It should be added to your Actor tree and manipulated in the + * normal ways. It can be considered a 'portal' in the sense that all child actors are clipped to the BloomView actor bounds. + * + * ************\n + * NB: It is essential to remove the BloomView from the stage and also to call Deactivate() on it when you are not using it. This will ensure that resources are freed and + * rendering stops.\n + * ************\n + * + * + * Usage example:- + * + * // initialise\n + * BloomView bloomView = BloomView::New();\n + * + * // create and add some visible actors to the BloomView, all these child actors will therefore get bloomed\n + * Image image = Image::New(...);\n + * ImageActor imageActor = ImageActor::New(image);\n + * bloomView.Add(imageActor);\n + * ...\n + * + * // Start rendering the BloomView\n + * Stage::GetCurrent().Add(bloomView);\n + * bloomView.Activate();\n + * ...\n + * + * // animate the strength of the bloom - this can fade between no bloom and your desired max bloom. See GetBloomIntensityPropertyIndex().\n + * Animation blurAnimation = Animation::New( ... );\n + * blurAnimation.AnimateTo( Property( bloomView, bloomView.GetBloomIntensityPropertyIndex() ), ... );\n + * blurAnimation.Play();\n + * + * ...\n + * // Stop rendering the BloomView\n + * Stage::GetCurrent().Remove(bloomView);\n + * bloomView.Deactivate();\n + */ +class BloomView : public Control +{ +public: + + /** + * Create an uninitialized BloomView; this can be initialized with BloomView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + BloomView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + BloomView(const BloomView& handle); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + BloomView& operator=(const BloomView& ZoomView); + + /** + * Virtual destructor. + */ + virtual ~BloomView(); + + /** + * Downcast an Object handle to BloomView. If handle points to a BloomView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a BloomView or an uninitialized handle + */ + static BloomView DownCast( BaseHandle handle ); + + /** + * Create an initialized BloomView, using default settings. The default settings are:-\n + * + * numSamples = 5\n + * blurBellCurveWidth = 1.5\n + * renderTargetPixelFormat = RGB888\n + * downsampleWidthScale = 0.5\n + * downsampleHeightScale = 0.5\n + * @return A handle to a newly allocated Dali resource + */ + static BloomView New(); + + /** + * Create an initialized BloomView. + * @param numSamples The size of the Gaussian blur kernel (number of samples in horizontal / vertical blur directions) that is used to blur the bloom + * @param blurBellCurveWidth The constant controlling the Gaussian function, must be > 0.0. Controls the width of the bell curve, i.e. the look of the blur and also indirectly + * the amount of blurriness Smaller numbers for a tighter curve. Useful values in the range [0.5..3.0] - near the bottom of that range the curve is weighted heavily towards + * the centre pixel of the kernel (so there won't be much blur), near the top of that range the pixels have nearly equal weighting (closely approximating a box filter + * therefore). Values close to zero result in the bell curve lying almost entirely within a single pixel, in other words there will be basically no blur as neighbouring pixels + * have close to zero weights. + * @param renderTargetPixelFormat The pixel format of the render targets we are using to perform the bloom. + * @param downsampleWidthScale The width scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @param downsampleHeightScale The height scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @return A handle to a newly allocated Dali resource + */ + static BloomView New(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale); + + /** + * Adds a child Actor to this Actor. + * NOTE! if the child already has a parent, it will be removed from old parent + * and reparented to this actor. This may change childs position, color, shader effect, + * scale etc as it now inherits them from this actor + * @pre This Actor (the parent) has been initialized. + * @pre The child actor has been initialized. + * @pre The child actor is not the same as the parent actor. + * @pre The actor is not the Root actor + * @param [in] child The child. + * @post The child will be referenced by its parent. This means that the child will be kept alive, + * even if the handle passed into this method is reset or destroyed. + * @post This may invalidate ActorContainer iterators. + */ + void Add(Actor child); + + /** + * Removes a child Actor from this Actor. + * If the actor was not a child of this actor, this is a no-op. + * @pre This Actor (the parent) has been initialized. + * @pre The child actor is not the same as the parent actor. + * @param [in] child The child. + * @post This may invalidate ActorContainer iterators. + */ + void Remove(Actor child); + + /** + * Start rendering the BloomView. Must be called after you Add() it to the stage. + */ + void Activate(); + + /** + * Stop rendering the BloomView. Must be called after you Remove() it from the stage. + */ + void Deactivate(); + + /** + * Get the property index that controls the intensity threshold above which the pixels will be bloomed. Useful for animating this property. + * This property represents a value such that pixels brighter than this threshold will be bloomed. Values are normalised, i.e. RGB 0.0 = 0, 1.0 = 255. Default 0.25. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetBloomThresholdPropertyIndex() const; + + /** + * Get the property index that controls the strength of the blur applied to the bloom. Useful for animating this property. + * This property represents a value in the range [0.0 - 1.0] where 0.0 is no blur and 1.0 is full blur. Default 1.0. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetBlurStrengthPropertyIndex() const; + + /** + * Get the property index that controls the intensity of the child actor render texture used during compositing. Useful for animating this property. + * This property represents a multiplier on the intensity of the bloom texture. Default 1.0. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetBloomIntensityPropertyIndex() const; + + /** + * Get the property index that controls the saturation of the child actor render texture used during compositing. Useful for animating this property. + * This property represents a multiplier on the saturation of the bloom texture. Default 1.0. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetBloomSaturationPropertyIndex() const; + + /** + * Get the property index that controls the intensity of the child actor render texture used during compositing. Useful for animating this property. + * This property represents a multiplier on the intensity of the image texture. Default 1.0. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetImageIntensityPropertyIndex() const; + + /** + * Get the property index that controls the saturation of the child actor render texture used during compositing. Useful for animating this property. + * This property represents a multiplier on the saturation of the image texture. Default 1.0. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetImageSaturationPropertyIndex() const; + +public: + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The UI Control implementation. + */ + BloomView( Internal::BloomView& implementation ); + + /** + * Allows the creation of this UI Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + BloomView( Dali::Internal::CustomActor* internal ); + +private: + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_BLOOM_VIEW_H__ diff --git a/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.cpp b/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.cpp new file mode 100644 index 0000000..d6415de --- /dev/null +++ b/dali-toolkit/public-api/controls/bubble-effect/bubble-emitter.cpp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +//INTERNAL INCLUDES +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +BubbleEmitter::BubbleEmitter() +{ +} + +BubbleEmitter::~BubbleEmitter() +{ +} + +BubbleEmitter::BubbleEmitter( Internal::BubbleEmitter& implementation ) +: Control( implementation ) +{ +} + +BubbleEmitter::BubbleEmitter(Dali::Internal::CustomActor* internal) +: Control( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +BubbleEmitter BubbleEmitter::New( const Vector2& winSize, + Image shapeImage, + unsigned int maximumNumberOfBubble, + const Vector2& bubbleSizeRange ) +{ + return Internal::BubbleEmitter::New( winSize, shapeImage, maximumNumberOfBubble, bubbleSizeRange ); +} + +BubbleEmitter::BubbleEmitter( const BubbleEmitter& handle ) +: Control( handle ) +{ +} + +BubbleEmitter& BubbleEmitter::operator=( const BubbleEmitter& rhs ) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +BubbleEmitter BubbleEmitter::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +Actor BubbleEmitter::GetRootActor() +{ + return GetImpl(*this).GetRootActor(); +} + +void BubbleEmitter::SetBackground( Image bgImage, const Vector3& hsvDelta ) +{ + GetImpl(*this).SetBackground( bgImage, hsvDelta ); +} + +void BubbleEmitter::SetShapeImage( Image shapeImage ) +{ + GetImpl(*this).SetShapeImage( shapeImage ); +} + +void BubbleEmitter::SetBubbleScale( float scale ) +{ + GetImpl(*this).SetBubbleScale( scale ); +} + +void BubbleEmitter::SetBubbleDensity( unsigned int density ) +{ + GetImpl(*this).SetBubbleDensity( density ); +} + +void BubbleEmitter::SetBlendMode( bool enable ) +{ + GetImpl(*this).SetBlendMode( enable ); +} + +void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement ) +{ + GetImpl(*this).EmitBubble( animation, emitPosition, direction, displacement ); +} + +void BubbleEmitter::StartExplosion( float duration, float multiple ) +{ + GetImpl(*this).StartExplosion( duration, multiple ); +} + +void BubbleEmitter::Restore() +{ + GetImpl(*this).Restore(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/buttons/button.cpp b/dali-toolkit/public-api/controls/buttons/button.cpp new file mode 100644 index 0000000..aa03116 --- /dev/null +++ b/dali-toolkit/public-api/controls/buttons/button.cpp @@ -0,0 +1,98 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const Button::SIGNAL_CLICKED = "clicked"; +const char* const Button::PROPERTY_DIMMED = "dimmed"; + +Button::Button() +{} + +Button::Button( const Button& button ) +: Control( button ) +{ +} + +Button& Button::operator=( const Button& button ) +{ + if( &button != this ) + { + Control::operator=( button ); + } + return *this; +} + +Button::~Button() +{ +} + +Button Button::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void Button::SetDimmed( bool dimmed ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmed( dimmed ); +} + +bool Button::IsDimmed() const +{ + return Dali::Toolkit::GetImplementation( *this ).IsDimmed(); +} + +void Button::SetAnimationTime( float animationTime ) +{ + Dali::Toolkit::GetImplementation( *this ).SetAnimationTime( animationTime ); +} + +float Button::GetAnimationTime() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetAnimationTime(); +} + +Button::ClickedSignalV2& Button::ClickedSignal() +{ + return Dali::Toolkit::GetImplementation( *this ).ClickedSignal(); +} + +Button::Button( Internal::Button& implementation ) +: Control( implementation ) +{ +} + +Button::Button( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/buttons/check-box-button.cpp b/dali-toolkit/public-api/controls/buttons/check-box-button.cpp new file mode 100644 index 0000000..591a1e4 --- /dev/null +++ b/dali-toolkit/public-api/controls/buttons/check-box-button.cpp @@ -0,0 +1,154 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "check-box-button.h" + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const CheckBoxButton::ACTION_CHECK_BOX_BUTTON_CLICK = "check-box-button-click"; + +const std::string CheckBoxButton::USE_FADE_ANIMATION_PROPERTY_NAME( "use-fade-animation" ); +const std::string CheckBoxButton::USE_CHECK_ANIMATION_PROPERTY_NAME( "use-check-animation" ); + + +CheckBoxButton::CheckBoxButton() +: Button() +{ +} + +CheckBoxButton::CheckBoxButton( const CheckBoxButton& checkBox ) +: Button( checkBox ) +{ +} + +CheckBoxButton& CheckBoxButton::operator=( const CheckBoxButton& checkBox ) +{ + if( &checkBox != this ) + { + Button::operator=( checkBox ); + } + return *this; +} + +CheckBoxButton::~CheckBoxButton() +{ +} + +CheckBoxButton CheckBoxButton::New() +{ + return Internal::CheckBoxButton::New(); +} + +CheckBoxButton CheckBoxButton::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void CheckBoxButton::SetChecked( bool checked ) +{ + Dali::Toolkit::GetImplementation( *this ).SetChecked( checked ); +} + +bool CheckBoxButton::IsChecked() const +{ + return Dali::Toolkit::GetImplementation( *this ).IsChecked(); +} + +void CheckBoxButton::SetBackgroundImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetBackgroundImage( image ); +} + +void CheckBoxButton::SetBackgroundImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetBackgroundImage( image ); +} + +Actor CheckBoxButton::GetBackgroundImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetBackgroundImage(); +} + +void CheckBoxButton::SetCheckedImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetCheckedImage( image ); +} + +void CheckBoxButton::SetCheckedImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetCheckedImage( image ); +} + + +Actor CheckBoxButton::GetCheckedImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetCheckedImage(); +} + +void CheckBoxButton::SetDimmedBackgroundImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedBackgroundImage( image ); +} + +void CheckBoxButton::SetDimmedBackgroundImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedBackgroundImage( image ); +} + +Actor CheckBoxButton::GetDimmedBackgroundImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetDimmedBackgroundImage(); +} + +void CheckBoxButton::SetDimmedCheckedImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedCheckedImage( image ); +} + +void CheckBoxButton::SetDimmedCheckedImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedCheckedImage( image ); +} + +Actor CheckBoxButton::GetDimmedCheckedImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetDimmedCheckedImage(); +} + +CheckBoxButton::CheckBoxButton( Internal::CheckBoxButton& implementation ) +: Button( implementation ) +{ +} + +CheckBoxButton::CheckBoxButton( Dali::Internal::CustomActor* internal ) +: Button( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/buttons/check-box-button.h b/dali-toolkit/public-api/controls/buttons/check-box-button.h new file mode 100644 index 0000000..1a4267d --- /dev/null +++ b/dali-toolkit/public-api/controls/buttons/check-box-button.h @@ -0,0 +1,206 @@ +#ifndef __DALI_TOOLKIT_CHECK_BOX_BUTTON_H__ +#define __DALI_TOOLKIT_CHECK_BOX_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +// Forward declarations + +namespace Internal DALI_INTERNAL +{ +class CheckBoxButton; +} + +/** + * CheckBoxButton provides a check box button which user can check or uncheck. + * + * By default a CheckBoxButton emits a Button::ClickedSignal() signal when the button changes its state to checked or unchecked. + * + * The button's appearance could be modified by setting images or actors with CheckBoxButton::SetBackgroundImage, + * CheckBoxButton::SetCheckedImage, CheckBoxButton::SetDimmedBackgroundImage and CheckBoxButton::SetDimmedCheckedImage. + * + * When the button is not dimmed, if it's not checked it only shows the \e background image. The \e checked image is shown over the + * \e background image when the box is checked (\e background image is not replaced by \e checked image). + * + * When the button is dimmed, \e background image and \e checked image are replaced by \e dimmed images. + * + * CheckBoxButton doesn't have a text. However, a Dali::Toolkit::TableView with a Dali::TextActor or a Dali::Toolkit::TextView + * and a CheckBoxButton could be used instead. + */ +class CheckBoxButton : public Button +{ +public: + //Action Names + static const char* const ACTION_CHECK_BOX_BUTTON_CLICK; + + // Properties + static const std::string USE_FADE_ANIMATION_PROPERTY_NAME; + static const std::string USE_CHECK_ANIMATION_PROPERTY_NAME; + +public: + + /** + * Create an uninitialized CheckBoxButton; this can be initialized with CheckBoxButton::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + CheckBoxButton(); + + /** + * Copy constructor. + */ + CheckBoxButton( const CheckBoxButton& checkBox ); + + /** + * Assignment operator. + */ + CheckBoxButton& operator=( const CheckBoxButton& checkBox ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~CheckBoxButton(); + + /** + * Create an initialized CheckBoxButton. + * @return A handle to a newly allocated Dali resource. + */ + static CheckBoxButton New(); + + /** + * Downcast an Object handle to CheckBoxButton. If handle points to a CheckBoxButton the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a CheckBoxButton or an uninitialized handle + */ + static CheckBoxButton DownCast( BaseHandle handle ); + + /** + * Sets the button as checked or unchecked. + * + * Emits a Button::ClickedSignal() signal if the checkbox is not dimmed and the new state, + * given in the \e checked param, is different than the previous one. + * + * @param[in] checked state. + */ + void SetChecked( bool checked ); + + /** + * @return \e true if the button is checked. + */ + bool IsChecked() const; + + /** + * Sets the background image. + * + * @param[in] image The background image. + */ + void SetBackgroundImage( Image image ); + + /** + * @copydoc SetBackgroundImage( Image image ) + */ + void SetBackgroundImage( Actor image ); + + /** + * Gets the background image. + * @return An actor with the background image. + */ + Actor GetBackgroundImage() const; + + /** + * Sets the checked image. + * + * @param[in] image The checked image. + */ + void SetCheckedImage( Image image ); + + /** + * @copydoc SetCheckedImage( Image image ) + */ + void SetCheckedImage( Actor image ); + + /** + * Gets the checked image. + * @return An actor with the checked image. + */ + Actor GetCheckedImage() const; + + /** + * Sets the dimmed background image. + * + * @param[in] image The dimmed background image. + */ + void SetDimmedBackgroundImage( Image image ); + + /** + * @copydoc SetDimmedBackgroundImage( Image image ) + */ + void SetDimmedBackgroundImage( Actor image ); + + /** + * Gets the dimmed background image. + * @return An actor with the dimmed background image. + */ + Actor GetDimmedBackgroundImage() const; + + /** + * Sets the dimmed checked image. + * + * @param[in] image The dimmed checked image. + */ + void SetDimmedCheckedImage( Image image ); + + /** + * @copydoc SetDimmedCheckedImage( Image image ) + */ + void SetDimmedCheckedImage( Actor image ); + + /** + * Gets the dimmed checked image. + * @return An actor with the dimmed checked image. + */ + Actor GetDimmedCheckedImage() const; + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + CheckBoxButton( Internal::CheckBoxButton& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + CheckBoxButton( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_CHECK_BOX_BUTTON_H__ diff --git a/dali-toolkit/public-api/controls/buttons/push-button.cpp b/dali-toolkit/public-api/controls/buttons/push-button.cpp new file mode 100644 index 0000000..5ca5681 --- /dev/null +++ b/dali-toolkit/public-api/controls/buttons/push-button.cpp @@ -0,0 +1,238 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const PushButton::SIGNAL_TOGGLED = "toggled"; +const char* const PushButton::SIGNAL_PRESSED = "pressed"; +const char* const PushButton::SIGNAL_RELEASED = "released"; + +const char* const PushButton::ACTION_PUSH_BUTTON_CLICK = "push-button-click"; + +PushButton::PushButton() +: Button() +{ +} + +PushButton::PushButton( Internal::PushButton& implementation ) +: Button( implementation ) +{ +} + +PushButton::PushButton( const PushButton& pushButton ) +: Button( pushButton ) +{ +} + +PushButton& PushButton::operator=( const PushButton& pushButton ) +{ + if( &pushButton != this ) + { + Button::operator=( pushButton ); + } + return *this; +} + +PushButton::PushButton( Dali::Internal::CustomActor* internal ) +: Button( internal ) +{ + VerifyCustomActorPointer(internal); +} + +PushButton::~PushButton() +{ +} + +PushButton PushButton::New() +{ + return Internal::PushButton::New(); +} + +PushButton PushButton::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void PushButton::SetAutoRepeating( bool autoRepeating ) +{ + Dali::Toolkit::GetImplementation( *this ).SetAutoRepeating( autoRepeating ); +} + +bool PushButton::IsAutoRepeating() const +{ + return Dali::Toolkit::GetImplementation( *this ).IsAutoRepeating(); +} + +void PushButton::SetInitialAutoRepeatingDelay( float initialAutoRepeatingDelay ) +{ + Dali::Toolkit::GetImplementation( *this ).SetInitialAutoRepeatingDelay( initialAutoRepeatingDelay ); +} + +float PushButton::GetInitialAutoRepeatingDelay() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetInitialAutoRepeatingDelay(); +} + +void PushButton::SetNextAutoRepeatingDelay( float nextAutoRepeatingDelay ) +{ + Dali::Toolkit::GetImplementation( *this ).SetNextAutoRepeatingDelay( nextAutoRepeatingDelay ); +} + +float PushButton::GetNextAutoRepeatingDelay() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetNextAutoRepeatingDelay(); +} + +void PushButton::SetToggleButton( bool toggle ) +{ + Dali::Toolkit::GetImplementation( *this ).SetToggleButton( toggle ); +} + +bool PushButton::IsToggleButton() const +{ + return Dali::Toolkit::GetImplementation( *this ).IsToggleButton(); +} + +void PushButton::SetToggled( bool toggle ) +{ + Dali::Toolkit::GetImplementation( *this ).SetToggled( toggle ); +} + +bool PushButton::IsToggled() const +{ + return Dali::Toolkit::GetImplementation( *this ).IsToggled(); +} + +void PushButton::SetButtonImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetButtonImage( image ); +} + +void PushButton::SetButtonImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetButtonImage( image ); +} + +Actor PushButton::GetButtonImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetButtonImage(); +} + +void PushButton::SetBackgroundImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetBackgroundImage( image ); +} + +void PushButton::SetBackgroundImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetBackgroundImage( image ); +} + +Actor PushButton::GetBackgroundImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetBackgroundImage(); +} + +void PushButton::SetPressedImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetPressedImage( image ); +} + +void PushButton::SetPressedImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetPressedImage( image ); +} + +Actor PushButton::GetPressedImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetPressedImage(); +} + +void PushButton::SetDimmedBackgroundImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedBackgroundImage( image ); +} + +void PushButton::SetDimmedBackgroundImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedBackgroundImage( image ); +} + +Actor PushButton::GetDimmedBackgroundImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetDimmedBackgroundImage(); +} + +void PushButton::SetDimmedImage( Image image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedImage( image ); +} + +void PushButton::SetDimmedImage( Actor image ) +{ + Dali::Toolkit::GetImplementation( *this ).SetDimmedImage( image ); +} + +Actor PushButton::GetDimmedImage() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetDimmedImage(); +} + +void PushButton::SetLabelText( const std::string& text ) +{ + Dali::Toolkit::GetImplementation( *this ).SetLabelText( text ); +} + +void PushButton::SetLabelText( Actor text ) +{ + Dali::Toolkit::GetImplementation( *this ).SetLabelText( text ); +} + +Actor PushButton::GetLabelText() const +{ + return Dali::Toolkit::GetImplementation( *this ).GetLabelText(); +} + +PushButton::ToggledSignalV2& PushButton::ToggledSignal() +{ + return Dali::Toolkit::GetImplementation( *this ).ToggledSignal(); +} + +PushButton::PressedSignalV2& PushButton::PressedSignal() +{ + return Dali::Toolkit::GetImplementation( *this ).PressedSignal(); +} + +PushButton::ReleasedSignalV2& PushButton::ReleasedSignal() +{ + return Dali::Toolkit::GetImplementation( *this ).ReleasedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/cluster/cluster-style.cpp b/dali-toolkit/public-api/controls/cluster/cluster-style.cpp new file mode 100644 index 0000000..aecb12b --- /dev/null +++ b/dali-toolkit/public-api/controls/cluster/cluster-style.cpp @@ -0,0 +1,97 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace Dali +{ + +namespace Toolkit +{ + +// ClusterStyle /////////////////////////////////////////////////////////////// + +const unsigned int ClusterStyle::UNLIMITED_CHILDREN = std::numeric_limits::max(); + +ClusterStyle::ClusterStyle() +{ +} + +ClusterStyle::~ClusterStyle() +{ +} + +ClusterStyle::ClusterStyle(Internal::ClusterStyle* internal) +: BaseHandle(internal) +{ +} + +unsigned int ClusterStyle::GetMaximumNumberOfChildren() const +{ + return GetImpl(*this).GetMaximumNumberOfChildren(); +} + +void ClusterStyle::ApplyStyle(Actor child, unsigned int index, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + GetImpl(*this).ApplyStyle(child, index, alpha, durationSeconds); +} + +void ClusterStyle::ApplyStyleToBackground(Actor background, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + GetImpl(*this).ApplyStyleToBackground(background, alpha, durationSeconds); +} + +void ClusterStyle::ApplyStyleToTitle(Actor title, AlphaFunction alpha, const TimePeriod& durationSeconds) +{ + GetImpl(*this).ApplyStyleToTitle(title, alpha, durationSeconds); +} + +// ClusterStyleStandard /////////////////////////////////////////////////////// + +ClusterStyleStandard ClusterStyleStandard::New(StyleType style) +{ + Internal::ClusterStylePtr internal = Internal::ClusterStyleStandard::New(style); + + return ClusterStyleStandard(internal.Get()); +} + +ClusterStyleStandard::ClusterStyleStandard(Internal::ClusterStyle* internal) +: ClusterStyle(internal) +{ +} + +// ClusterStyleRandom ///////////////////////////////////////////////////////// + +ClusterStyleRandom ClusterStyleRandom::New() +{ + Internal::ClusterStylePtr internal = Internal::ClusterStyleRandom::New(); + + return ClusterStyleRandom(internal.Get()); +} + +ClusterStyleRandom::ClusterStyleRandom(Internal::ClusterStyle* internal) +: ClusterStyle(internal) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/cluster/cluster.cpp b/dali-toolkit/public-api/controls/cluster/cluster.cpp new file mode 100644 index 0000000..c65f24a --- /dev/null +++ b/dali-toolkit/public-api/controls/cluster/cluster.cpp @@ -0,0 +1,176 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Cluster +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string Cluster::CLUSTER_ACTOR_DEPTH( "cluster-actor-depth" ); + +const char* const Cluster::ACTION_EXPAND = "expand"; +const char* const Cluster::ACTION_COLLAPSE = "collapse"; +const char* const Cluster::ACTION_TRANSFORM = "transform"; + +Cluster::Cluster() +{ +} + +Cluster::Cluster(const Cluster& cluster) +: Control(cluster) +{ +} + +Cluster& Cluster::operator =(const Cluster& cluster) +{ + if( &cluster != this ) + { + Control::operator=( cluster ); + } + return *this; +} + +Cluster::~Cluster() +{ +} + +Cluster Cluster::New( ClusterStyle& style ) +{ + return Internal::Cluster::New(style); +} + +Cluster Cluster::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void Cluster::AddChild( Actor child ) +{ + GetImpl(*this).AddChild( child ); +} + +void Cluster::AddChild( Actor child, unsigned int positionIndex ) +{ + GetImpl(*this).AddChild( child, positionIndex); +} + +void Cluster::AddChildAt( Actor child, unsigned int index ) +{ + GetImpl(*this).AddChildAt( child, index ); +} + +void Cluster::AddChildAt( Actor child, unsigned int positionIndex, unsigned int index ) +{ + GetImpl(*this).AddChildAt( child, positionIndex, index ); +} + +Actor Cluster::GetChildAt( unsigned int index ) +{ + return GetImpl(*this).GetChildAt(index); +} + +Actor Cluster::RemoveChildAt( unsigned int index ) +{ + return GetImpl(*this).RemoveChildAt(index); +} + +void Cluster::ExpandChild( unsigned int index ) +{ + GetImpl(*this).ExpandChild(index); +} + +void Cluster::ExpandAllChildren() +{ + GetImpl(*this).ExpandAllChildren(); +} + +void Cluster::CollapseChild( unsigned int index, bool front ) +{ + GetImpl(*this).CollapseChild(index, front); +} + +void Cluster::CollapseAllChildren( bool front ) +{ + GetImpl(*this).CollapseAllChildren( front ); +} + +void Cluster::TransformChild( unsigned int index, const Vector3& position, const Vector3& scale, const Quaternion& rotation, AlphaFunction alpha, const TimePeriod& period ) +{ + GetImpl(*this).TransformChild( index, position, scale, rotation, alpha, period ); +} + +void Cluster::RestoreChild( unsigned int index, AlphaFunction alpha, const TimePeriod& period, bool front ) +{ + GetImpl(*this).RestoreChild( index, alpha, period, front ); +} + +void Cluster::SetBackgroundImage( Actor image ) +{ + GetImpl(*this).SetBackgroundImage(image); +} + +void Cluster::SetTitle( Actor text ) +{ + GetImpl(*this).SetTitle(text); +} + +void Cluster::SetStyle(ClusterStyle style) +{ + GetImpl(*this).SetStyle(style); +} + +ClusterStyle Cluster::GetStyle() const +{ + return GetImpl(*this).GetStyle(); +} + +unsigned int Cluster::GetExpandedCount() const +{ + return GetImpl(*this).GetExpandedCount(); +} + +unsigned int Cluster::GetTotalCount() const +{ + return GetImpl(*this).GetTotalCount(); +} + +Cluster::Cluster( Internal::Cluster& impl ) +: Control( impl ) +{ +} + +Cluster::Cluster( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/cluster/cluster.h b/dali-toolkit/public-api/controls/cluster/cluster.h new file mode 100644 index 0000000..8d6c3e4 --- /dev/null +++ b/dali-toolkit/public-api/controls/cluster/cluster.h @@ -0,0 +1,256 @@ +#ifndef __DALI_TOOLKIT_CLUSTER_H__ +#define __DALI_TOOLKIT_CLUSTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Cluster; +} + +class ClusterStyle; + +/** + * Cluster is a container of grouped actors positioned in different cluster styles. + */ +class Cluster : public Control +{ +public: + + // Custom properties + + static const std::string CLUSTER_ACTOR_DEPTH; ///< Property, name "cluster-actor-depth", type FLOAT + + //Action Names + static const char* const ACTION_EXPAND; + static const char* const ACTION_COLLAPSE; + static const char* const ACTION_TRANSFORM; + +public: + + /** + * Create a Cluster handle; this can be initialised with Cluster::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + Cluster(); + + /** + * Copy Constructor. + */ + Cluster( const Cluster& cluster ); + + /** + * Assignment Operator. + */ + Cluster& operator=( const Cluster& cluster ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Cluster(); + + /** + * Create the Cluster control with the given style. + * @param[in] style The style of the cluster + * @return A handle to the Cluster control. + */ + static Cluster New( ClusterStyle& style ); + + /** + * Downcast an Object handle to Cluster. If handle points to a Cluster the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Cluster or an uninitialized handle + */ + static Cluster DownCast( BaseHandle handle ); + + /** + * Adds a child to the Cluster + * Will automatically choose a position for the child. + * @pre The child actor has been initialized. + * @param[in] child The child to add + */ + void AddChild( Actor child ); + + /** + * Adds a child to the Cluster + * User specifies the position for the child. + * @pre The child actor has been initialized. + * @param[in] child The child to add + * @param[in] positionIndex The position for this child + */ + void AddChild( Actor child, unsigned int positionIndex ); + + /** + * Adds a child to the Cluster to be inserted at a specified + * depth index. + * Will automatically choose a position for the child. + * @pre The child actor has been initialized. + * @param[in] child The child to add + * @param[in] index The depth position for this child + */ + void AddChildAt( Actor child, unsigned int index ); + + /** + * Adds a child to the Cluster to be inserted at a specified + * depth index. + * User specifies the position for the child. + * @pre The child actor has been initialized. + * @param[in] child The child to add + * @param[in] positionIndex The position for this child + * @param[in] index The depth position for this child + */ + void AddChildAt( Actor child, unsigned int positionIndex, unsigned int index ); + + /** + * Returns a child from the given layout position + * Note! if there is no child in this layout position this method returns an uninitialized + * Actor handle + * @param[in] index The child index in the cluster + * @return The child that was in the layout position or an uninitialized handle + */ + Actor GetChildAt( unsigned int index ); + + /** + * Removes a child from the given layout position + * Note! if there is no child in this layout position this method does nothing + * @param[in] index The index of the child to remove + * @return The child that was removed or an uninitialized handle + */ + Actor RemoveChildAt( unsigned int index ); + + /** + * Expands a child + * A child will move away from the cluster. + * @param[in] index The child position index to expand + */ + void ExpandChild( unsigned int index ); + + /** + * Expands all children + * All children that have been collapsed will + * move away from the cluster + */ + void ExpandAllChildren(); + + /** + * Collapses a child + * A child that has been expanded will move + * back to its original positions. + * @param[in] index The child index to collapse + * @param[in] front Whether to move child to the front or + * back of cluster (depth). + */ + void CollapseChild( unsigned int index, bool front = false ); + + /** + * Collapses all children. + * All children that have been expanded will move + * back to their original positions. + * @param[in] front Whether to move child to the front or + * back of cluster (depth). + */ + void CollapseAllChildren( bool front = false ); + + /** + * Transforms Actor from default transform to new transform + * @param[in] index The child index to move + * @param[in] position The position to move to + * @param[in] scale The scale to change to + * @param[in] rotation The rotation to change to + * @param[in] alpha The alpha function to use to tween to this transform + * @param[in] period The duration for this transformation to take + */ + void TransformChild( unsigned int index, const Vector3& position, const Vector3& scale, const Quaternion& rotation, AlphaFunction alpha, const TimePeriod& period ); + + /** + * Restores Actor to the default transform (based on current style) + * @param[in] index The child index to move back + * @param[in] alpha The alpha function to use to tween to this transform + * @param[in] period The duration for this transformation to take + * @param[in] front Whether to move child to the front or + * back of cluster (depth). + */ + void RestoreChild( unsigned int index, AlphaFunction alpha, const TimePeriod& period, bool front = false ); + + /** + * Sets the background image. + * @param[in] image The background image. + */ + void SetBackgroundImage( Actor image ); + + /** + * Sets the title. + * + * @param[in] text Title text. + */ + void SetTitle( Actor text ); + + /** + * Sets the style of the cluster + * @param[in] style The style of the cluster + */ + void SetStyle(ClusterStyle style); + + /** + * Gets the style of the cluster + * @return style of the cluster + */ + ClusterStyle GetStyle() const; + + /** + * Gets the number of children that have been expanded in this cluster. + * @return the number of children expanded. + */ + unsigned int GetExpandedCount() const; + + /** + * Gets the number of children that have been added to this cluster. + * @return the total number of children. + */ + unsigned int GetTotalCount() const; + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Cluster( Internal::Cluster& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Cluster( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_CLUSTER_H__ diff --git a/dali-toolkit/public-api/controls/control-impl.cpp b/dali-toolkit/public-api/controls/control-impl.cpp new file mode 100644 index 0000000..93e0117 --- /dev/null +++ b/dali-toolkit/public-api/controls/control-impl.cpp @@ -0,0 +1,840 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +#include + +#include + +#include "dali-toolkit/internal/controls/style-change-processor.h" +#include "dali-toolkit/internal/controls/relayout-controller.h" +#include "dali-toolkit/internal/controls/relayout-helper.h" +#include "dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h" +#include "dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h" +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +#if defined(DEBUG_ENABLED) +Integration::Log::Filter* gLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_CONTROL"); +#endif + +const float MAX_FLOAT_VALUE( std::numeric_limits::max() ); + +BaseHandle Create() +{ + return ControlImpl::New(); +} + +TypeRegistration CONTROL_TYPE( typeid(Control), typeid(CustomActor), Create ); + +TypeAction ACTION_TYPE_1(CONTROL_TYPE, Toolkit::Control::ACTION_CONTROL_ACTIVATED, &ControlImpl::DoAction); + +/** + * Helper class used to set the Control's size through the Actor's API or through children added. + */ +class SetSizeLock +{ +public: + SetSizeLock( bool& lock ) + : mLock( lock ) + { + mLock = true; + } + + ~SetSizeLock() + { + mLock = false; + } + +private: + bool& mLock; +}; + +/** + * Helper function to calculate a dimension given the policy of that dimension; the minimum & + * maximum values that dimension can be; and the allocated value for that dimension. + * + * @param[in] policy The size policy for that dimension. + * @param[in] minimum The minimum value that dimension can be. + * @param[in] maximum The maximum value that dimension can be. + * @param[in] allocated The value allocated for that dimension. + * + * @return The value that the dimension should be. + * + * @note This does not handle Control::Fixed policy. + */ +float Calculate( Control::SizePolicy policy, float minimum, float maximum, float allocated ) +{ + float size( allocated ); + + switch( policy ) + { + case Control::Fixed: + { + // Use allocated value + break; + } + + case Control::Minimum: + { + // Size is always at least the minimum. + size = std::max( allocated, minimum ); + break; + } + + case Control::Maximum: + { + // Size can grow but up to a maximum value. + size = std::min( allocated, maximum ); + break; + } + + case Control::Range: + { + // Size is at least the minimum and can grow up to the maximum + size = std::max( size, minimum ); + size = std::min( size, maximum ); + break; + } + + case Control::Flexible: + { + // Size grows or shrinks with no limits. + size = allocated; + break; + } + + default: + { + DALI_ASSERT_DEBUG( false && "This function was not intended to be used by any other policy." ); + break; + } + } + + return size; +} + +} // unnamed namespace + +class ControlImpl::Impl : public ConnectionTrackerInterface +{ +public: + // Construction & Destruction + Impl(ControlImpl& controlImpl) + : mControlImpl(controlImpl), + mInitialized( false ), + mPinchGestureDetector(), + mPanGestureDetector(), + mTapGestureDetector(), + mLongPressGestureDetector(), + mStartingPinchScale(), + mLockSetSize( false ), + mWidthPolicy( Control::Fixed ), + mHeightPolicy( Control::Fixed ), + mSize(), + mSetSize(), + mMinimumSize(), + mMaximumSize( MAX_FLOAT_VALUE, MAX_FLOAT_VALUE, MAX_FLOAT_VALUE ), + mIsKeyboardNavigationSupported(false), + mIsKeyboardFocusGroup(false), + mKeyEventSignalV2() + { + } + + ~Impl() + { + // All gesture detectors will be destroyed so no need to disconnect. + } + + // Gesture Detection Methods + + void PinchDetected(Actor actor, PinchGesture pinch) + { + mControlImpl.OnPinch(pinch); + } + + void PanDetected(Actor actor, PanGesture pan) + { + mControlImpl.OnPan(pan); + } + + void TapDetected(Actor actor, TapGesture tap) + { + mControlImpl.OnTap(tap); + } + + void LongPressDetected(Actor actor, LongPressGesture longPress) + { + mControlImpl.OnLongPress(longPress); + } + + /** + * @copydoc ConnectionTrackerInterface::SignalConnected + */ + virtual void SignalConnected( SlotObserver* slotObserver, CallbackBase* callback ) + { + mConnectionTracker.SignalConnected( slotObserver, callback ); + } + + /** + * @copydoc ConnectionTrackerInterface::SignalDisconnected + */ + virtual void SignalDisconnected( SlotObserver* slotObserver, CallbackBase* callback ) + { + mConnectionTracker.SignalDisconnected( slotObserver, callback ); + } + + /** + * @copydoc ConnectionTrackerInterface::GetConnectionCount + */ + virtual std::size_t GetConnectionCount() const + { + return mConnectionTracker.GetConnectionCount(); + } + + // Data + + ControlImpl& mControlImpl; + + bool mInitialized:1; + + ConnectionTracker mConnectionTracker; // signal connection tracker + + // Gesture Detection + + PinchGestureDetector mPinchGestureDetector; + PanGestureDetector mPanGestureDetector; + TapGestureDetector mTapGestureDetector; + LongPressGestureDetector mLongPressGestureDetector; + + Vector3 mStartingPinchScale; ///< The scale when a pinch gesture starts + + // Relayout and size negotiation + + bool mLockSetSize; ///< Used to avoid. Can't be a bitfield as a reference to this member is used in SetSizeLock helper class. + + Control::SizePolicy mWidthPolicy; ///< Stores the width policy. + Control::SizePolicy mHeightPolicy; ///< Stores the height policy. + + Vector3 mSize; ///< Stores the current control's size. + Vector3 mSetSize; ///< Always stores the size set through the Actor's API. Useful when reset to the initial size is needed. + Vector3 mMinimumSize; ///< Stores the control's minimum size. + Vector3 mMaximumSize; ///< Stores the control's maximum size. + + bool mIsKeyboardNavigationSupported; ///< Stores whether keyboard navigation is supported by the control. + bool mIsKeyboardFocusGroup; ///< Stores whether the control is a focus group. + + Toolkit::Control::KeyEventSignalV2 mKeyEventSignalV2; +}; + +Control ControlImpl::New() +{ + // Create the implementation, temporarily owned on stack + IntrusivePtr controlImpl = new ControlImpl( false ); + + // Pass ownership to handle + Control handle( *controlImpl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + controlImpl->Initialize(); + + return handle; +} + +ControlImpl::~ControlImpl() +{ + if( mImpl->mInitialized ) + { + // Unregister only if control has been initialized. + Internal::StyleChangeProcessor::Unregister( this ); + } + delete mImpl; +} + +void ControlImpl::Initialize() +{ + // Register with the style change processor so we are informed when the default style changes + Internal::StyleChangeProcessor::Register( this ); + + // Calling deriving classes + OnInitialize(); + + mImpl->mInitialized = true; +} + +void ControlImpl::EnableGestureDetection(Gesture::Type type) +{ + if ( (type & Gesture::Pinch) && !mImpl->mPinchGestureDetector ) + { + mImpl->mPinchGestureDetector = PinchGestureDetector::New(); + mImpl->mPinchGestureDetector.DetectedSignal().Connect(mImpl, &Impl::PinchDetected); + mImpl->mPinchGestureDetector.Attach(Self()); + } + + if ( (type & Gesture::Pan) && !mImpl->mPanGestureDetector ) + { + mImpl->mPanGestureDetector = PanGestureDetector::New(); + mImpl->mPanGestureDetector.DetectedSignal().Connect(mImpl, &Impl::PanDetected); + mImpl->mPanGestureDetector.Attach(Self()); + } + + if ( (type & Gesture::Tap) && !mImpl->mTapGestureDetector ) + { + mImpl->mTapGestureDetector = TapGestureDetector::New(); + mImpl->mTapGestureDetector.DetectedSignal().Connect(mImpl, &Impl::TapDetected); + mImpl->mTapGestureDetector.Attach(Self()); + } + + if ( (type & Gesture::LongPress) && !mImpl->mLongPressGestureDetector ) + { + mImpl->mLongPressGestureDetector = LongPressGestureDetector::New(); + mImpl->mLongPressGestureDetector.DetectedSignal().Connect(mImpl, &Impl::LongPressDetected); + mImpl->mLongPressGestureDetector.Attach(Self()); + } +} + +void ControlImpl::DisableGestureDetection(Gesture::Type type) +{ + if ( (type & Gesture::Pinch) && mImpl->mPinchGestureDetector ) + { + mImpl->mPinchGestureDetector.Detach(Self()); + mImpl->mPinchGestureDetector.Reset(); + } + + if ( (type & Gesture::Pan) && mImpl->mPanGestureDetector ) + { + mImpl->mPanGestureDetector.Detach(Self()); + mImpl->mPanGestureDetector.Reset(); + } + + if ( (type & Gesture::Tap) && mImpl->mTapGestureDetector ) + { + mImpl->mTapGestureDetector.Detach(Self()); + mImpl->mTapGestureDetector.Reset(); + } + + if ( (type & Gesture::LongPress) && mImpl->mLongPressGestureDetector) + { + mImpl->mLongPressGestureDetector.Detach(Self()); + mImpl->mLongPressGestureDetector.Reset(); + } +} + +PinchGestureDetector ControlImpl::GetPinchGestureDetector() const +{ + return mImpl->mPinchGestureDetector; +} + +PanGestureDetector ControlImpl::GetPanGestureDetector() const +{ + return mImpl->mPanGestureDetector; +} + +TapGestureDetector ControlImpl::GetTapGestureDetector() const +{ + return mImpl->mTapGestureDetector; +} + +LongPressGestureDetector ControlImpl::GetLongPressGestureDetector() const +{ + return mImpl->mLongPressGestureDetector; +} + +void ControlImpl::OnPinch(PinchGesture pinch) +{ + if (pinch.state == Gesture::Started) + { + mImpl->mStartingPinchScale = Self().GetCurrentScale(); + } + + Self().SetScale(mImpl->mStartingPinchScale * pinch.scale); +} + +void ControlImpl::OnStageConnection() +{ + RelayoutRequest(); + + // Notify derived classes. + OnControlStageConnection(); +} + +void ControlImpl::OnStageDisconnection() +{ + // Notify derived classes + OnControlStageDisconnection(); +} + +void ControlImpl::OnChildAdd(Actor& child) +{ + // Request for relayout. + RelayoutRequest(); + + // Notify derived classes. + OnControlChildAdd( child ); +} + +void ControlImpl::OnChildRemove(Actor& child) +{ + // Request for relayout. + RelayoutRequest(); + + // Notify derived classes. + OnControlChildRemove( child ); +} + +void ControlImpl::OnSizeSet(const Vector3& targetSize) +{ + if( ( !mImpl->mLockSetSize ) && ( targetSize != mImpl->mSetSize ) ) + { + // Only updates size if set through Actor's API + mImpl->mSetSize = targetSize; + } + + if( targetSize != mImpl->mSize ) + { + // Update control size. + mImpl->mSize = targetSize; + + // Notify derived classes. + OnControlSizeSet( targetSize ); + } +} + +void ControlImpl::OnSizeAnimation(Animation& animation, const Vector3& targetSize) +{ + // Do Nothing +} + +bool ControlImpl::OnTouchEvent(const TouchEvent& event) +{ + return false; // Do not consume +} + +bool ControlImpl::OnKeyEvent(const KeyEvent& event) +{ + return false; // Do not consume +} + +bool ControlImpl::OnMouseWheelEvent(const MouseWheelEvent& event) +{ + return false; // Do not consume +} + +void ControlImpl::OnKeyInputFocusGained() +{ + // Do Nothing +} + +void ControlImpl::OnKeyInputFocusLost() +{ + // Do Nothing +} + +Actor ControlImpl::GetChildByAlias(const std::string& actorAlias) +{ + return Actor(); +} + +bool ControlImpl::OnAccessibilityPan(PanGesture gesture) +{ + return false; // Accessibility pan gesture is not handled by default +} + +bool ControlImpl::OnAccessibilityValueChange(bool isIncrease) +{ + return false; // Accessibility value change action is not handled by default +} + + +void ControlImpl::SetKeyboardNavigationSupport(bool isSupported) +{ + mImpl->mIsKeyboardNavigationSupported = isSupported; +} + +bool ControlImpl::IsKeyboardNavigationSupported() +{ + return mImpl->mIsKeyboardNavigationSupported; +} + +void ControlImpl::SetAsKeyboardFocusGroup(bool isFocusGroup) +{ + mImpl->mIsKeyboardFocusGroup = isFocusGroup; + + // The following line will be removed when the deprecated API in KeyboardFocusManager is deleted + KeyboardFocusManager::Get().SetAsFocusGroup(Self(), isFocusGroup); +} + +bool ControlImpl::IsKeyboardFocusGroup() +{ + return KeyboardFocusManager::Get().IsFocusGroup(Self()); +} + +Actor ControlImpl::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + return Actor(); +} + +bool ControlImpl::DoAction(BaseObject* object, const std::string& actionName, const std::vector& attributes) +{ + bool ret = false; + + return ret; +} + +void ControlImpl::DoActivatedAction(const PropertyValueContainer& attributes) +{ + OnActivated(); +} + +Toolkit::Control::KeyEventSignalV2& ControlImpl::KeyEventSignal() +{ + return mImpl->mKeyEventSignalV2; +} + +void ControlImpl::SetSizePolicy( Control::SizePolicy widthPolicy, Control::SizePolicy heightPolicy ) +{ + bool relayoutRequest( false ); + + if ( ( mImpl->mWidthPolicy != widthPolicy ) || ( mImpl->mHeightPolicy != heightPolicy ) ) + { + relayoutRequest = true; + } + + mImpl->mWidthPolicy = widthPolicy; + mImpl->mHeightPolicy = heightPolicy; + + // Ensure RelayoutRequest is called AFTER new policies have been set. + if ( relayoutRequest ) + { + RelayoutRequest(); + } +} + +void ControlImpl::GetSizePolicy( Control::SizePolicy& widthPolicy, Control::SizePolicy& heightPolicy ) const +{ + widthPolicy = mImpl->mWidthPolicy; + heightPolicy = mImpl->mHeightPolicy; +} + +void ControlImpl::SetMinimumSize( const Vector3& size ) +{ + if ( mImpl->mMinimumSize != size ) + { + mImpl->mMinimumSize = size; + + // Only relayout if our control is using the minimum or range policy. + if ( ( mImpl->mHeightPolicy == Control::Minimum ) || ( mImpl->mWidthPolicy == Control::Minimum ) || + ( mImpl->mHeightPolicy == Control::Range ) || ( mImpl->mWidthPolicy == Control::Range ) ) + { + RelayoutRequest(); + } + } +} + +const Vector3& ControlImpl::GetMinimumSize() const +{ + return mImpl->mMinimumSize; +} + +void ControlImpl::SetMaximumSize( const Vector3& size ) +{ + if ( mImpl->mMaximumSize != size ) + { + mImpl->mMaximumSize = size; + + // Only relayout if our control is using the maximum or range policy. + if ( ( mImpl->mHeightPolicy == Control::Maximum ) || ( mImpl->mWidthPolicy == Control::Maximum ) || + ( mImpl->mHeightPolicy == Control::Range ) || ( mImpl->mWidthPolicy == Control::Range ) ) + { + RelayoutRequest(); + } + } +} + +const Vector3& ControlImpl::GetMaximumSize() const +{ + return mImpl->mMaximumSize; +} + +Vector3 ControlImpl::GetNaturalSize() +{ + // could be overridden in derived classes. + return mImpl->mSetSize; +} + +float ControlImpl::GetHeightForWidth( float width ) +{ + // could be overridden in derived classes. + float height( 0.0f ); + if ( mImpl->mSetSize.width > 0.0f ) + { + height = mImpl->mSetSize.height * width / mImpl->mSetSize.width; + } + return height; +} + +float ControlImpl::GetWidthForHeight( float height ) +{ + // could be overridden in derived classes. + float width( 0.0f ); + if ( mImpl->mSetSize.height > 0.0f ) + { + width = mImpl->mSetSize.width * height / mImpl->mSetSize.height; + } + return width; +} + +const Vector3& ControlImpl::GetControlSize() const +{ + return mImpl->mSize; +} + +const Vector3& ControlImpl::GetSizeSet() const +{ + return mImpl->mSetSize; +} + +void ControlImpl::SetKeyInputFocus() +{ + if( Self().OnStage() ) + { + KeyInputFocusManager::Get().SetFocus(Control::DownCast(Self())); + } +} + +bool ControlImpl::HasKeyInputFocus() +{ + bool result = false; + if( Self().OnStage() ) + { + result = KeyInputFocusManager::Get().IsKeyboardListener(Control::DownCast(Self())); + } + return result; +} + +void ControlImpl::ClearKeyInputFocus() +{ + if( Self().OnStage() ) + { + KeyInputFocusManager::Get().RemoveFocus(Control::DownCast(Self())); + } +} + +void ControlImpl::RelayoutRequest() +{ + Internal::RelayoutController::Get().Request(); +} + +void ControlImpl::Relayout( Vector2 size, ActorSizeContainer& container ) +{ + // Avoids relayout again when OnSizeSet callback arrives. + { + SetSizeLock lock( mImpl->mLockSetSize ); + Self().SetSize( size ); + } + + // Only relayout controls which requested to be relaid out. + OnRelaidOut( size, container ); +} + +void ControlImpl::Relayout( Actor actor, Vector2 size, ActorSizeContainer& container ) +{ + if ( actor ) + { + Control control( Control::DownCast( actor ) ); + if( control ) + { + control.GetImplementation().NegotiateSize( size, container ); + } + else + { + container.push_back( ActorSizePair( actor, size ) ); + } + } +} + +void ControlImpl::OnRelaidOut( Vector2 size, ActorSizeContainer& container ) +{ + unsigned int numChildren = Self().GetChildCount(); + + for( unsigned int i=0; imWidthPolicy == Control::Fixed ) + { + if ( mImpl->mHeightPolicy == Control::Fixed ) + { + // If a control says it has a fixed size, then use the size set by the application / control. + Vector2 setSize( mImpl->mSetSize ); + if ( setSize != Vector2::ZERO ) + { + size = setSize; + + // Policy is set to Fixed, so if the application / control has not set one of the dimensions, + // then we should use the natural size of the control rather than the full allocation. + if ( EqualsZero( size.width ) ) + { + size.width = GetWidthForHeight( size.height ); + } + else if ( EqualsZero( size.height ) ) + { + size.height = GetHeightForWidth( size.width ); + } + } + else + { + // If that is not set then set the size to the control's natural size + size = Vector2( GetNaturalSize() ); + } + } + else + { + // Width is fixed so if the application / control has set it, then use that. + if ( !EqualsZero( mImpl->mSetSize.width ) ) + { + size.width = mImpl->mSetSize.width; + } + else + { + // Otherwise, set the width to what has been allocated. + size.width = allocatedSize.width; + } + + // Height is flexible so ask control what the height should be for our width. + size.height = GetHeightForWidth( size.width ); + + // Ensure height is within our policy rules + size.height = Calculate( mImpl->mHeightPolicy, mImpl->mMinimumSize.height, mImpl->mMaximumSize.height, size.height ); + } + } + else + { + if ( mImpl->mHeightPolicy == Control::Fixed ) + { + // Height is fixed so if the application / control has set it, then use that. + if ( !EqualsZero( mImpl->mSetSize.height ) ) + { + size.height = mImpl->mSetSize.height; + } + else + { + // Otherwise, set the height to what has been allocated. + size.height = allocatedSize.height; + } + + // Width is flexible so ask control what the width should be for our height. + size.width = GetWidthForHeight( size.height ); + + // Ensure width is within our policy rules + size.width = Calculate( mImpl->mWidthPolicy, mImpl->mMinimumSize.width, mImpl->mMaximumSize.width, size.width ); + } + else + { + // Width and height are BOTH flexible. + // Calculate the width and height using the policy rules. + size.width = Calculate( mImpl->mWidthPolicy, mImpl->mMinimumSize.width, mImpl->mMaximumSize.width, allocatedSize.width ); + size.height = Calculate( mImpl->mHeightPolicy, mImpl->mMinimumSize.height, mImpl->mMaximumSize.height, allocatedSize.height ); + } + } + + // If the width has not been set, then set to the allocated width. + // Also if the width set is greater than the allocated, then set to allocated (no exceed support). + if ( EqualsZero( size.width ) || ( size.width > allocatedSize.width ) ) + { + size.width = allocatedSize.width; + } + + // If the height has not been set, then set to the allocated height. + // Also if the height set is greater than the allocated, then set to allocated (no exceed support). + if ( EqualsZero( size.height ) || ( size.height > allocatedSize.height ) ) + { + size.height = allocatedSize.height; + } + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, + "%p: Natural: [%.2f, %.2f] Allocated: [%.2f, %.2f] Set: [%.2f, %.2f]\n", + Self().GetObjectPtr(), + GetNaturalSize().x, GetNaturalSize().y, + allocatedSize.x, allocatedSize.y, + size.x, size.y ); + + Relayout( size, container ); +} + +bool ControlImpl::EmitKeyEventSignal( const KeyEvent& event ) +{ + // Guard against destruction during signal emission + Dali::Toolkit::Control handle( GetOwner() ); + + bool consumed = false; + + // signals are allocated dynamically when someone connects + if ( !mImpl->mKeyEventSignalV2.Empty() ) + { + consumed = mImpl->mKeyEventSignalV2.Emit( handle, event ); + } + + if (!consumed) + { + // Notification for derived classes + consumed = OnKeyEvent(event); + } + + return consumed; +} + +void ControlImpl::SignalConnected( SlotObserver* slotObserver, CallbackBase* callback ) +{ + mImpl->SignalConnected( slotObserver, callback ); +} + +void ControlImpl::SignalDisconnected( SlotObserver* slotObserver, CallbackBase* callback ) +{ + mImpl->SignalDisconnected( slotObserver, callback ); +} + +std::size_t ControlImpl::GetConnectionCount() const +{ + return mImpl->GetConnectionCount(); +} + +ControlImpl::ControlImpl( bool requiresTouchEvents ) +: CustomActorImpl( requiresTouchEvents ), + mImpl(new Impl(*this)) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/control.cpp b/dali-toolkit/public-api/controls/control.cpp new file mode 100644 index 0000000..2022ce5 --- /dev/null +++ b/dali-toolkit/public-api/controls/control.cpp @@ -0,0 +1,174 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const Control::ACTION_CONTROL_ACTIVATED = "control-activated"; +const char* const Control::SIGNAL_KEY_EVENT = "key-event"; + +Control Control::New() +{ + return ControlImpl::New(); +} + +Control::Control() +{ +} + +Control::Control(const Control& uiControl) +: CustomActor( uiControl ? static_cast( uiControl.GetImplementation() ).GetOwner() : NULL) +{ +} + +Control::~Control() +{ +} + +Control& Control::operator=( const Control& handle ) +{ + if( &handle != this ) + { + CustomActor::operator=( handle ); + } + return *this; +} + +Control Control::DownCast( BaseHandle handle ) +{ + return DownCast(handle); +} + +ControlImpl& Control::GetImplementation() +{ + return static_cast(CustomActor::GetImplementation()); +} + +const ControlImpl& Control::GetImplementation() const +{ + return static_cast(CustomActor::GetImplementation()); +} + +void Control::SetSizePolicy( SizePolicy widthPolicy, SizePolicy heightPolicy ) +{ + GetImplementation().SetSizePolicy( widthPolicy, heightPolicy ); +} + +void Control::GetSizePolicy( SizePolicy& widthPolicy, SizePolicy& heightPolicy ) const +{ + GetImplementation().GetSizePolicy( widthPolicy, heightPolicy ); +} + +void Control::SetMinimumSize( const Vector3& size ) +{ + GetImplementation().SetMinimumSize( size ); +} + +const Vector3& Control::GetMinimumSize() const +{ + return GetImplementation().GetMinimumSize(); +} + +void Control::SetMaximumSize( const Vector3& size ) +{ + GetImplementation().SetMaximumSize( size ); +} + +const Vector3& Control::GetMaximumSize() const +{ + return GetImplementation().GetMaximumSize(); +} + +Vector3 Control::GetNaturalSize() +{ + return GetImplementation().GetNaturalSize(); +} + +float Control::GetHeightForWidth( float width ) +{ + return GetImplementation().GetHeightForWidth( width ); +} + +float Control::GetWidthForHeight( float height ) +{ + return GetImplementation().GetWidthForHeight( height ); +} + +void Control::SetKeyInputFocus() +{ + GetImplementation().SetKeyInputFocus(); +} + +bool Control::HasKeyInputFocus() +{ + return GetImplementation().HasKeyInputFocus(); +} + +void Control::ClearKeyInputFocus() +{ + GetImplementation().ClearKeyInputFocus(); +} + +Control::KeyEventSignalV2& Control::KeyEventSignal() +{ + return GetImplementation().KeyEventSignal(); +} + +/** + * @copydoc ConnectionTrackerInterface::SignalConnected + */ +void Control::SignalConnected( SlotObserver* slotObserver, CallbackBase* callback ) +{ + GetImplementation().SignalConnected(slotObserver, callback ); +} + +/** + * @copydoc ConnectionTrackerInterface::SignalDisconnected + */ +void Control::SignalDisconnected( SlotObserver* slotObserver, CallbackBase* callback ) +{ + GetImplementation().SignalDisconnected(slotObserver, callback ); +} + +/** + * @copydoc ConnectionTrackerInterface::GetConnectionCount + */ +std::size_t Control::GetConnectionCount() const +{ + return GetImplementation().GetConnectionCount( ); +} + + +Control::Control(ControlImpl& implementation) +: CustomActor(implementation) +{ +} + +Control::Control(Dali::Internal::CustomActor* internal) +: CustomActor(internal) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/default-controls/check-button-factory.cpp b/dali-toolkit/public-api/controls/default-controls/check-button-factory.cpp new file mode 100644 index 0000000..97cd22c --- /dev/null +++ b/dali-toolkit/public-api/controls/default-controls/check-button-factory.cpp @@ -0,0 +1,144 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "check-button-factory.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +Alignment CreateAlignedImage( Actor image ) +{ + Alignment alignmentContainer = Toolkit::Alignment::New(); + alignmentContainer.SetScaling( Toolkit::Alignment::ShrinkToFitKeepAspect ); + + alignmentContainer.Add( image ); + + return alignmentContainer; +} + +Alignment CreateAlignedImage( const std::string& imagePath ) +{ + Image image = Image::New( imagePath ); + + return CreateAlignedImage( ImageActor::New( image ) ); +} + +} // namespace + + +CheckBoxButton CreateCheckBoxButton( const std::string& backgroundImagePath, const std::string& checkedImagePath, const std::string& dimmedBackgroundImagePath, const std::string& dimmedCheckedImagePath ) +{ + CheckBoxButton button = Toolkit::CheckBoxButton::New(); + + if( !backgroundImagePath.empty() ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImagePath ) ); + } + + if( !checkedImagePath.empty() ) + { + button.SetCheckedImage( CreateAlignedImage( checkedImagePath ) ); + } + + if( !dimmedBackgroundImagePath.empty() ) + { + button.SetDimmedBackgroundImage( CreateAlignedImage( dimmedBackgroundImagePath ) ); + } + + if( !dimmedCheckedImagePath.empty() ) + { + button.SetDimmedCheckedImage( CreateAlignedImage( dimmedCheckedImagePath ) ); + } + + return button; +} + +CheckBoxButton CreateCheckBoxButton( Actor backgroundImageActor, Actor checkedImageActor, Actor dimmedBackgroundImageActor, Actor dimmedCheckedImagActor ) +{ + CheckBoxButton button = Toolkit::CheckBoxButton::New(); + + if( backgroundImageActor ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImageActor ) ); + } + + if( checkedImageActor ) + { + button.SetCheckedImage( CreateAlignedImage( checkedImageActor ) ); + } + + if( dimmedBackgroundImageActor ) + { + button.SetDimmedBackgroundImage( CreateAlignedImage( dimmedBackgroundImageActor ) ); + } + + if( dimmedCheckedImagActor ) + { + button.SetDimmedCheckedImage( CreateAlignedImage( dimmedCheckedImagActor ) ); + } + + return button; +} + +CheckBoxButton CreateCheckBoxButton( const std::string& backgroundImagePath, const std::string& checkedImagePath ) +{ + CheckBoxButton button = Toolkit::CheckBoxButton::New(); + + if( !backgroundImagePath.empty() ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImagePath ) ); + } + + if( !checkedImagePath.empty() ) + { + button.SetCheckedImage( CreateAlignedImage( checkedImagePath ) ); + } + + return button; +} + +CheckBoxButton CreateCheckBoxButton( Actor backgroundImageActor, Actor checkedImageActor ) +{ + CheckBoxButton button = Toolkit::CheckBoxButton::New(); + + if( backgroundImageActor ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImageActor ) ); + } + + if( checkedImageActor ) + { + button.SetCheckedImage( CreateAlignedImage( checkedImageActor ) ); + } + + return button; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/default-controls/check-button-factory.h b/dali-toolkit/public-api/controls/default-controls/check-button-factory.h new file mode 100644 index 0000000..2a30a3a --- /dev/null +++ b/dali-toolkit/public-api/controls/default-controls/check-button-factory.h @@ -0,0 +1,79 @@ +#ifndef __DALI_TOOLKIT_CHECK_BUTTON_FACTORY_H__ +#define __DALI_TOOLKIT_CHECK_BUTTON_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * Creates a check box button with the given images. + * Images will be shrunk to fit the button size keeping their aspect ratio. + * @note Images won't be scaled to fill the whole button size. + * @note If an image path is empty, this image is not set to the button. + * + * @param[in] backgroundImagePath Image path to be shown as button background. + * @param[in] checkedImagePath Image path to be shown as checked button. + * @param[in] dimmedBackgroundImagePath Image path to be shown as button dimmed background. + * @param[in] dimmedCheckedImagePath Image path to be shown as dimmed checked button. + */ +CheckBoxButton CreateCheckBoxButton( const std::string& backgroundImagePath, const std::string& checkedImagePath, const std::string& dimmedBackgroundImagePath, const std::string& dimmedCheckedImagePath ); + +/** + * Creates a check box button with the given images. + * Images will be shrunk to fit the button size keeping their aspect ratio. + * @note Images won't be scaled to fill the whole button size. + * @note If an image is an empty handle, this image is not set to the button. + * + * @param[in] backgroundImageActor Image to be shown as button background. + * @param[in] checkedImageActor Image to be shown as checked button. + * @param[in] dimmedBackgroundImageActor Image to be shown as button dimmed background. + * @param[in] dimmedCheckedImagActor Image to be shown as dimmed checked button. + */ +CheckBoxButton CreateCheckBoxButton( Actor backgroundImageActor, Actor checkedImageActor, Actor dimmedBackgroundImageActor, Actor dimmedCheckedImagActor ); + +/** + * Creates a check box button with the given background and checked images. + * Background and checked images will be shrunk to fit the button size keeping their aspect ratio. + * @note Background and checked images won't be scaled to fill the whole button size. + * + * @param[in] backgroundImagePath Image path to be shown as button background. + * @param[in] checkedImagePath Image path to be shown as checked button. + */ +CheckBoxButton CreateCheckBoxButton( const std::string& backgroundImagePath, const std::string& checkedImagePath ); + +/** + * Creates a check box button with the given background and checked images. + * Background and checked images will be shrunk to fit the button size keeping their aspect ratio. + * @note Background and checked images won't be scaled to fill the whole button size. + * + * @param[in] backgroundImageActor Image to be shown as button background. + * @param[in] checkedImageActor Image to be shown as checked button. + */ +CheckBoxButton CreateCheckBoxButton( Actor backgroundImageActor, Actor checkedImageActor ); +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_CHECK_BUTTON_FACTORY_H__ diff --git a/dali-toolkit/public-api/controls/default-controls/push-button-factory.cpp b/dali-toolkit/public-api/controls/default-controls/push-button-factory.cpp new file mode 100644 index 0000000..bbc21dc --- /dev/null +++ b/dali-toolkit/public-api/controls/default-controls/push-button-factory.cpp @@ -0,0 +1,145 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +// EXTERNAL INCLUDES +// INTERNAL INCLUDES + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +Alignment CreateAlignedImage( Actor image ) +{ + Alignment alignmentContainer = Toolkit::Alignment::New(); + alignmentContainer.SetScaling( Toolkit::Alignment::ShrinkToFitKeepAspect ); + + alignmentContainer.Add( image ); + + return alignmentContainer; +} + +Alignment CreateAlignedImage( const std::string& imagePath ) +{ + Image image = Image::New( imagePath ); + + return CreateAlignedImage( ImageActor::New( image ) ); +} + +} // namespace + + +PushButton CreatePushButton( const std::string& releasedImagePath, const std::string& pressedImagePath, const std::string& backgroundImagePath, + const std::string& dimmedReleasedImagePath, const std::string& dimmedBackgroundImagePath ) +{ + PushButton button = Toolkit::PushButton::New(); + + if( !releasedImagePath.empty() ) + { + button.SetButtonImage( CreateAlignedImage( releasedImagePath ) ); + } + + if( !pressedImagePath.empty() ) + { + button.SetPressedImage( CreateAlignedImage( pressedImagePath ) ); + } + + if( !backgroundImagePath.empty() ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImagePath ) ); + } + + if( !dimmedReleasedImagePath.empty() ) + { + button.SetDimmedImage( CreateAlignedImage( dimmedReleasedImagePath ) ); + } + + if( !dimmedBackgroundImagePath.empty() ) + { + button.SetDimmedBackgroundImage( CreateAlignedImage( dimmedBackgroundImagePath ) ); + } + + return button; +} + +PushButton CreatePushButton( Actor releasedImageActor, Actor pressedImageActor, Actor backgroundImageActor, + Actor dimmedReleasedImageActor, Actor dimmedBackgroundImageActor ) +{ + PushButton button = Toolkit::PushButton::New(); + + if( releasedImageActor ) + { + button.SetButtonImage( CreateAlignedImage( releasedImageActor ) ); + } + + if( pressedImageActor ) + { + button.SetPressedImage( CreateAlignedImage( pressedImageActor ) ); + } + + if( backgroundImageActor ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImageActor ) ); + } + + if( dimmedReleasedImageActor ) + { + button.SetDimmedImage( CreateAlignedImage( dimmedReleasedImageActor ) ); + } + + if( dimmedBackgroundImageActor ) + { + button.SetDimmedBackgroundImage( CreateAlignedImage( dimmedBackgroundImageActor ) ); + } + + return button; +} + +PushButton CreatePushButton( const std::string& backgroundImagePath ) +{ + PushButton button = Toolkit::PushButton::New(); + + if( !backgroundImagePath.empty() ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImagePath ) ); + } + + return button; +} + +PushButton CreatePushButton( Actor backgroundImageActor ) +{ + PushButton button = Toolkit::PushButton::New(); + + if( backgroundImageActor ) + { + button.SetBackgroundImage( CreateAlignedImage( backgroundImageActor ) ); + } + + return button; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/default-controls/solid-color-actor.cpp b/dali-toolkit/public-api/controls/default-controls/solid-color-actor.cpp new file mode 100644 index 0000000..beda262 --- /dev/null +++ b/dali-toolkit/public-api/controls/default-controls/solid-color-actor.cpp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ +const unsigned int MAX_BORDER_SIZE( 9 ); +} + +ImageActor CreateSolidColorActor( const Vector4& color, bool border, const Vector4& borderColor, const unsigned int borderSize ) +{ + ImageActor image; + if( borderSize > MAX_BORDER_SIZE ) + { + return image; + } + + const unsigned int bitmapWidth = borderSize * 2 + 2; + BitmapImage imageData = BitmapImage::New( bitmapWidth, bitmapWidth, Pixel::RGBA8888 ); + + // Create the image + PixelBuffer* pixbuf = imageData.GetBuffer(); + Vector4 outerColor = color; + if ( border ) + { + outerColor = borderColor; + } + + // Using a (2 + border) x (2 + border) image gives a better blend with the GL implementation + // than a (1 + border) x (1 + border) image + const unsigned int bitmapSize = bitmapWidth * bitmapWidth; + const unsigned int topLeft = bitmapWidth * borderSize + borderSize; + const unsigned int topRight = topLeft + 1; + const unsigned int bottomLeft = bitmapWidth * (borderSize + 1) + borderSize; + const unsigned int bottomRight = bottomLeft + 1; + + for( size_t i = 0; i < bitmapSize; ++i ) + { + if( i == topLeft || + i == topRight || + i == bottomLeft || + i == bottomRight ) + { + pixbuf[i*4+0] = 0xFF * color.r; + pixbuf[i*4+1] = 0xFF * color.g; + pixbuf[i*4+2] = 0xFF * color.b; + pixbuf[i*4+3] = 0xFF * color.a; + } + else + { + pixbuf[i*4+0] = 0xFF * outerColor.r; + pixbuf[i*4+1] = 0xFF * outerColor.g; + pixbuf[i*4+2] = 0xFF * outerColor.b; + pixbuf[i*4+3] = 0xFF * outerColor.a; + } + } + + imageData.Update(); + image = ImageActor::New( imageData ); + image.SetAnchorPoint( AnchorPoint::CENTER ); + image.SetParentOrigin( ParentOrigin::CENTER ); + + if( border ) + { + image.SetStyle( ImageActor::STYLE_NINE_PATCH ); + image.SetNinePatchBorder( Vector4::ONE * (float)borderSize * 2.0f ); + } + + return image; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/effects-view/effects-view.cpp b/dali-toolkit/public-api/controls/effects-view/effects-view.cpp new file mode 100644 index 0000000..a9a8062 --- /dev/null +++ b/dali-toolkit/public-api/controls/effects-view/effects-view.cpp @@ -0,0 +1,153 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +EffectsView EffectsView::New() +{ + return Internal::EffectsView::New(); +} + +EffectsView::EffectsView() +{ +} + +EffectsView::EffectsView( const EffectsView& handle ) +: Control( handle ) +{ +} + +EffectsView& EffectsView::operator=( const EffectsView& rhs ) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +EffectsView EffectsView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +EffectsView::~EffectsView() +{ +} + +void EffectsView::SetType( EffectsView::EffectType type ) +{ + GetImpl(*this).SetType( type ); +} + +EffectsView::EffectType EffectsView::GetType() const +{ + return GetImpl(*this).GetType(); +} + +void EffectsView::Enable() +{ + GetImpl(*this).Enable(); +} + +void EffectsView::Disable() +{ + GetImpl(*this).Disable(); +} + +void EffectsView::Refresh() +{ + GetImpl(*this).Refresh(); +} + +void EffectsView::SetRefreshOnDemand( bool onDemand ) +{ + GetImpl(*this).SetRefreshOnDemand( onDemand ); +} + +void EffectsView::SetPixelFormat( Pixel::Format pixelFormat ) +{ + GetImpl(*this).SetPixelFormat( pixelFormat ); +} + +void EffectsView::SetOutputImage( FrameBufferImage image ) +{ + GetImpl(*this).SetOutputImage( image ); +} + +FrameBufferImage EffectsView::GetOutputImage() +{ + return GetImpl(*this).GetOutputImage(); +} + +Property::Index EffectsView::GetEffectSizePropertyIndex() const +{ + return GetImpl(*this).GetEffectSizePropertyIndex(); +} + +Property::Index EffectsView::GetEffectStrengthPropertyIndex() const +{ + return GetImpl(*this).GetEffectStrengthPropertyIndex(); +} + +Property::Index EffectsView::GetEffectOffsetPropertyIndex() const +{ + return GetImpl(*this).GetEffectOffsetPropertyIndex(); +} + +Property::Index EffectsView::GetEffectColorPropertyIndex() const +{ + return GetImpl(*this).GetEffectColorPropertyIndex(); +} + +void EffectsView::SetBackgroundColor( const Vector4& color ) +{ + GetImpl(*this).SetBackgroundColor(color); +} + +Vector4 EffectsView::GetBackgroundColor() const +{ + return GetImpl(*this).GetBackgroundColor(); +} + + +EffectsView::EffectsView( Internal::EffectsView& implementation ) +: Control( implementation ) +{ +} + +EffectsView::EffectsView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + + +} //namespace Toolkit + +} //namespace Dali diff --git a/dali-toolkit/public-api/controls/effects-view/effects-view.h b/dali-toolkit/public-api/controls/effects-view/effects-view.h new file mode 100644 index 0000000..ca3d5e4 --- /dev/null +++ b/dali-toolkit/public-api/controls/effects-view/effects-view.h @@ -0,0 +1,227 @@ +#ifndef __DALI_TOOLKIT_EFFECTS_VIEW_H__ +#define __DALI_TOOLKIT_EFFECTS_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +class EffectsView; + +} // namespace Internal + +/** + * EffectsView: Applies an effect to a tree of actors + * + * Example usage: Applying an emboss effect + * ... + * EffectsView effectsView = EffectsView::New(); + * + * // set position and format + * effectsView.SetParentOrigin( ParentOrigin::CENTER ); + * effectsView.SetSize( Vector2( width, height) ); + * effectsView.SetPixelFormat( Pixel::RGBA8888 ); + * + * // set effect type and properties + * effectsView.SetType( Toolkit::EffectsView::EMBOSS ); + * effectsView.SetProperty( effectsView.GetEffectSizePropertyIndex(), static_cast< float >( shadowSize ) ); + * effectsView.SetProperty( effectsView.GetEffectOffsetPropertyIndex(), Vector3( shadowDistance.x, shadowDistance.y, 0.0f ) ); + * effectsView.SetProperty( effectsView.GetEffectColorPropertyIndex(), shadowColor ); + * + * // Render result to an offscreen + * effectsView.SetOutputImage( image ); + * + * // Render once + * effectsView.SetRefreshOnDemand( true ); + * + * // optionally set a clear color + * effectsView.SetBackgroundColor( Vector4( 0.0f, 0.0f, 0.0f, 0.0f ) ); + * + * // start effect processing + * effectsView.Enable(); + */ +class EffectsView : public Control +{ +public: + + enum EffectType + { + DROP_SHADOW, + EMBOSS, + INVALID_TYPE + }; + +public: + + /** + * Create an EffectsView object with default configuration + */ + static EffectsView New(); + + /** + * Create an uninitialized EffectsView. Only derived versions can be instantiated. + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + EffectsView(); + + /** + * Copy constructor. + */ + EffectsView( const EffectsView& handle ); + + /** + * Assignment operator. + */ + EffectsView& operator=( const EffectsView& rhs ); + + /** + * Downcast an Object handle to EffectsView. If handle points to a EffectsView the + * downcast produces a valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a EffectsView or an uninitialized handle + */ + static EffectsView DownCast( BaseHandle handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~EffectsView(); + +public: + + /** + * Set the effect type + * @param[in] type The type of effect to be performed by the EffectView. + * A member of the EffectType enumeration. + */ + void SetType( EffectType type ); + + /** + * Get the effect type + * @return The type of effect performed by the EffectView. A member of the EffectType enumeration. + */ + EffectType GetType() const; + + /** + * Enable the effect + */ + void Enable(); + + /** + * Disable the effect + */ + void Disable(); + + /** + * Refresh/Redraw the effect + */ + void Refresh(); + + /** + * Set refresh mode + * @param[in] onDemand Set true to enable on demand rendering, call Refresh() whenever a render is required. + * Set false to render each frame. (EffectsView refresh mode is set to continuous by default). + */ + void SetRefreshOnDemand( bool onDemand ); + + /** + * Set the pixel format for the output + * @param[in] pixelFormat The pixel format for the output + */ + void SetPixelFormat( Pixel::Format pixelFormat ); + + /** + * Set the FrameBufferImage that will receive the final output of the EffectsView. + * @param[in] image User supplied FrameBufferImage that will receive the final output of the EffectsView. + */ + void SetOutputImage( FrameBufferImage image ); + + /** + * Get the FrameBufferImage that holds the final output of the EffectsView. + * @return The FrameBufferImage that holds the final output of the EffectsView. + */ + FrameBufferImage GetOutputImage(); + + /** + * Get the property index to the effect size + * @return The property index to the effect size + */ + Property::Index GetEffectSizePropertyIndex() const; + + /** + * Get the property index to the effect strength + * @return The property index to the effect strength + */ + Property::Index GetEffectStrengthPropertyIndex() const; + + /** + * Get the property index to the Vector3 specifying the effect offset (eg drop shadow offset) + * @return The property index to the Vector3 specifying the effect offset + */ + Property::Index GetEffectOffsetPropertyIndex() const; + + /** + * Get the property index to the effect color (eg shadow color) + * @return The property index to the effect color + */ + Property::Index GetEffectColorPropertyIndex() const; + + /** + * Set background color for the view. The background will be filled with this color. + * @param[in] color The background color. + */ + void SetBackgroundColor( const Vector4& color ); + + /** + * Get the background color. + * @return The background color. + */ + Vector4 GetBackgroundColor() const; + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + EffectsView( Internal::EffectsView& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + EffectsView( Dali::Internal::CustomActor* internal ); + +}; // class EffectsView + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_EFFECTS_VIEW_H__ diff --git a/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.cpp b/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.cpp new file mode 100644 index 0000000..7285c50 --- /dev/null +++ b/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.cpp @@ -0,0 +1,138 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +GaussianBlurView::GaussianBlurView() +{ +} + +GaussianBlurView::~GaussianBlurView() +{ +} + +GaussianBlurView::GaussianBlurView(const GaussianBlurView& handle) + : Control( handle ) +{ +} + +GaussianBlurView& GaussianBlurView::operator=(const GaussianBlurView& rhs) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +GaussianBlurView GaussianBlurView::New() +{ + return Internal::GaussianBlurView::New(); +} + +GaussianBlurView GaussianBlurView::New( const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + bool blurUserImage) +{ + return Internal::GaussianBlurView::New( numSamples, blurBellCurveWidth, renderTargetPixelFormat, + downsampleWidthScale, downsampleHeightScale, + blurUserImage); +} + +GaussianBlurView::GaussianBlurView( Internal::GaussianBlurView& implementation ) +: Control( implementation ) +{ +} + +GaussianBlurView::GaussianBlurView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +GaussianBlurView GaussianBlurView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void GaussianBlurView::Add(Actor child) +{ + GetImpl(*this).Add(child); +} + +void GaussianBlurView::Remove(Actor child) +{ + GetImpl(*this).Remove(child); +} + +void GaussianBlurView::Activate() +{ + GetImpl(*this).Activate(); +} + +void GaussianBlurView::ActivateOnce() +{ + GetImpl(*this).ActivateOnce(); +} + +void GaussianBlurView::Deactivate() +{ + GetImpl(*this).Deactivate(); +} + +void GaussianBlurView::SetUserImageAndOutputRenderTarget(Image inputImage, FrameBufferImage outputRenderTarget) +{ + GetImpl(*this).SetUserImageAndOutputRenderTarget(inputImage, outputRenderTarget); +} + +Property::Index GaussianBlurView::GetBlurStrengthPropertyIndex() const +{ + return GetImpl(*this).GetBlurStrengthPropertyIndex(); +} + +FrameBufferImage GaussianBlurView::GetBlurredRenderTarget() const +{ + return GetImpl(*this).GetBlurredRenderTarget(); +} + +void GaussianBlurView::SetBackgroundColor( const Vector4& color ) +{ + GetImpl(*this).SetBackgroundColor(color); +} + +Vector4 GaussianBlurView::GetBackgroundColor() const +{ + return GetImpl(*this).GetBackgroundColor(); +} + +GaussianBlurView::GaussianBlurViewSignal& GaussianBlurView::FinishedSignal() +{ + return GetImpl(*this).FinishedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.h b/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.h new file mode 100644 index 0000000..913acde --- /dev/null +++ b/dali-toolkit/public-api/controls/gaussian-blur-view/gaussian-blur-view.h @@ -0,0 +1,274 @@ +#ifndef __DALI_TOOLKIT_GAUSSIAN_BLUR_EFFECT_H__ +#define __DALI_TOOLKIT_GAUSSIAN_BLUR_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * GaussianBlurView implementation class + */ +class GaussianBlurView; + +/** + * BloomView implementation class - requires access to private methods + */ +class BloomView; + +} // namespace Internal + +/** + * + * GaussianBlurView is a class for applying a render process that blurs an image. + * + * Basic idea:- + * + * 1) The GaussianBlurView object will render all its child actors offscreen.\n + * 2) The GaussianBlurView object then blurs the result of step 1), using a two pass separated Gaussian blur.\n + * 3) The GaussianBlurView object then composites the blur from step 2) with the child actors image from step 1). See GetBlurStrengthPropertyIndex() for more info.\n + * 4) The GaussianBlurView object gets rendered automatically, either to the screen via the default render task, or via a RenderTask the user has created for + * e.g. further offscreen rendering. + * + * Fundamentally, the GaussianBlurView is simply an Actor in the normal actor tree that affects all of its children. It should be added to your Actor tree and manipulated in the + * normal ways. It can be considered a 'portal' in the sense that all child actors are clipped to the GaussianBlurView actor bounds. + * + * ************\n + * NB: It is essential to remove the GaussianBlurView from the stage and also to call Deactivate() on it when you are not using it. This will ensure that resources are freed and + * rendering stops.\n + * ************\n + * + * Usage example:- + * + * // initialise\n + * GaussianBlurView gaussianBlurView = GaussianBlurView::New();\n + * + * // create and add some visible actors to the GaussianBlurView, all these child actors will therefore get blurred.\n + * Image image = Image::New(...);\n + * ImageActor imageActor = ImageActor::New(image);\n + * gaussianBlurView.Add(imageActor);\n + * ...\n + * + * // Start rendering the GaussianBlurView\n + * Stage::GetCurrent().Add(gaussianBlurView);\n + * gaussianBlurView.Activate();\n + * ...\n + * + * // animate the strength of the blur - this can fade between no blur and full blur. See GetBlurStrengthPropertyIndex().\n + * Animation blurAnimation = Animation::New( ... );\n + * blurAnimation.AnimateTo( Property( gaussianBlurView, gaussianBlurView.GetBlurStrengthPropertyIndex() ), ... );\n + * blurAnimation.Play();\n + * + * ...\n + * // Stop rendering the GaussianBlurView\n + * Stage::GetCurrent().Remove(gaussianBlurView);\n + * gaussianBlurView.Deactivate();\n + */ +class GaussianBlurView : public Control +{ +public: + /** + * Signal type for notifications + */ + typedef SignalV2< void (GaussianBlurView source) > GaussianBlurViewSignal; + + /** + * Create an uninitialized GaussianBlurView; this can be initialized with GaussianBlurView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + GaussianBlurView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + GaussianBlurView(const GaussianBlurView& handle); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + GaussianBlurView& operator=(const GaussianBlurView& ZoomView); + + /** + * Virtual destructor. + */ + virtual ~GaussianBlurView(); + + /** + * Downcast an Object handle to GaussianBlurView. If handle points to a GaussianBlurView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a GaussianBlurView or an uninitialized handle + */ + static GaussianBlurView DownCast( BaseHandle handle ); + + /** + * Create an initialized GaussianBlurView, using default settings. The default settings are:-\n + * + * numSamples = 5\n + * blurBellCurveWidth = 1.5\n + * renderTargetPixelFormat = RGB888\n + * downsampleWidthScale = 0.5\n + * downsampleHeightScale = 0.5\n + * blurUserImage = false + * @return A handle to a newly allocated Dali resource + */ + static GaussianBlurView New(); + + /** + * Create an initialized GaussianBlurView. + * @param numSamples The size of the Gaussian blur kernel (number of samples in horizontal / vertical blur directions). + * @param blurBellCurveWidth The constant controlling the Gaussian function, must be > 0.0. Controls the width of the bell curve, i.e. the look of the blur and also indirectly + * the amount of blurriness Smaller numbers for a tighter curve. Useful values in the range [0.5..3.0] - near the bottom of that range the curve is weighted heavily towards + * the centre pixel of the kernel (so there won't be much blur), near the top of that range the pixels have nearly equal weighting (closely approximating a box filter + * therefore). Values close to zero result in the bell curve lying almost entirely within a single pixel, in other words there will be basically no blur as neighbouring pixels + * have close to zero weights. + * @param renderTargetPixelFormat The pixel format of the render targets we are using to perform the blur. + * @param downsampleWidthScale The width scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @param downsampleHeightScale The height scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @param blurUserImage If this is set to true, the GaussianBlurView object will operate in a special mode that allows the user to blur an image of their choice. See + * SetUserImageAndOutputRenderTarget(). + * @return A handle to a newly allocated Dali resource + */ + static GaussianBlurView New(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, + const float downsampleWidthScale, const float downsampleHeightScale, + bool blurUserImage = false); + + /** + * Adds a child Actor to this Actor. + * NOTE! if the child already has a parent, it will be removed from old parent + * and reparented to this actor. This may change childs position, color, shader effect, + * scale etc as it now inherits them from this actor + * @pre This Actor (the parent) has been initialized. + * @pre The child actor has been initialized. + * @pre The child actor is not the same as the parent actor. + * @pre The actor is not the Root actor + * @param [in] child The child. + * @post The child will be referenced by its parent. This means that the child will be kept alive, + * even if the handle passed into this method is reset or destroyed. + * @post This may invalidate ActorContainer iterators. + */ + void Add(Actor child); + + /** + * Removes a child Actor from this Actor. + * If the actor was not a child of this actor, this is a no-op. + * @pre This Actor (the parent) has been initialized. + * @pre The child actor is not the same as the parent actor. + * @param [in] child The child. + * @post This may invalidate ActorContainer iterators. + */ + void Remove(Actor child); + + /** + * Start rendering the GaussianBlurView. Must be called after you Add() it to the stage. + */ + void Activate(); + + /** + * Render the GaussianBlurView once. Must be called after you Add() it to the stage. + * Only works with a gaussian blur view created with blurUserImage = true. + * Listen to the Finished signal to determine when the rendering has completed. + */ + void ActivateOnce(); + + /** + * Stop rendering the GaussianBlurView. Must be called after you Remove() it from the stage. + */ + void Deactivate(); + + /** + * Sets a custom image to be blurred and a render target to receive the blurred result. If this is called the children of the GaussianBlurObject will not be rendered blurred, + * instead the inputImage will get blurred. + * To retrieve the blurred image the user can either pass a handle on a render target they own as the second parameter to SetUserImageAndOutputRenderTarget( ... ), or they + * can pass NULL for this parameter and instead call GetBlurredRenderTarget() which will return a handle on a render target created internally to the GaussianBlurView object. + * @pre This object was created with a New( ... ) call where the blurUserImage argument was set to true. If this was not the case an exception will be thrown. + * @param inputImage The image that the user wishes to blur. + * @param outputRenderTarget A render target to receive the blurred result. Passing NULL is allowed. See also GetBlurredRenderTarget(). + */ + void SetUserImageAndOutputRenderTarget(Image inputImage, FrameBufferImage outputRenderTarget); + + /** + * Get the index of the property that can be used to fade the blur in / out. This is the overall strength of the blur. + * User can use this to animate the blur. A value of 0.0 is zero blur and 1.0 is full blur. Default is 1.0. + * Note that if you set the blur to 0.0, the result will be no blur BUT the internal rendering will still be happening. If you wish to turn the blur off, you should remove + * the GaussianBlurView object from the stage also. + * @return Index of the property that can be used to fade the blur in / out + */ + Property::Index GetBlurStrengthPropertyIndex() const; + + /** + * Get the final blurred image. + * Use can call this function to get the blurred result as an image, to use as they wish. It is not necessary to call this unless you specifically require it. + * @pre The user must call Activate() before the render target will be returned. + * @return A handle on the blurred image, contained in a render target. + */ + FrameBufferImage GetBlurredRenderTarget() const; + + /** + * Set background color for the view. The background will be filled with this color. + * @param[in] color The background color. + */ + void SetBackgroundColor( const Vector4& color ); + + /** + * Get the background color. + * @return The background color. + */ + Vector4 GetBackgroundColor() const; + +public: // Signals + /** + * If ActivateOnce has been called, then connect to this signal to be notified when the + * target actor has been rendered. + * @return The Finished signal + */ + GaussianBlurViewSignal& FinishedSignal(); + +public: + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The UI Control implementation. + */ + GaussianBlurView( Internal::GaussianBlurView& implementation ); + + /** + * Allows the creation of this UI Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + GaussianBlurView( Dali::Internal::CustomActor* internal ); + +private: + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_GAUSSIAN_BLUR_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/image-view/image-view.cpp b/dali-toolkit/public-api/controls/image-view/image-view.cpp new file mode 100644 index 0000000..ac42aeb --- /dev/null +++ b/dali-toolkit/public-api/controls/image-view/image-view.cpp @@ -0,0 +1,121 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +using namespace Dali; + +namespace +{ +const float DEFAULT_MINIMUM_DETAIL = 0.125f; ///< Default Minimum Detail level 12.5% of original size. +const float DEFAULT_MAXIMUM_DETAIL = 1.0f; ///< Default Maximum Detail level 100% (original size) +const float CAMERA_100_PCT_DISTANCE(1695.0f); ///< Based on Camera/Viewport/Projection settings at this distance object is 100% size. +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ImageView +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string ImageView::DETAIL_PROPERTY_NAME( "image-view-detail" ); + +ImageView::ImageView() +{ +} + +ImageView::ImageView( const ImageView& handle ) +: Control( handle ) +{ +} + +ImageView& ImageView::operator=( const ImageView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +ImageView::ImageView(Internal::ImageView& implementation) +: Control(implementation) +{ +} + +ImageView::ImageView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +ImageView ImageView::New() +{ + return Internal::ImageView::New(); +} + +ImageView ImageView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +ImageView::~ImageView() +{ +} + +void ImageView::SetImage(const std::string& filename, ImageType type) +{ + GetImpl(*this).SetImage( filename, type, DEFAULT_MINIMUM_DETAIL, DEFAULT_MAXIMUM_DETAIL ); +} + +void ImageView::SetImage(const std::string& filename, ImageType type, float min, float max) +{ + GetImpl(*this).SetImage( filename, type, min, max ); +} + +void ImageView::SetImage(Image image) +{ + GetImpl(*this).SetImage( image ); +} + +void ImageView::SetCameraActor(CameraActor camera) +{ + // TODO: Default detail factor should be calculated based on + // current Camera's field of view/viewport/projection. + // Ideal place would be inside the constraint, and have the Camera + // settings as properties. + GetImpl(*this).SetCameraActor( camera, CAMERA_100_PCT_DISTANCE ); +} + +void ImageView::SetCameraActor(CameraActor camera, float detailFactor) +{ + GetImpl(*this).SetCameraActor( camera, detailFactor ); +} + +void ImageView::SetDetail(float detail) +{ + GetImpl(*this).SetDetail( detail ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/image-view/image-view.h b/dali-toolkit/public-api/controls/image-view/image-view.h new file mode 100644 index 0000000..a5529b6 --- /dev/null +++ b/dali-toolkit/public-api/controls/image-view/image-view.h @@ -0,0 +1,196 @@ +#ifndef __DALI_TOOLKIT_IMAGE_VIEW_H__ +#define __DALI_TOOLKIT_IMAGE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ImageView; +} + +class Button; + +/** + * ImageView control loads and displays the correct + * image for the current level of detail (LOD) required. + * LOD is typically calculated from the Camera distance. + * + * Example: + * + * ImageView imageView = ImageView::New(); + * imageView.SetCameraActor( mCamera ); + * imageView.SetSize( Vector2(64.0f, 64.0f) ); + * imageView.SetImage( "my-image.png", ImageView::BitmapType, 0.125f, 4.0f ); + * layer.Add(imageView); + * + * The above creates an ImageView at 64x64 in size. Images of 12.5% the size up + * to 400% the size of imageView are created + * i.e. 8x8, 16x16, 32x32, 64x64, 128x128, and 256x256 + * + * based on the distance imageView is from mCamera an appropriate, different + * image will be loaded and dispayed. + */ +class ImageView : public Control +{ +public: + + /** + * Image Types, determines how image should be rendered. + */ + enum ImageType + { + BitmapType, ///< Standard Bitmap image + DistanceFieldType ///< Distance Field encoded image + }; + + static const std::string DETAIL_PROPERTY_NAME; ///< The level of detail property + +public: + + /** + * Creates an empty ImageView handle + */ + ImageView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + ImageView( const ImageView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + ImageView& operator=( const ImageView& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~ImageView(); + + /** + * Create the Poup control + * @return A handle to the ImageView control. + */ + static ImageView New(); + + /** + * Downcast an Object handle to ImageView. If handle points to an ImageView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ImageView or an uninitialized handle + */ + static ImageView DownCast( BaseHandle handle ); + +public: + + /** + * Load image into ImageView for level of detail scaling. + * Will automatically create different sized versions + * of the source image. + * + * @param[in] filename The image path to load + * @param[in] type The type of image e.g. BitmapType or DistanceFieldType + */ + void SetImage(const std::string& filename, ImageType type); + + /** + * Load image into ImageView for level of detail scaling. + * The minimum scale is a percentage of the size of the + * image view, and represents the smallest version of the + * source image to display e.g. 0.125 for 12.5% + * While the maximum scale represents the largest version of + * the source image to display e.g. 1.00 for 100% (original + * image view size) + * + * @note ImageView SetSize must be set specified prior to + * calling this. + * + * @param[in] filename The image path to load + * @param[in] type The type of image e.g. BitmapImage or DistanceFieldImage + * @param[in] min The minimum scale detail to load. + * @param[in] max The maximum scale detail to load. + */ + void SetImage(const std::string& filename, ImageType type, float min, float max); + + /** + * Sets an image to displayed for the entire detail range. + * Regardless of the detail level this image will be displayed. + * + * @param[in] image The image to display + */ + void SetImage(Image image); + + /** + * Sets the camera to use for determining level of detail. + * Which is based on distance from camera to this ImageView. + * The detailFactor is the distance at which the ImageView + * should appear at 100% scale. Which may differ based on + * Projection, and ShaderEffect settings. + * @param[in] camera The camera + */ + void SetCameraActor(CameraActor camera); + + /** + * Sets the camera to use for determining level of detail. + * Which is based on distance from camera to this ImageView. + * The detailFactor is the distance at which the ImageView + * should appear at 100% scale. Which may differ based on + * Projection, and ShaderEffect settings. + * @param[in] camera The camera + * @param[in] detailFactor The Camera distance where detail should be 1.0 + * (ImageView should appear at 100% scale) + */ + void SetCameraActor(CameraActor camera, float detailFactor); + + /** + * Sets the current detail level. + * @note This sets the detail property value. + * @param[in] detail The level of detail to be viewed at. + */ + void SetDetail(float detail); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ImageView(Internal::ImageView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ImageView(Dali::Internal::CustomActor* internal); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_IMAGE_VIEW_H__ diff --git a/dali-toolkit/public-api/controls/image-view/masked-image-view.cpp b/dali-toolkit/public-api/controls/image-view/masked-image-view.cpp new file mode 100644 index 0000000..1097b46 --- /dev/null +++ b/dali-toolkit/public-api/controls/image-view/masked-image-view.cpp @@ -0,0 +1,167 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const float MaskedImageView::DEFAULT_MAXIMUM_SOURCE_SCALE(3.0f); + +MaskedImageView::MaskedImageView() +{ +} + +MaskedImageView::MaskedImageView( const MaskedImageView& handle ) +: Control( handle ) +{ +} + +MaskedImageView& MaskedImageView::operator=( const MaskedImageView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +MaskedImageView::~MaskedImageView() +{ +} + +MaskedImageView MaskedImageView::New( unsigned int targetWidth, + unsigned int targetHeight, + Image sourceImage, + Image maskImage ) +{ + return Internal::MaskedImageView::New( targetWidth, targetHeight, sourceImage, maskImage ); +} + +MaskedImageView MaskedImageView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +void MaskedImageView::SetSourceImage( Image sourceImage ) +{ + GetImpl(*this).SetSourceImage( sourceImage ); +} + +Image MaskedImageView::GetSourceImage() +{ + return GetImpl(*this).GetSourceImage(); +} + +void MaskedImageView::SetMaskImage( Image sourceImage ) +{ + GetImpl(*this).SetMaskImage( sourceImage ); +} + +Image MaskedImageView::GetMaskImage() +{ + return GetImpl(*this).GetMaskImage(); +} + +Property::Index MaskedImageView::GetPropertyIndex( MaskedImageView::CustomProperty customProperty ) const +{ + return GetImpl(*this).GetPropertyIndex( customProperty ); +} + +void MaskedImageView::Pause() +{ + GetImpl(*this).Pause(); +} + +void MaskedImageView::Resume() +{ + GetImpl(*this).Resume(); +} + +bool MaskedImageView::IsPaused() const +{ + return GetImpl(*this).IsPaused(); +} + +void MaskedImageView::SetEditMode( MaskedImageView::EditMode editMode ) +{ + GetImpl(*this).SetEditMode( editMode ); +} + +MaskedImageView::EditMode MaskedImageView::GetEditMode() const +{ + return GetImpl(*this).GetEditMode(); +} + +void MaskedImageView::SetSourceAspectRatio( float widthOverHeight ) +{ + GetImpl(*this).SetSourceAspectRatio( widthOverHeight ); +} + +float MaskedImageView::GetSourceAspectRatio() const +{ + return GetImpl(*this).GetSourceAspectRatio(); +} + +void MaskedImageView::SetMaximumSourceScale( float scale ) +{ + GetImpl(*this).SetMaximumSourceScale( scale ); +} + +float MaskedImageView::GetMaximumSourceScale() const +{ + return GetImpl(*this).GetMaximumSourceScale(); +} + +void MaskedImageView::SetSourceRotation( MaskedImageView::ImageRotation rotation ) +{ + GetImpl(*this).SetSourceRotation( rotation ); +} + +MaskedImageView::ImageRotation MaskedImageView::GetSourceRotation() const +{ + return GetImpl(*this).GetSourceRotation(); +} + +Dali::RenderTask::RenderTaskSignalV2& MaskedImageView::RenderFinishedSignal() +{ + return GetImpl(*this).RenderFinishedSignal(); +} + +MaskedImageView::MaskedImageViewSignal& MaskedImageView::MaskFinishedSignal() +{ + return GetImpl(*this).MaskFinishedSignal(); +} + +MaskedImageView::MaskedImageView(Internal::MaskedImageView& implementation) +: Control(implementation) +{ +} + +MaskedImageView::MaskedImageView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/magnifier/magnifier.cpp b/dali-toolkit/public-api/controls/magnifier/magnifier.cpp new file mode 100644 index 0000000..24c9271 --- /dev/null +++ b/dali-toolkit/public-api/controls/magnifier/magnifier.cpp @@ -0,0 +1,110 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace +{ + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Magnifier +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string Magnifier::SOURCE_POSITION_PROPERTY_NAME( "source-position" ); + +Magnifier::Magnifier() +{ +} + +Magnifier::Magnifier( const Magnifier& handle ) +: Control( handle ) +{ +} + +Magnifier& Magnifier::operator=( const Magnifier& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +Magnifier::Magnifier(Internal::Magnifier& implementation) +: Control(implementation) +{ +} + +Magnifier::Magnifier( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +Magnifier Magnifier::New() +{ + return Internal::Magnifier::New(); +} + +Magnifier::~Magnifier() +{ +} + +void Magnifier::SetSourceActor(Actor actor) +{ + GetImpl(*this).SetSourceActor( actor ); +} + +void Magnifier::SetSourcePosition(Vector3 position) +{ + GetImpl(*this).SetSourcePosition( position ); +} + +bool Magnifier::GetFrameVisibility() const +{ + return GetImpl(*this).GetFrameVisibility(); +} + +void Magnifier::SetFrameVisibility(bool visible) +{ + GetImpl(*this).SetFrameVisibility(visible); +} + +float Magnifier::GetMagnificationFactor() const +{ + return GetImpl(*this).GetMagnificationFactor(); +} + +void Magnifier::SetMagnificationFactor(float value) +{ + GetImpl(*this).SetMagnificationFactor( value ); +} + + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/magnifier/magnifier.h b/dali-toolkit/public-api/controls/magnifier/magnifier.h new file mode 100644 index 0000000..df02af4 --- /dev/null +++ b/dali-toolkit/public-api/controls/magnifier/magnifier.h @@ -0,0 +1,151 @@ +#ifndef __DALI_TOOLKIT_MAGNIFIER_H__ +#define __DALI_TOOLKIT_MAGNIFIER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Magnifier; +} + +/** + * Magnifier control is used to apply a magnify effect to content on the stage. + * + * This is done by rendering the contents of a SourceActor at a given source position + * to the stage as a separate overlay. In addition to the contents, an optional frame + * is displayed around the magnified contents. + */ +class Magnifier : public Control +{ +public: + + // Custom properties + + static const std::string SOURCE_POSITION_PROPERTY_NAME; ///< Property, name "source-position", type VECTOR3 + +public: + + /** + * Creates an empty Magnifier handle + */ + Magnifier(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + Magnifier( const Magnifier& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + Magnifier& operator=( const Magnifier& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Magnifier(); + + /** + * Create the Poup control + * @return A handle to the Magnifier control. + */ + static Magnifier New(); + + /** + * Downcast an Object handle to Magnifier. If handle points to an Magnifier the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Magnifier or an uninitialized handle + */ + static Magnifier DownCast( BaseHandle handle ); + +public: + + /** + * Set the actors to be rendered in magnifier. + * @param[in] actor This actor and its children will be rendered. + */ + void SetSourceActor(Actor actor); + + /** + * Set the source camera position to render in magnifier + * @param[in] position The target position from which to render source. + */ + void SetSourcePosition(Vector3 position); + + /** + * Returns whether the frame is visible or not. + * @return true if frame is visible, false if not. + */ + bool GetFrameVisibility() const; + + /** + * Sets whether the frame part of the magnifier should be visible + * or not. + * @param[in] visible true to display frame, false to hide frame. + */ + void SetFrameVisibility(bool visible); + + /** + * Get the magnification factor of the magnifier + * The larger the value the larger the contents magnified. + * A value of 1.0f indications 1x magnification. + * @return Magnification factor is returned + */ + float GetMagnificationFactor() const; + + /** + * Set the magnification factor of the magnifier + * The larger the value the larger the contents magnified. + * A value of 1.0f indications 1x magnification. + * @param[in] value Magnification factor. + */ + void SetMagnificationFactor(float value); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + DALI_INTERNAL Magnifier(Internal::Magnifier& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + DALI_INTERNAL Magnifier(Dali::Internal::CustomActor* internal); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_MAGNIFIER_H__ diff --git a/dali-toolkit/public-api/controls/navigation-frame/navigation-bar-style.h b/dali-toolkit/public-api/controls/navigation-frame/navigation-bar-style.h new file mode 100644 index 0000000..31e0566 --- /dev/null +++ b/dali-toolkit/public-api/controls/navigation-frame/navigation-bar-style.h @@ -0,0 +1,150 @@ +#ifndef __DALI_TOOLKIT_NAVIGATION_BAR_STYLE_H__ +#define __DALI_TOOLKIT_NAVIGATION_BAR_STYLE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + + /** + * The basic information of a navigation bar style: background and size. + * All the style metrics for tool bar and title bar are in pixels referring to a bar width of 'referenceWidth'. + * To fit the bars into different window size, the NavigationControl scales the bar by the ratio of windowWidth and referenceWidth. + */ + struct BasicNaviBarStyle + { + /** + * Constructor + */ + BasicNaviBarStyle ( Actor background, + int referenceWidth, + int height ) + : background( background ), + referenceWidth( referenceWidth ), + height( height ) + { + } + + Actor background; ///< bar background + int referenceWidth; ///< the width of the bar, this value also used to calculate the scale to fit the bar into windows of different sizes + int height; ///< the height of the bar + }; + + /** + * The tool bar locates in the bottom of the frame where other controls (such as PushButton ) could be placed. + * NaviToolBarStyle provides the tool bar layout style, which customize the position and size of the controls placed on it. + * Controls could be added into three groups: HorizontalLeft, HorizontalCenter or HorizontalRight. + * The left and right groups can only have one control maximum each, while the central group can have multiple controls. + * It is fine to have no control in every group. + * +----------------------------------------+ + * | +-+ +-----+ +-----+ +-+ | + * | +-+ +-----+ +-----+ +-+ | + * +----------------------------------------+ + */ + struct NaviToolBarStyle : BasicNaviBarStyle + { + /** + * Constructor + */ + NaviToolBarStyle ( Actor background, + int referenceWidth, + int height, + int centralMaximum, + int centralMinimum, + int centralButtonHeight, + int centralButtonGap, + int sideButtonSize, + int hotizontalMargin ) + : BasicNaviBarStyle( background, referenceWidth, height), + centralMaximum( centralMaximum ), centralMinimum( centralMinimum ), + centralButtonHeight( centralButtonHeight ), centralButtonGap( centralButtonGap ), + sideButtonSize( sideButtonSize ), hotizontalMargin( hotizontalMargin ) + { + } + + int centralMaximum; ///< the maximum width of central button + int centralMinimum; ///< the minimum width of central button + int centralButtonHeight; ///< the height of the central button + int centralButtonGap; ///< the gap width between central buttons + int sideButtonSize; ///< the size of side buttons in the left and right groups: sideButtonSize*sideButtonSize + int hotizontalMargin; ///< the horizontal margin width + }; + + /** + * The title bar locates in the top of the frame where title, subtitle, title icon and other controls could be placed. + * NaviTitleBarStyle provides the title bar layout style, + * which customize the position and size of the components placed on it and the text style of the titles. + * The title bar contains two groups: the left group includes title icon, title and subtitle (subtitle and title icon are not must); + * while the right group can have multiple controls placed on, and it is also fine to have no control on it. + * +----------------------------------------+ + * | +-+ Title +-+ +-+ | + * | +-+ Subtitle +-+ +-+ | + * +----------------------------------------+ + */ + struct NaviTitleBarStyle : BasicNaviBarStyle + { + /** + * Constructor + */ + NaviTitleBarStyle( Actor background, + TextStyle titleTextStyle, + TextStyle subtitleTextStyle, + int referenceWidth, + int height, + int titleHeightWithoutSubtitle, + int titleHeightWithSubtitle, + int subtitleHeight, + int titleLeftMargin, + int titleBottomMargin, + int titleIconSize, + int buttonSize, + int buttonRightMargin, + int buttonBottomMargin, + int gapBetweenButtons ) + : BasicNaviBarStyle( background, referenceWidth, height), + titleTextStyle( titleTextStyle ), subtitleTextStyle( subtitleTextStyle ), + titleHeightWithoutSubtitle( titleHeightWithoutSubtitle ), + titleHeightWithSubtitle( titleHeightWithSubtitle ), subtitleHeight( subtitleHeight ), + titleLeftMargin( titleLeftMargin ), titleBottomMargin( titleBottomMargin ), + titleIconSize( titleIconSize ), buttonSize( buttonSize ), + buttonRightMargin( buttonRightMargin ), buttonBottomMargin( buttonBottomMargin ), + gapBetweenButtons( gapBetweenButtons ) + { + } + + TextStyle titleTextStyle; ///< the text style of the tile text + TextStyle subtitleTextStyle; ///< the text style of the subtitle text + int titleHeightWithoutSubtitle; ///< the height of the title when no subtitle exists + int titleHeightWithSubtitle; ///< the height of the title when there is subtitle below + int subtitleHeight; ///< the height of the subtitle + int titleLeftMargin; ///< the Margin between title and the left edge of the bar + int titleBottomMargin; ///< the Margin between title and the bottom edge of the bar + int titleIconSize; ///< the size of the title icon: titleIconSize*titleIconSize + int buttonSize; ///< the size of the buttons in the right group: buttonSize*buttonSize + int buttonRightMargin; ///< the Margin between the button and the right edge of the bar + int buttonBottomMargin; ///< the Margin between the button and the bottom edge of the bar + int gapBetweenButtons; ///< the gap width between buttons + }; + +} // namespace Toolkit +} // namespace Dali + + +#endif /* __DALI_TOOLKIT_NAVIGATION_BAR_STYLE_H__ */ diff --git a/dali-toolkit/public-api/controls/navigation-frame/navigation-control.cpp b/dali-toolkit/public-api/controls/navigation-frame/navigation-control.cpp new file mode 100644 index 0000000..d4f4afd --- /dev/null +++ b/dali-toolkit/public-api/controls/navigation-frame/navigation-control.cpp @@ -0,0 +1,140 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "navigation-control.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const NavigationControl::ACTION_PUSH = "push"; +const char* const NavigationControl::ACTION_POP = "pop"; + +NavigationControl::NavigationControl() +{ +} + +NavigationControl::NavigationControl( const NavigationControl& handle ) +: Control(handle) +{ +} + +NavigationControl& NavigationControl::operator=( const NavigationControl& handle) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +NavigationControl::~NavigationControl() +{ +} + +NavigationControl NavigationControl::New() +{ + return Internal::NavigationControl::New(); +} + +NavigationControl NavigationControl::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +NavigationControl::NavigationControl( Internal::NavigationControl& implementation ) +: Control( implementation ) +{ +} + +NavigationControl::NavigationControl( Dali::Internal::CustomActor* internal ) +: Control( internal) +{ + VerifyCustomActorPointer(internal); +} + + +void NavigationControl::PushItem( Page item ) +{ + GetImpl( *this ).PushItem( item ); +} + +Page NavigationControl::PopItem() +{ + return GetImpl( *this ).PopItem(); +} + +size_t NavigationControl::GetItemCount() const +{ + return GetImpl( *this ).GetItemCount(); +} + +Page NavigationControl::GetItem(std::size_t index) const +{ + return GetImpl( *this ).GetItem( index ); +} + +Page NavigationControl::GetCurrentItem() const +{ + return GetImpl(*this ).GetCurrentItem(); +} + +void NavigationControl::SetBackground( Actor background) +{ + GetImpl( *this ).SetBackground( background ); +} + +void NavigationControl::CreateNavigationToolBar( NaviToolBarStyle toolBarStylePortrait, NaviToolBarStyle toolBarStyleLandscape ) +{ + GetImpl( *this ).CreateNavigationToolBar( toolBarStylePortrait, toolBarStyleLandscape ); +} + +void NavigationControl::CreateNavigationTitleBar( NaviTitleBarStyle titleBarStylePortrait, NaviTitleBarStyle titleBarStyleLandscape ) +{ + GetImpl( *this ).CreateNavigationTitleBar( titleBarStylePortrait, titleBarStyleLandscape ); +} + +void NavigationControl::OrientationChanged( int angle ) +{ + GetImpl( *this ).OrientationChanged( angle ); +} + +void NavigationControl::SetOrientationRotateAnimation( float duration, AlphaFunction alphaFunc) +{ + GetImpl( *this ).SetOrientationRotateAnimation( duration, alphaFunc ); +} + +NavigationControl::ItemPushedSignalV2& NavigationControl::ItemPushedSignal() +{ + return GetImpl( *this ).ItemPushedSignal(); +} + +NavigationControl::ItemPoppedSignalV2& NavigationControl::ItemPoppedSignal() +{ + return GetImpl( *this ).ItemPoppedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/navigation-frame/navigation-control.h b/dali-toolkit/public-api/controls/navigation-frame/navigation-control.h new file mode 100644 index 0000000..6275e43 --- /dev/null +++ b/dali-toolkit/public-api/controls/navigation-frame/navigation-control.h @@ -0,0 +1,246 @@ +#ifndef __DALI_TOOLKIT_NAVIGATION_CONTROL_H__ +#define __DALI_TOOLKIT_NAVIGATION_CONTROL_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class NavigationControl; +} + +/** + * NavigationControl implements a controller than manages the navigation of hierarchical contents. + * NavigationControl holds views as its item which are organized in a stack. + * New items get pushed on the top of the old. Only the topmost item is displayed in the view area at one time. + * Its layout contains a title bar on the top, a tool bar in the bottom, and the content of top item in the middle. + * The top item carries title/subtitle/buttons/icon information, + * so with new item on the top, the NavigationControl will update the bars accordingly. + * if no component is needed to place on the bar for the current item, the bar is hidden + * +----------------------------------------+ + * | | + * | +-+ Title +-+ +-+ | title bar + * | +-+ Subtitle +-+ +-+ | + * +----------------------------------------+ + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | View Area | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * +----------------------------------------+ + * | +-+ +-----+ +-----+ +-+ | + * | +-+ +-----+ +-----+ +-+ | tool bar + * +----------------------------------------+ + */ + +class NavigationControl : public Control +{ + +public: + //Action Names + static const char* const ACTION_PUSH; + static const char* const ACTION_POP; + +public: + + /** + * Create a NavigationControl handle; this can be initialize with NavigationControl::New(). + * Calling member function with an uninitialized handle is not allowed. + */ + NavigationControl(); + + /** + * Copy Constructor. + */ + NavigationControl( const NavigationControl& handle ); + + /** + * Assignment operator. + */ + NavigationControl& operator=( const NavigationControl& handle ); + + /** + * virtual Destructor. + */ + virtual ~NavigationControl(); + + /** + * Create an initialized NavigationControl. + * @return A handle to a newly allocated Dali resource. + */ + static NavigationControl New(); + + /** + * Downcast an object handle to NavigationControl. + * If handle points to a NavigationControl, the downcast produces valid handle. + * If not, the returned handle is left uninitialized. + * @param[in] handle Handle to an object. + * @return handle to a NavigationControl of an uninitialized handle. + */ + static NavigationControl DownCast( BaseHandle handle ); + + /** + * Push a new item to the top of the NavigationControl stack and show it. + * @param[in] item A Page object. + */ + void PushItem( Page item ); + + /** + * Pop an item that is on the top of the NavigationControl stack and make it disappear. + * It doesnot pop out the last item in the stack. + * It returns an uninitialized item handle if there is no item or only one item in the stack. + * @return The Page popped out. + */ + Page PopItem(); + + /** + * Query the number of items in the stack. + * @return the number of items in the stack. + */ + std::size_t GetItemCount() const; + + /** + * Retrieve the index-th item in the stack + * Here, the index is from zero to stack size minus one, the bottom-most item is with index zero + * @pre There are more items in the stack than the parameter index plus one + * @param[in] index The location index of the item in the stack + * @return The index-th item in the navigation stack + */ + Page GetItem(std::size_t index) const; + + /** + * Retrieve the current top item. + * @return the Page object which is on the top of the stack. + */ + Page GetCurrentItem() const; + + /** + * Sets a background image. + * @param[in] background Actor with the navigation control background. + */ + void SetBackground( Actor background); + + /** + *Create a tool bar at the bottom of the navigation control. + *@param[in] toolBarStylePortrait the given navigation tool bar style of Portrait orientation. + *@param[in] toolBarStyleLandscape the given navigation tool bar style of Landscape orientation. + */ + void CreateNavigationToolBar( NaviToolBarStyle toolBarStylePortrait, NaviToolBarStyle toolBarStyleLandscape ); + + /** + * Create a title bar at the top of the navigation control. + * @param[in] titleBarStylePortrait the given navigation title bar style of Portrait orientation. + * @param[in] titleBarStyleLandscape the given navigation title bar style of Landscape orientation. + */ + void CreateNavigationTitleBar( NaviTitleBarStyle titleBarStylePortrait, NaviTitleBarStyle titleBarStyleLandscape); + + /** + * Rotate all the contents to the new given orientation. This rotation is animated. + * Also change the bar style from portrait to landscape style, or vice versa. + * The application should invoke this function in call back of the orientation change signal if different orientations are required. + * @param[in] angle The angle degree of the new orientation, this is one of four discrete values, in degrees clockwise: 0, 90, 180, & 270 + */ + void OrientationChanged( int angle ); + + /** + * Set the duration and the alpha function for the rotating animation in OrientationChanged function above. + * Without calling this function, the default values are 1.0 and EaseOut respectively. + * @param[in] duration The duration of the rotating animation when orientation changed. + * @param[in] alphaFunc The alpha function of the rotating animation when orientation changed. + */ + void SetOrientationRotateAnimation( float duration, AlphaFunction alphaFunc); + +public: //Signal + + typedef SignalV2< void( NavigationControl, Page ) > ItemPushedSignalV2; + typedef SignalV2< void( NavigationControl, Page ) > ItemPoppedSignalV2; + + /** + * Signal emitted right after a new item is pushed into the navigation stack. + * A callback of the following type may be connected: + * @code + * void YourCallBackName(NavigationControl controller, Page pushedItem); + * @endcode + * @return The signal to connect to. + */ + ItemPushedSignalV2& ItemPushedSignal(); + + /** + * Signal emitted right after an item is popped out from the navigation stack. + * A callback of the following type may be connected: + * @code + * void YourCallBackName(NavigationControl controller, Page poppedItem); + * @endcode + * If attempt to pop the bottom-most item, the poppedItem in the callback will receive an uninitialized handle + * The app can use this signal and check the poppedItem to be uninitialized to know the app window should be lower + * @return The signal to connect to. + */ + ItemPoppedSignalV2& ItemPoppedSignal(); + + + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + NavigationControl( Internal::NavigationControl& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + NavigationControl( Dali::Internal::CustomActor* internal ); + +}; // class NavigationControl + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_NAVIGATION_CONTROL_H__ */ diff --git a/dali-toolkit/public-api/controls/navigation-frame/page.cpp b/dali-toolkit/public-api/controls/navigation-frame/page.cpp new file mode 100644 index 0000000..f322b3a --- /dev/null +++ b/dali-toolkit/public-api/controls/navigation-frame/page.cpp @@ -0,0 +1,135 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "page.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const Page::PROPERTY_TITLE = "title"; +const char* const Page::PROPERTY_SUB_TITLE = "sub-title"; + +Page::Page() +{ +} + +Page::Page( const Page& handle ) +: Control(handle) +{ +} + +Page& Page::operator=( const Page& handle ) +{ + if( &handle != this ) + { + CustomActor::operator=( handle ); + } + return *this; +} + +Page Page::New() +{ + return Internal::Page::New(); +} + +Page Page::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +Page::Page(Internal::Page& impl) +: Control(impl) +{ +} + +Page::Page( Dali::Internal::CustomActor* internal ) +: Control( internal) +{ + VerifyCustomActorPointer(internal); +} + +void Page::SetTitle(const std::string& title) +{ + GetImpl( *this ).SetTitle(title); +} + +const std::string& Page::GetTitle() const +{ + return GetImpl( *this ).GetTitle(); +} + +void Page::SetSubTitle(const std::string& subtitle) +{ + GetImpl( *this ).SetSubTitle(subtitle); +} + +const std::string& Page::GetSubTitle() const +{ + return GetImpl( *this ).GetSubTitle(); +} + +void Page::SetTitleIcon( Actor titleIcon) +{ + GetImpl( *this ).SetTitleIcon(titleIcon); +} + +Actor Page::GetTitleIcon() const +{ + return GetImpl( *this ).GetTitleIcon(); +} + +bool Page::AddControlToToolBar(Actor control, Alignment::Type alignment) +{ + return GetImpl( *this ).AddControlToToolBar(control, alignment); +} + +const Page::ControlOnBarContainer Page::GetControlsOnToolBar() const +{ + return GetImpl( *this ).GetControlsOnToolBar(); +} + +bool Page::AddControlToTitleBar(Actor control) +{ + return GetImpl( *this ).AddControlToTitleBar(control); +} + +const ActorContainer Page::GetControlsOnTitleBar() const +{ + return GetImpl( *this ).GetControlsOnTitleBar(); +} + +void Page::SetPopupMenu( Toolkit::Popup popupMenu ) +{ + GetImpl( *this ).SetPopupMenu( popupMenu ); +} + +Toolkit::Popup Page::GetPopupMenu() const +{ + return GetImpl( *this ).GetPopupMenu(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/navigation-frame/page.h b/dali-toolkit/public-api/controls/navigation-frame/page.h new file mode 100644 index 0000000..0dfc952 --- /dev/null +++ b/dali-toolkit/public-api/controls/navigation-frame/page.h @@ -0,0 +1,209 @@ +#ifndef __DALI_TOOLKIT_PAGE_H__ +#define __DALI_TOOLKIT_PAGE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class Page; +} + +/** + * A Page is a custom control which can be pushed into the stack of navigation control. + * It serves as the root of a navigation view. + * It also carries the title/subtitle/buttons/icons information which would be shown on the navigation bars when the item is on the top of the stack. + */ +class Page : public Control +{ + +public: + + /** + * The names of custom properties installed by this control. + */ + // Property Names + static const char* const PROPERTY_TITLE; ///< name "title", type std::string + static const char* const PROPERTY_SUB_TITLE; ///< name "sub-title", type std::string + + /** + * Structure to indicate a control on the navigation tool bar and its group (HorizontalLeft, HorizontalRight or HorizontalCenter) + */ + struct ControlOnBar + { + /** + * Constructor + */ + ControlOnBar(Actor actor, Toolkit::Alignment::Type alignment) + : control( actor ), + alignment( alignment ) + { + } + + Actor control; ///< The control actor + Toolkit::Alignment::Type alignment; ///< The alignment of the control actor + }; + + typedef std::vector< const ControlOnBar* > ControlOnBarContainer; + +public: + + /** + * Create a Page handle; this can be initialized with Page::New(). + * Calling member function with an uninitialized handle is not allowed. + */ + Page(); + + /** + * Copy Constructor. + */ + Page( const Page& handle ); + + /** + * Assignment operator. + * Change this handle to point to another real object. + */ + Page& operator=( const Page& handle ); + + /** + * Create an initialized Page. + * @return A handle to a newly allocated Dali resource. + */ + static Page New(); + + /** + * Downcast an object handle to Page. + * If handle points to a Page, the downcast produces valid handle. + * If not, the returned handle is left uninitialized. + * @param[in] handle Handle to an object. + * @return handle to a Page of an uninitialized handle. + */ + static Page DownCast( BaseHandle handle ); + + /** + * Sets the Page's title. + * The title will be displayed on the navigation title bar when the item is on the top of the stack. + * @param[in] title The Page's title. + */ + void SetTitle(const std::string& title); + + /** + * Retrieve the title of the page. + * @return The Page's title or "" when the item does not have a title. + */ + const std::string& GetTitle() const; + + /** + * Sets the Page's subtitle. + * The subtitle will be displayed on the navigation title bar when the item is on the top of the stack. + * @param[in] subtitle The Page's subtitle. + */ + void SetSubTitle(const std::string& subtitle); + + /** + * Retrieve the subtitle of the page. + * @return The Page's subtitle or "" when the subtitle does not have a subtitle. + */ + const std::string& GetSubTitle() const; + + /** + * Sets the Page's title icon. + * The icon will be displayed in front of the title on the navigation item bar when the item is on the top. + * @param[in] titleIcon The Page's icon + */ + void SetTitleIcon( Actor titleIcon); + + /** + * Retrieve the title icon of the page. + * @return The Page's icon or an empty handle when the item does not have title icon. + */ + Actor GetTitleIcon() const; + + /** + * Set a control onto the navigation tool bar when the item is on the top. + * Only one control (the last set one) is valid for HorizontalLeft and HorizontalRight each. + * Can have multiple controls for HorizontalCenter. + * If the control is uninitialized or if the alignment has a value other from HorizontalLeft/HorizontalRight/HorizontalCenter, the control is not added. + * @param[in] control The control on the navigation tool bar. + * @param[in] alignment The position of the control, can be HorizontalLeft/HorizontalRight/HorizontalCenter. + * @return true if add control successfully, false if fail to add the control + */ + bool AddControlToToolBar(Actor control, Alignment::Type alignment); + + /** + * Retrieve the controls that would be displayed on the navigation tool bar when the item is on the top. + * @return the vector of tool bar controls associated with the current item. + */ + const ControlOnBarContainer GetControlsOnToolBar() const; + + /** + * Set a control onto the right part of the navigation title bar when the item is on the top. + * If the control is uninitialized or if the alignment has a value other from HorizontalLeft/HorizontalRight/HorizontalCenter, the control is not added. + * @param[in] control The control on the navigation title bar. + * @return true if add control successfully, false if fail to add the control + */ + bool AddControlToTitleBar(Actor control); + + /** + * Retrieve the controls that would be displayed on the navigation title bar when the item is on the top. + * @return the vector of title bar controls associate with the current item. + */ + const ActorContainer GetControlsOnTitleBar() const; + + /** + * Set the menu which would pop up when the KEY_MENU is pressed. + * @param[in] popupMenu the popup menu connected to the KEY_MENU event + */ + void SetPopupMenu( Toolkit::Popup popupMenu ); + + /** + * Get the menu which would pop up when the KEY_MENU is pressed. + * @return The popup menu connected to the KEY_MENU event + */ + Toolkit::Popup GetPopupMenu() const; + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] impl The Page implementation. + */ + Page(Internal::Page& impl); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Page( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_PAGE_H__ */ diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-factory.cpp b/dali-toolkit/public-api/controls/page-turn-view/page-factory.cpp new file mode 100644 index 0000000..d72a762 --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-factory.cpp @@ -0,0 +1,85 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string PageFactory::ACTOR_HITTABLE( "actor-hittable" ); + +PageFactory::PageFactory( ) +: mNeedOffscreenRendering( false ) +{ +} + +PageFactory::~PageFactory() +{ +} + +void PageFactory::EnableOffscreenRendering( ) +{ + mNeedOffscreenRendering = true; +} + +bool PageFactory::IsOffscreenRenderingNeeded() +{ + return mNeedOffscreenRendering; +} + +void PageFactory::SetActorHittability( Actor actor, bool hittable ) +{ + // Create actor focusable property if not already created. + Property::Index propertyActorHittable = actor.GetPropertyIndex(ACTOR_HITTABLE); + if(propertyActorHittable == Property::INVALID_INDEX && hittable) + { + propertyActorHittable = actor.RegisterProperty(ACTOR_HITTABLE, true); + } + else + { + actor.SetProperty( propertyActorHittable, hittable ); + } +} + +bool PageFactory::GetActorHittability( Actor actor ) +{ + bool hittable = false; + + Property::Index propertyActorHittable = actor.GetPropertyIndex(ACTOR_HITTABLE); + if(propertyActorHittable != Property::INVALID_INDEX) + { + hittable = actor.GetProperty( propertyActorHittable ); + } + + return hittable; +} + +PageFactory::RefreshSignal& PageFactory::PageRefreshSignal() +{ + return mPageRefreshSignal; +} + +void PageFactory::EmitPageRefreshSignal( int pageId ) +{ + mPageRefreshSignal.Emit( pageId ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-factory.h b/dali-toolkit/public-api/controls/page-turn-view/page-factory.h new file mode 100644 index 0000000..1e27fbe --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-factory.h @@ -0,0 +1,122 @@ +#ifndef __DALI_TOOLKIT_PAGE_FACTORY_H__ +#define __DALI_TOOLKIT_PAGE_FACTORY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * PageFactory is an abstract interface for providing image actors to PageTurnView + * Each image actor is identified by a unique ID, and has a linear order from 0 to GetNumberOfPages()-1 + */ +class PageFactory +{ +public: + // Property Names + static const std::string ACTOR_HITTABLE; ///< name "actor-hittable", type bool + +public: + + /** + * Constructor + * By default, the off screen rendering is disabled + * Is off screen rendering is required to create the page image, + * call EnableOffscreenRendering() before pass it as parameter to the PageTurnView + */ + PageFactory(); + + /** + * Virtual destructor + */ + virtual ~PageFactory(); + + /** + * Enable the off screen rendering to create the page image from actor tree + */ + void EnableOffscreenRendering( ); + + /** + * Query whether offscreen rendering is needed to create the page image + * @return + */ + bool IsOffscreenRenderingNeeded(); + + /** + * Query the number of pages available from the factory. + * The maximum available page has an ID of GetNumberOfPages()-1. + */ + virtual unsigned int GetNumberOfPages() = 0; + + /** + * Create an actor to represent the page content. + * @param[in] pageId The ID of the page to create. + * @return An actor, or an uninitialized pointer if the ID is out of range. + */ + virtual Actor NewPage( unsigned int pageId ) = 0; + +public: //Signal + /** + * Signal type for notification + */ + typedef SignalV2< void ( int ) > RefreshSignal; + + /** + * Signal emitted when the Actor tree is ready for rendering into the page image. + * The signal is connected to the page refresh function inside PageTurnView. + */ + RefreshSignal& PageRefreshSignal(); + + /** + * Emit the page ready singal. The PageTurn view will be notified to refresh the given page accordingly. + * @param[in] pageId the index of the page which is ready for refreshing. + */ + void EmitPageRefreshSignal( int pageId ); + +protected: + + /** + * Sets whether an actor should be hittable for the PageTurnView::GetHitActor(). + * It is useful when a sub-tree should be hit instead of the 'leaf' actor in the actor tree. + * By default, actors are not hittable for PageTurnView::GetHitActor() + * @param[in] actor The actor to be set with the hittablity + * @param[in] hittable True to be hittable, false otherwise. + */ + void SetActorHittability( Actor actor, bool hittable ); + + /** + * Query whether an actor is hittable for the PageTurnView::GetHitActor(). + */ + bool GetActorHittability( Actor actor ); + +private: + + bool mNeedOffscreenRendering; + + RefreshSignal mPageRefreshSignal; +}; + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_PAGE_FACTORY_H__ */ diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.cpp b/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.cpp new file mode 100644 index 0000000..8eeae67 --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.cpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +PageTurnLandscapeView::PageTurnLandscapeView() +: PageTurnView() +{ +} + +PageTurnLandscapeView::PageTurnLandscapeView( const PageTurnLandscapeView& pageTurnLandscapeView ) +: PageTurnView( pageTurnLandscapeView ) +{ +} + +PageTurnLandscapeView::PageTurnLandscapeView( Internal::PageTurnLandscapeView& implementation ) +: PageTurnView( implementation ) +{ +} + +PageTurnLandscapeView::PageTurnLandscapeView( Dali::Internal::CustomActor* internal ) +: PageTurnView( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +PageTurnLandscapeView& PageTurnLandscapeView::operator=( const PageTurnLandscapeView& pageTurnLandscapeView ) +{ + if( &pageTurnLandscapeView != this) + { + PageTurnView::operator=( pageTurnLandscapeView ); + } + return *this; +} + +PageTurnLandscapeView::~PageTurnLandscapeView() +{ +} + +PageTurnLandscapeView PageTurnLandscapeView::New( PageFactory& pageFactory, const Vector2& pageSize ) +{ + return Internal::PageTurnLandscapeView::New(pageFactory, pageSize); +} + +PageTurnLandscapeView PageTurnLandscapeView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.h b/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.h new file mode 100644 index 0000000..2479044 --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-landscape-view.h @@ -0,0 +1,100 @@ +#ifndef __DALI_TOOLKIT_PAGE_TURN_LANDSCAPE_VIEW_H__ +#define __DALI_TOOLKIT_PAGE_TURN_LANDSCAPE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +//INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class PageTurnLandscapeView; +} + +/** + * PageTurnLandscapeView provides a page turn view in landscape mode + */ +class PageTurnLandscapeView : public PageTurnView +{ +public: + /** + * Create an uninitialized PageTurnLandscapeView; this can be initialized with PageTurnLandscapeView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + PageTurnLandscapeView(); + + /** + * Copy constructor. + */ + PageTurnLandscapeView( const PageTurnLandscapeView& pageTurnLandscapeView ); + + /** + * Assignment operator. + */ + PageTurnLandscapeView& operator=( const PageTurnLandscapeView& pageTurnLandscapeView ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~PageTurnLandscapeView(); + + /** + * Create an initialized PageTurnLandscapeView control + * @param[in] pageFactory The factory which provides PageTurnView with pages. + * @param[in] pageSize The size of the page + * @return A handle to the PageTurnLandscapeView control. + */ + static PageTurnLandscapeView New( PageFactory& pageFactory, const Vector2& pageSize ); + + /** + * Downcast an Object handle to PageTurnPortraitView. If handle points to a PageTurnLandscapeView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a PageTurnLandscapeView or an uninitialized handle + */ + static PageTurnLandscapeView DownCast( BaseHandle handle ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + PageTurnLandscapeView( Internal::PageTurnLandscapeView& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + PageTurnLandscapeView( Dali::Internal::CustomActor* internal ); + + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_PAGE_TURN_LANDSCAPE_VIEW_H__ */ diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.cpp b/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.cpp new file mode 100644 index 0000000..14ac27f --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.cpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +PageTurnPortraitView::PageTurnPortraitView() +: PageTurnView() +{ +} + +PageTurnPortraitView::PageTurnPortraitView( const PageTurnPortraitView& pageTurnPortraitView ) +: PageTurnView( pageTurnPortraitView ) +{ +} + +PageTurnPortraitView::PageTurnPortraitView( Internal::PageTurnPortraitView& implementation ) +: PageTurnView( implementation ) +{ +} + +PageTurnPortraitView::PageTurnPortraitView( Dali::Internal::CustomActor* internal ) +: PageTurnView( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +PageTurnPortraitView& PageTurnPortraitView::operator=( const PageTurnPortraitView& pageTurnPortraitView ) +{ + if( &pageTurnPortraitView != this) + { + PageTurnView::operator=( pageTurnPortraitView ); + } + return *this; +} + +PageTurnPortraitView::~PageTurnPortraitView() +{ +} + +PageTurnPortraitView PageTurnPortraitView::New( PageFactory& pageFactory, const Vector2& pageSize) +{ + return Internal::PageTurnPortraitView::New(pageFactory, pageSize); +} + +PageTurnPortraitView PageTurnPortraitView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.h b/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.h new file mode 100644 index 0000000..5d74cbf --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-portrait-view.h @@ -0,0 +1,98 @@ +#ifndef __DALI_TOOLKIT_PAGE_TURN_PORTRAIT_VIEW_H__ +#define __DALI_TOOLKIT_PAGE_TURN_PORTRAIT_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +//INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class PageTurnPortraitView; +} + +/** + * PageTurnLandscapeView provides a page turn view in portrait mode + */ +class PageTurnPortraitView : public PageTurnView +{ +public: + /** + * Create an uninitialized PageTurnPortraitView; this can be initialized with PageTurnPortraitView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + PageTurnPortraitView(); + + /** + * Copy constructor. + */ + PageTurnPortraitView( const PageTurnPortraitView& pageTurnPortraitView ); + + /** + * Assignment operator. + */ + PageTurnPortraitView& operator=( const PageTurnPortraitView& pageTurnPortraitView ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~PageTurnPortraitView(); + + /** + * Create an initialized PageTurnPortraitView control + * @param[in] pageFactory The factory which provides PageTurnView with pages. + * @param[in] pageSize The size of the page + * @return A handle to the PageTurnPortraitView control. + */ + static PageTurnPortraitView New( PageFactory& pageFactory, const Vector2& pageSize ); + + /** + * Downcast an Object handle to PageTurnPortraitView. If handle points to a PageTurnPortraitView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a PageTurnPortraitView or an uninitialized handle + */ + static PageTurnPortraitView DownCast( BaseHandle handle ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + PageTurnPortraitView( Internal::PageTurnPortraitView& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + PageTurnPortraitView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_PAGE_TURN_PORTRAIT_VIEW_H__ */ diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.cpp b/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.cpp new file mode 100644 index 0000000..7af4f71 --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.cpp @@ -0,0 +1,133 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +PageTurnView::PageTurnView() +{ +} + +PageTurnView::PageTurnView( const PageTurnView& handle ) +: Control( handle ) +{ +} + +PageTurnView& PageTurnView::operator=( const PageTurnView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +PageTurnView::~PageTurnView() +{ +} + +PageTurnView PageTurnView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +PageTurnView::PageTurnView( Internal::PageTurnView& implementation ) +: Control( implementation ) +{ +} + +PageTurnView::PageTurnView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter ) +{ + Toolkit::GetImplementation( *this ).SetSpineShadowParameter( spineShadowParameter ); +} + +Vector2 PageTurnView::GetSpineShadowParameter() +{ + return Toolkit::GetImplementation( *this ).GetSpineShadowParameter(); +} + +void PageTurnView::GoToPage( unsigned int pageId ) +{ + Toolkit::GetImplementation( *this ).GoToPage(pageId); +} + +unsigned int PageTurnView::GetCurrentPage() +{ + return Toolkit::GetImplementation( *this ).GetCurrentPage(); +} + +PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal() +{ + return Toolkit::GetImplementation( *this ).PageTurnStartedSignal(); +} + +PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal() +{ + return Toolkit::GetImplementation( *this ).PageTurnFinishedSignal(); +} + +PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal() +{ + return Toolkit::GetImplementation( *this ).PagePanStartedSignal(); +} + +PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal() +{ + return Toolkit::GetImplementation( *this ).PagePanFinishedSignal(); +} + +Actor PageTurnView::EnterEditMode() +{ + return Toolkit::GetImplementation( *this ).EnterEditMode(); +} + +void PageTurnView::LeaveEditMode() +{ + Toolkit::GetImplementation( *this ).LeaveEditMode(); +} + +Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates ) +{ + return Toolkit::GetImplementation( *this ).GetHitActor( screenCoordinates, actorCoordinates ); +} + +void PageTurnView::RefreshAll() +{ + Toolkit::GetImplementation( *this ).RefreshAll(); +} + +void PageTurnView::RefreshCurrentPage() +{ + Toolkit::GetImplementation( *this ).RefreshCurrentPage(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.h b/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.h new file mode 100644 index 0000000..91aa276 --- /dev/null +++ b/dali-toolkit/public-api/controls/page-turn-view/page-turn-view.h @@ -0,0 +1,218 @@ +#ifndef __DALI_TOOLKIT_PAGE_TURN_VIEW_H__ +#define __DALI_TOOLKIT_PAGE_TURN_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +// Forward declarations +class PageFactory; + +namespace Internal DALI_INTERNAL +{ +class PageTurnView; +} + +/** + * PageTurnView is a base class of different mode of pageTurnViews ( portrait or landscape ) + * Page actors are provided from an external PageFactory + * PanGesture is used to activate the page bending, streching and tuning forward/backward + * + * Signal usage: There are four signals. Two matching pairs for panning and page turning: + * PagePanStarted/PagePanFinished and PageTurnStarted/PageTurnFinished. Panning relates to user interaction with + * the screen while page turning refers to animation of the page. There are three scenarios for these + * events: normal page turn (forwards or backwards), aborted page turn (forwards or backwards) + * and pan with no animation. The order of events is as follows: + * 1) Normal page turn: PagePanStarted -> PageTurnStarted direction -> PagePanFinished -> PageTurnFinished direction + * 2) Aborted page turn: PagePanStarted -> PageTurnStarted direction -> PageTurnStarted opposite direction + * -> PagePanFinished -> PageTurnFinished opposite direction + * 3) Pan with no animation: PagePanStarted -> PagePanFinished + * Pan with no animation will occur when the user touches the page in an area that does not start the + * page turning. + */ +class PageTurnView : public Control +{ +public: + + /** + * Creates an empty PageTurnView handle. Only derived versions can be instantiated. + * Calling member function with an uninitialized handle is not allowed. + */ + PageTurnView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param[in] handle Handle to copy from + */ + PageTurnView( const PageTurnView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + PageTurnView& operator=( const PageTurnView& handle ); + + /** + * Virtual destructor. + */ + virtual ~PageTurnView(); + + /** + * Downcast an Object handle to PageTurnView. + * If handle points to an PageTurnView the downcast produces valid handle. + * If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a PageTurnView or an uninitialized handle + */ + static PageTurnView DownCast( BaseHandle handle ); + + /** + * Set the spine shadow parameter to the shader effects + * The two parameters are the major&minor radius (in pixels) to form an ellipse shape + * The top-left quarter of this ellipse is used to calculate spine normal for simulating shadow + * @param [in] spineShadowParameter The major&minor ellipse radius for the simulated spine shadow + */ + void SetSpineShadowParameter( const Vector2& spineShadowParameter ); + + /** + * Retrieve the spine shadow parameter of the shader effects + * @return The major&minor ellipse radius for the simulated spine shadow + */ + Vector2 GetSpineShadowParameter(); + + /* + * Go to a specific page + * @param[in] pageId The new current page index + */ + void GoToPage( unsigned int pageId ); + + /** + * Retrieve the index of the current Page + * @return The index of the current page + */ + unsigned int GetCurrentPage(); + + /** + * Enter edit mode + * Case 1, the page factory passes image actor into the view as page content, do nothing. + * Case 2, the page factory passes an actor tree into the view as page content, + * the actor tree will receive the touch event in edit mode, and set the refresh rate of the off screen render task to always + * @return an empty actor in case 1; the actor tree root of the current page + */ + Actor EnterEditMode(); + + /** + * Leave edit mode + * Case 1, the page factory passes image actor into the view as page content, do nothing. + * Case 2, the page factory passes an actor tree into the view as page content, + * the page actor will receive all the touch event, and set the refresh rage of the off screen render task to once. + */ + void LeaveEditMode(); + + /** + * Return the actor get hit in the actor tree of the current page by given the touch position on the PageTurnView + * @param[in] screenCoordinates The hit position of the PageTurnView + * @param[out] actorCoordinates The local hit position of the hitted actor + * @return the hitted actor + */ + Actor GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates ); + + /** + * Refresh all the cached pages by calling the render task to refresh. + */ + void RefreshAll(); + + /** + * Refresh current page by calling the render task to refresh + */ + void RefreshCurrentPage(); + +public: //Signal + + // Page Turned signal, with page index and boolean turning direction (true = forward, false = backward) + typedef SignalV2< void ( PageTurnView, unsigned int, bool ) > PageTurnSignal; + typedef SignalV2< void ( PageTurnView ) > PagePanSignal; + + /** + * Signal emitted when a page has started to turn over. + * A callback of the following type may be connected: + * @code + * void YourCallBackName( PageTurnView pageTurnView, unsigned int pageIndex, bool isTurningForward ); + * @endcode + * @return The signal to connect to + */ + PageTurnSignal& PageTurnStartedSignal(); + + /** + * Signal emitted when a page has finished turning over. + * A callback of the following type may be connected: + * @code + * void YourCallBackName( PageTurnView pageTurnView, unsigned int pageIndex, bool isTurningForward ); + * @endcode + * @return The signal to connect to + */ + PageTurnSignal& PageTurnFinishedSignal(); + + /** + * Signal emitted when a page pan has commenced + * A callback of the following type may be connected: + * @code + * void YourCallBackName( PageTurnView pageTurnView ); + * @endcode + * @return The signal to connect to + */ + PagePanSignal& PagePanStartedSignal(); + + /** + * Signal emitted when a page pan has finished + * A callback of the following type may be connected: + * @code + * void YourCallBackName( PageTurnView pageTurnView ); + * @endcode + * @return The signal to connect to + */ + PagePanSignal& PagePanFinishedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + DALI_INTERNAL PageTurnView(Internal::PageTurnView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + DALI_INTERNAL PageTurnView(Dali::Internal::CustomActor* internal); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_PAGE_TURN_VIEW_H__ */ diff --git a/dali-toolkit/public-api/controls/popup/popup.cpp b/dali-toolkit/public-api/controls/popup/popup.cpp new file mode 100644 index 0000000..735e64f --- /dev/null +++ b/dali-toolkit/public-api/controls/popup/popup.cpp @@ -0,0 +1,153 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Popup +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const char* const Popup::SIGNAL_TOUCHED_OUTSIDE = "touched-outside"; +const char* const Popup::SIGNAL_HIDDEN = "hidden"; +const char* const Popup::PROPERTY_TITLE = "title"; +const char* const Popup::PROPERTY_STATE = "state"; + +Popup::Popup() +{ +} + +Popup::Popup( const Popup& handle ) +: Control( handle ) +{ +} + +Popup& Popup::operator=( const Popup& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +Popup::Popup(Internal::Popup& implementation) +: Control(implementation) +{ +} + +Popup::Popup( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +Popup Popup::New() +{ + return Internal::Popup::New(); +} + +Popup::~Popup() +{ +} + +Popup Popup::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void Popup::SetBackgroundImage( Actor image ) +{ + GetImpl(*this).SetBackgroundImage( image ); +} + +void Popup::SetTitle( const std::string& text ) +{ + GetImpl(*this).SetTitle( text ); +} + +void Popup::SetTitle( TextView titleActor ) +{ + GetImpl(*this).SetTitle( titleActor ); +} + +TextView Popup::GetTitle() const +{ + return GetImpl(*this).GetTitle(); +} + +void Popup::AddButton( Button button ) +{ + GetImpl(*this).AddButton( button ); +} + +void Popup::SetState( PopupState state ) +{ + GetImpl(*this).SetState( state ); +} + +void Popup::SetState( PopupState state, float duration ) +{ + GetImpl(*this).SetState( state, duration ); +} + +Popup::PopupState Popup::GetState() const +{ + return GetImpl(*this).GetState(); +} + +void Popup::Show() +{ + GetImpl(*this).SetState( POPUP_SHOW ); +} + +void Popup::Hide() +{ + GetImpl(*this).SetState( POPUP_HIDE ); +} + +void Popup::ShowTail(const Vector3& position) +{ + GetImpl(*this).ShowTail( position ); +} + +void Popup::HideTail() +{ + GetImpl(*this).HideTail(); +} + +Popup::TouchedOutsideSignalV2& Popup::OutsideTouchedSignal() +{ + return GetImpl(*this).OutsideTouchedSignal(); +} + +Popup::HiddenSignalV2& Popup::HiddenSignal() +{ + return GetImpl(*this).HiddenSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scroll-component/scroll-bar.cpp b/dali-toolkit/public-api/controls/scroll-component/scroll-bar.cpp new file mode 100755 index 0000000..226c2ba --- /dev/null +++ b/dali-toolkit/public-api/controls/scroll-component/scroll-bar.cpp @@ -0,0 +1,81 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollBar::ScrollBar() +{ +} + +ScrollBar::ScrollBar(Internal::ScrollBar& implementation) +: ScrollComponent(implementation) +{ +} + +ScrollBar::ScrollBar( Dali::Internal::CustomActor* internal ) +: ScrollComponent( internal ) +{ + VerifyCustomActorPointer(internal); +} + +ScrollBar::ScrollBar( const ScrollBar& scrollBar ) +: ScrollComponent(scrollBar) +{ +} + +ScrollBar& ScrollBar::operator=( const ScrollBar& scrollBar ) +{ + if( &scrollBar != this ) + { + Control::operator=( scrollBar ); + } + return *this; +} + +ScrollBar ScrollBar::New(Scrollable& container, bool vertical) +{ + return Internal::ScrollBar::New(container, vertical); +} + +ScrollBar ScrollBar::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +ScrollBar::~ScrollBar() +{ +} + +void ScrollBar::Show() +{ + GetImpl(*this).Show(); +} + +void ScrollBar::Hide() +{ + GetImpl(*this).Hide(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scroll-component/scroll-component.cpp b/dali-toolkit/public-api/controls/scroll-component/scroll-component.cpp new file mode 100644 index 0000000..c595bba --- /dev/null +++ b/dali-toolkit/public-api/controls/scroll-component/scroll-component.cpp @@ -0,0 +1,66 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollComponent::ScrollComponent() +{ +} + +ScrollComponent::ScrollComponent(Internal::ScrollComponent& implementation) +: Control(implementation) +{ +} + +ScrollComponent::ScrollComponent( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +ScrollComponent::ScrollComponent( const ScrollComponent& scrollComponentNew ) +: Control(scrollComponentNew) +{ +} + +ScrollComponent& ScrollComponent::operator=( const ScrollComponent& scrollComponentNew ) +{ + if( &scrollComponentNew != this ) + { + Control::operator=( scrollComponentNew ); + } + return *this; +} + +ScrollComponent ScrollComponent::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +ScrollComponent::~ScrollComponent() +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.cpp new file mode 100755 index 0000000..a6c7cec --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.cpp @@ -0,0 +1,1393 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.005f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 3.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.25f; + +const float SELECTED_RIGHT = 2.5f; +const float SELECTED_LEFT = 3.5f; +const float SELECTED_CENTER = 3.0f; + +const float LAYOUT_POSITION_NAGATIVE_1 = -1.0f; +const float LAYOUT_POSITION_0 = 0.0f; +const float LAYOUT_POSITION_2 = 2.0f; +const float LAYOUT_POSITION_3 = 3.0f; +const float LAYOUT_POSITION_4 = 4.0f; +const float LAYOUT_POSITION_6 = 6.0f; +const float LAYOUT_POSITION_7 = 7.0f; + +const Vector3 POSITION_0 = Vector3(850.0f,-250.0f,0.0f); +const Vector3 POSITION_1 = Vector3(700.0f,50.0f,0.0f); +const Vector3 POSITION_2 = Vector3(440.0f,227.0f,0.0f); +const Vector3 POSITION_4 = Vector3(-440.0f,227.0f,0.0f); +const Vector3 POSITION_5 = Vector3(-700.0f,50.0f,0.0f); +const Vector3 POSITION_6 = Vector3(-850.0f,-250.0f,0.0f); + +const float ROTATION_0 = Math::PI/6.0f; +const float ROTATION_1 = 0.0f; +const float ROTATION_2 = Math::PI/6.0f; +const float ROTATION_4 = -Math::PI/6.0f; +const float ROTATION_5 = 0.0f; +const float ROTATION_6 = -Math::PI/6.0f; + +const float SCALE = 1.0f; + +const Vector2 COLOR = Vector2(1.0f,1.0f); + +const Vector3 SELECTED_ITEM_POSITION = Vector3( 0.0f,-80.0f,140.0f); +const float SELECETED_ITEM_SCALE = 1.72f; +const Vector2 SELECTED_ITEM_COLOR = Vector2(1.0f,1.0f); +const Vector3 VIRTUAL_ITEM_POSITION_RIGHT = Vector3( 280.0f,130.0f,130.0f); +const Vector3 VIRTUAL_ITEM_POSITION_LEFT = Vector3( -280.0f,130.0f,130.0f); +const float ROTATION_X = Math::PI/4.0f; +const float SCALE_RIGHT = 1.0f; +const float SCALE_LEFT = 1.0f; +const Vector2 COLOR_RIGHT = Vector2(1.0f,1.0f); +const Vector2 COLOR_LEFT = Vector2(1.0f,1.0f); +const Vector3 POSITION_RIGHT = Vector3(710.0f,-450.0f,0.0f); +const Vector3 POSITION_LEFT = Vector3(-710.0f,-450.0f,0.0f); +const float ROTATION_RIGHT = -Math::PI / 6.0f; +const float ROTATION_LEFT = Math::PI / 6.0f; + +const float ALBUM_HIGHT = 7.0f; +const float ALPHA = 1.1f; +const float ALPHA_OF_SIZE = 0.35f; +const float LINE_OF_BOTTOM = 360.0f; + +const float CHANCE_OF_RANDOM_ROTATION_OF_STACK = 0.5f; +const float RANGE_OF_RANDOM_ROTATION_OF_STACK = 0.5f; + +const float THRESHHOLD_OF_MOVING = 0.02f; +const int NUM_OF_FRAME_FILTERED = 5; + +const unsigned int SPREAD_ITEM_NUM = 6; + +typedef enum +{ + SCROLL_LEFT = 1, + SCROLL_NONE = 0, + SCROLL_RIGHT = -1 +}ScrollDirection; + +struct DefaultItemSizeFunction +{ + Vector3 operator()(const Vector3& layoutSize) + { + float width = layoutSize.height * ALPHA_OF_SIZE; + return Vector3(width, width, width); + } +}; + +struct AlbumScaleConstraint +{ + AlbumScaleConstraint(const float scaleRight, const float scaleLeft, const float selectedItemScale, vector scaleSpread) + { + mScaleRight = scaleRight; + mScaleLeft = scaleLeft; + mSelectedItemScale = selectedItemScale; + + if(scaleSpread.size() == SPREAD_ITEM_NUM) + { + mScaleVecSpread = scaleSpread; + } + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + int begin = 0; + int end = 0; + float beginScale = 0.0f; + float endScale = 0.0f; + float scale = 0.0f; + float pos = layoutPosition + SELECTED_CENTER; + + if(pos <= LAYOUT_POSITION_NAGATIVE_1)/*items of right stack*/ + { + scale = mScaleRight; + } + else if(pos > LAYOUT_POSITION_NAGATIVE_1 && pos < LAYOUT_POSITION_0)/*items between -1.0f and 0.0f*/ + { + beginScale = mScaleVecSpread.at(0); + endScale = mScaleRight; + + scale = (endScale - beginScale) * fabs(pos) + beginScale; + } + else if(pos >= LAYOUT_POSITION_0 && pos < SELECTED_RIGHT)/*items between 0.0f and center*/ + { + if(int(pos) < pos) + { + begin = int(pos); + end = int(pos) + 1; + + beginScale = mScaleVecSpread.at(begin); + endScale = mScaleVecSpread.at(end); + + scale = (endScale - beginScale) * fabs(pos - int(pos)) + beginScale; + } + else + { + scale = mScaleVecSpread.at(int(pos)); + } + } + else if(pos >= SELECTED_RIGHT && pos <= SELECTED_LEFT)/*items of center*/ + { + scale = mSelectedItemScale; + } + else if(pos > SELECTED_LEFT && pos <= LAYOUT_POSITION_6)/*items between center and 6.0f*/ + { + if(int(pos) < pos) + { + begin = int(pos)-1; + end = int(pos); + + beginScale = mScaleVecSpread.at(begin); + endScale = mScaleVecSpread.at(end); + + scale = (endScale - beginScale) * fabs(pos - int(pos)) + beginScale; + } + else + { + scale = mScaleVecSpread.at(int(pos)-1); + } + } + else if(pos > LAYOUT_POSITION_6 && pos < LAYOUT_POSITION_7)/*items between 6.0f and 7.0f*/ + { + beginScale = mScaleVecSpread.at(5); + endScale = mScaleLeft; + + scale = (endScale - beginScale) * fabs(pos - int(pos)) + beginScale; + } + else if(pos >= LAYOUT_POSITION_7)/*items of left stack*/ + { + scale = mScaleLeft; + } + else + { + scale = 1.0f; + } + + return Vector3(scale,scale,1.0f); + } + + float mScaleRight; + float mScaleLeft; + float mSelectedItemScale; + vector mScaleVecSpread; +}; + +Vector3 CalculatePosition(Vector3 pos, float rotateX) +{ + pos.z = pos.z - fabs(pos.y - LINE_OF_BOTTOM) * sinf(rotateX) / cosf(rotateX); + return pos; +} + +Vector3 CalculateStackPosition(Vector3 pos, float rotateX, int num, bool left) +{ + pos.z = pos.z - fabs(pos.y - LINE_OF_BOTTOM) * sinf(rotateX) / cosf(rotateX); + + if(left) + { + pos.x = pos.x + num * ALPHA; + } + else + { + pos.x = pos.x - num * ALPHA; + } + + pos.y -= num * ALBUM_HIGHT * sinf(rotateX); + pos.z += num * ALBUM_HIGHT * cosf(rotateX); + + return pos; +} + +Vector3 GetPosition(float layoutPos, Vector3 posRight, Vector3 posLeft, Vector3 posSelectedItem, vector posVecSpread, float rotateX) +{ + int begin =0; + int end = 0; + + float alpha = 0.0f; + + Vector3 beginPos = Vector3::ZERO; + Vector3 endPos = Vector3::ZERO; + + Vector3 pos = Vector3::ZERO; + + if(layoutPos <= LAYOUT_POSITION_NAGATIVE_1)/*items of right stack*/ + { + if(int(layoutPos) > layoutPos) + { + begin = int(layoutPos); + end = int(layoutPos)-1; + + beginPos = CalculateStackPosition(posRight, rotateX, int(LAYOUT_POSITION_NAGATIVE_1) - int(layoutPos),false); + + endPos = CalculateStackPosition(posRight, rotateX, - int(layoutPos),false); + + alpha = fabs(layoutPos - int(layoutPos)); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + + return pos; + } + else + { + return CalculateStackPosition(posRight,rotateX,int(LAYOUT_POSITION_NAGATIVE_1) - int(layoutPos),false); + } + } + else if(layoutPos < LAYOUT_POSITION_0 && layoutPos > LAYOUT_POSITION_NAGATIVE_1)/*items between -1.0f and 0.0f*/ + { + beginPos = CalculatePosition(posVecSpread.at(int(LAYOUT_POSITION_0)),rotateX); + endPos = CalculateStackPosition(posRight, rotateX, int(layoutPos),false); + + alpha = -layoutPos; + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + + return pos; + } + else if(layoutPos >= LAYOUT_POSITION_0 && layoutPos <= LAYOUT_POSITION_2)/*items between 0.0f and 2.0f*/ + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos); + end = int(layoutPos) + 1; + + beginPos = posVecSpread.at(begin); + endPos = posVecSpread.at(end); + + alpha = fabs(layoutPos - int(layoutPos)); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + } + else + { + pos = posVecSpread.at(int(layoutPos)); + } + } + else if(layoutPos >LAYOUT_POSITION_2 && layoutPos SELECTED_LEFT && layoutPos < LAYOUT_POSITION_4)/*items between center and 4.0f*/ + { + beginPos = posVecSpread.at(int(LAYOUT_POSITION_3)); + endPos = VIRTUAL_ITEM_POSITION_LEFT; + + alpha = (LAYOUT_POSITION_4 - layoutPos) / (LAYOUT_POSITION_4 - SELECTED_LEFT); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + } + else if(layoutPos >= LAYOUT_POSITION_4 && layoutPos <= LAYOUT_POSITION_6)/*items between 4.0f and 6.0f*/ + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos); + end = int(layoutPos) + 1; + + beginPos = posVecSpread.at(begin - 1); + endPos = posVecSpread.at(end - 1); + + alpha = fabs(layoutPos - int(layoutPos)); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + } + else + { + pos = posVecSpread.at(int(layoutPos) -1); + } + } + else if(layoutPos > LAYOUT_POSITION_6 && layoutPos < LAYOUT_POSITION_7)/*items between 6.0f and 7.0f*/ + { + beginPos = CalculatePosition(posVecSpread.at(int(LAYOUT_POSITION_6) - 1),rotateX); + endPos = CalculateStackPosition(posLeft, rotateX, int(layoutPos) + 1 - int(LAYOUT_POSITION_7),true); + + alpha = fabs(layoutPos - int(layoutPos)); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + + return pos; + } + else if(layoutPos >= LAYOUT_POSITION_7)/*items of left stack*/ + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos); + end = int(layoutPos) + 1; + + beginPos = CalculateStackPosition(posLeft, rotateX, int(layoutPos) - int(LAYOUT_POSITION_7),true); + + endPos = CalculateStackPosition(posLeft, rotateX, int(layoutPos) + 1 - int(LAYOUT_POSITION_7),true); + + alpha = fabs(layoutPos - int(layoutPos)); + + pos.x = (endPos.x - beginPos.x) * alpha + beginPos.x; + pos.y = (endPos.y - beginPos.y) * alpha + beginPos.y; + pos.z = (endPos.z - beginPos.z) * alpha + beginPos.z; + + return pos; + } + else + { + return CalculateStackPosition(posLeft, rotateX, int(layoutPos) - int(LAYOUT_POSITION_7),true); + } + } + + return CalculatePosition(pos,rotateX); +} + +struct AlbumPositionConstraint +{ + AlbumPositionConstraint(const Vector3 posRight, const Vector3 posLeft, const Vector3 posSelectedItem, const vector posVecSpread, float rotateX) + { + mPositionRight = posRight; + mPositionLeft = posLeft; + mSelectedItemPosition = posSelectedItem; + mRotationX = rotateX; + + if(posVecSpread.size() == SPREAD_ITEM_NUM) + { + mPositionVecSpread = posVecSpread; + } + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float pos = layoutPosition + SELECTED_CENTER; + + /*handle if the item is selected item(in the center)*/ + if(pos >= SELECTED_RIGHT && pos <= SELECTED_LEFT) + { + return SELECTED_ITEM_POSITION; + } + + /*get the spread items position*/ + return GetPosition(pos,mPositionRight,mPositionLeft,mSelectedItemPosition,mPositionVecSpread,mRotationX); + } + + Vector3 mPositionRight; + Vector3 mPositionLeft; + Vector3 mSelectedItemPosition; + vector mPositionVecSpread; + float mRotationX; + }; + +Quaternion GetRotation(float layoutPos, vector rotateVecStack, vector rotateVecSpread, float rotateX) +{ + int begin =0; + int end = 0; + + float alpha = 0.0f; + + float beginRotation = 0.0f; + float endRotation = 0.0f; + + float rotation = 0.0f; + if(layoutPos <= LAYOUT_POSITION_NAGATIVE_1)/*items of right stack*/ + { + if(int(layoutPos) > layoutPos) + { + begin = int(layoutPos) + 1; + end = int(layoutPos); + + begin *= -1; + end *= -1; + + beginRotation = rotateVecStack.at(begin); + endRotation = rotateVecStack.at(end); + + alpha = fabs(layoutPos - int(layoutPos)); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else + { + rotation = rotateVecStack.at(-int(layoutPos)-1); + } + } + else if(layoutPos > LAYOUT_POSITION_NAGATIVE_1 && layoutPos < LAYOUT_POSITION_0)/*items between -1.0f and 0.0f*/ + { + begin = 0; + end = 0; + + beginRotation = rotateVecSpread.at(begin); + endRotation = rotateVecStack.at(end); + + alpha = fabs(layoutPos); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else if(layoutPos >= LAYOUT_POSITION_0 && layoutPos < LAYOUT_POSITION_3) + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos); + end = int(layoutPos) + 1; + + beginRotation = rotateVecSpread.at(begin); + endRotation = rotateVecSpread.at(end); + + alpha = fabs(layoutPos - int(layoutPos)); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else + { + rotation = rotateVecSpread.at(int(layoutPos)); + } + } + else if(layoutPos > LAYOUT_POSITION_3 && layoutPos <= LAYOUT_POSITION_6) + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos) - 1; + end = int(layoutPos); + + beginRotation = rotateVecSpread.at(begin); + endRotation = rotateVecSpread.at(end); + + alpha = fabs(layoutPos - int(layoutPos)); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else + { + rotation = rotateVecSpread.at(int(layoutPos)-1); + } + } + else if(layoutPos > LAYOUT_POSITION_6 && layoutPos < LAYOUT_POSITION_7) + { + begin = 5; + end = 0; + + beginRotation = rotateVecSpread.at(begin); + endRotation = rotateVecStack.at(end); + + alpha = fabs(layoutPos - int(LAYOUT_POSITION_6)); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else if(layoutPos >= LAYOUT_POSITION_7) + { + if(int(layoutPos) < layoutPos) + { + begin = int(layoutPos) - int(LAYOUT_POSITION_7); + end = int(layoutPos) - int(LAYOUT_POSITION_7) + 1; + + beginRotation = rotateVecStack.at(begin); + endRotation = rotateVecStack.at(end); + + alpha = fabs(layoutPos - int(layoutPos)); + + rotation = (endRotation - beginRotation) * alpha + beginRotation; + } + else + { + rotation = rotateVecStack.at(int(layoutPos)-int(LAYOUT_POSITION_7)); + } + } + + return Quaternion(rotateX, Vector3::XAXIS) * Quaternion(rotation, Vector3::ZAXIS); +} + +struct AlbumRotationConstraint +{ + AlbumRotationConstraint(const vector rotatVecSpread, const vector rotatVecStack, const float rotateX, int num) + { + mRotationX = rotateX; + mNumOfItems = num; + mIndex = 0; + mLastIndex = 0; + mLeft = SCROLL_NONE; + mLastPosition = 0.0f; + mTimes = 0; + + if(rotatVecSpread.size() == SPREAD_ITEM_NUM) + { + mRotationVecSpread = rotatVecSpread; + } + + mRotationVecStack = rotatVecStack; + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float pos = layoutPosition + SELECTED_CENTER; + + if(mIndex == mNumOfItems) + { + mIndex = 0; + } + + mIndex ++; + + if(mLastIndex != mIndex) + { + mLastIndex = mIndex; + + if(mLeft == SCROLL_RIGHT) + { + if(pos > mLastPosition + THRESHHOLD_OF_MOVING) + { + mTimes = 0; + mLeft = SCROLL_LEFT; + } + else if(pos < mLastPosition) + { + mTimes = 0; + mLeft = SCROLL_RIGHT; + } + else + { + mTimes++; + if(mTimes > NUM_OF_FRAME_FILTERED) + { + mTimes = 0; + mLeft = SCROLL_NONE; + } + } + } + else if(mLeft == SCROLL_LEFT) + { + if(pos > mLastPosition) + { + mTimes = 0; + mLeft = SCROLL_LEFT; + } + else if(pos < mLastPosition - THRESHHOLD_OF_MOVING) + { + mTimes = 0; + mLeft = SCROLL_RIGHT; + } + else + { + mTimes++; + if(mTimes > NUM_OF_FRAME_FILTERED) + { + mTimes = 0; + mLeft = SCROLL_NONE; + } + } + } + else + { + if(pos < mLastPosition) + { + mLeft = SCROLL_RIGHT; + } + else if(pos > mLastPosition) + { + mLeft = SCROLL_LEFT; + } + else + { + mLeft = SCROLL_NONE; + } + } + + mLastPosition = pos; + + /*rotation for the selected item(center)*/ + if(pos >= SELECTED_RIGHT && pos < SELECTED_LEFT) + { + if(mLeft == SCROLL_LEFT) + { + return Quaternion(-fabs(SELECTED_CENTER - pos), Vector3::YAXIS); + } + else if(mLeft == SCROLL_RIGHT) + { + return Quaternion(fabs(pos - SELECTED_CENTER), Vector3::YAXIS); + } + else + { + return Quaternion(0.0f, Vector3::YAXIS); + } + } + } + + /*rotation for the spread item*/ + return GetRotation(pos,mRotationVecStack,mRotationVecSpread,mRotationX); + } + + vector mRotationVecSpread; + vector mRotationVecStack; + float mRotationX; + Actor mScrollDirectionActor; + int mLastIndex; + int mIndex; + int mNumOfItems; + int mTimes; + ScrollDirection mLeft; + float mLastPosition; +}; + +struct AlbumColorConstraint +{ + AlbumColorConstraint(const Vector2 colorRight, const Vector2 colorLeft, const Vector2 colorSelectedItem, const vector spreadVecColor, const int stackNum) + { + mColorRight = colorRight; + mColorLeft = colorLeft; + mSelectedItemColor = colorSelectedItem; + mStackNum = stackNum; + + if(spreadVecColor.size() == SPREAD_ITEM_NUM) + { + mColorVecSpread = spreadVecColor; + } + } + + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float black = 1.0f; + Vector4 color = current; + float pos = layoutPosition + SELECTED_CENTER; + Vector2 temp = Vector2(0.0f,0.0f); + + int begin = 0; + int end = 0; + + Vector2 beginColor = Vector2(0.0f,0.0f); + Vector2 endColor = Vector2(0.0f,0.0f); + + if(pos <= -mStackNum-1) + { + color.w = 0.0f; + black = 0.0f; + } + else if(pos > -mStackNum-1 && pos < -mStackNum) + { + beginColor = mColorRight; + endColor = Vector2(0.0f,0.0f); + + color.w = (endColor.x - beginColor.x) * fabs(pos - int(pos)) + beginColor.x; + black = (endColor.y - beginColor.y) * fabs(pos - int(pos)) + beginColor.y; + } + else if(pos <= LAYOUT_POSITION_NAGATIVE_1 && pos >= -mStackNum) + { + color.w = mColorRight.x; + black = mColorRight.y; + } + else if(pos > LAYOUT_POSITION_NAGATIVE_1 && pos < LAYOUT_POSITION_0) + { + beginColor = mColorVecSpread.at(int(LAYOUT_POSITION_0)); + endColor = mColorRight; + + color.w = (endColor.x - beginColor.x) * fabs(pos) + beginColor.x; + black = (endColor.y - beginColor.y) * fabs(pos) + beginColor.y; + } + else if(pos >= LAYOUT_POSITION_0 && pos <= LAYOUT_POSITION_2) + { + if(int(pos) < pos) + { + begin = int(pos); + end = int(pos) + 1; + + beginColor = mColorVecSpread.at(begin); + endColor = mColorVecSpread.at(end); + + temp = (endColor - beginColor) * fabs(pos - int(pos)) + beginColor; + + color.w = temp.x; + black = temp.y; + } + else + { + beginColor = mColorVecSpread.at(int(pos)); + + color.w = beginColor.x; + black = beginColor.y; + } + } + else if(pos > LAYOUT_POSITION_2 && pos < SELECTED_RIGHT)/*items between 2.0f and center*/ + { + beginColor = mColorVecSpread.at(int(LAYOUT_POSITION_2)); + endColor = Vector2(0.0f,0.0f); + + temp = (endColor - beginColor) * (pos - LAYOUT_POSITION_2)/(SELECTED_RIGHT - LAYOUT_POSITION_2) + beginColor; + + color.w = temp.x; + black = temp.y; + } + else if(pos >= SELECTED_RIGHT && pos <= SELECTED_LEFT)/*items of center*/ + { + color.w = mSelectedItemColor.x; + black = mSelectedItemColor.y; + } + else if(pos > SELECTED_LEFT && pos < LAYOUT_POSITION_4)/*items between center and 4.0f*/ + { + beginColor = Vector2(0.0f,0.0f); + endColor = mColorVecSpread.at(int(LAYOUT_POSITION_3)); + + temp = (endColor - beginColor) * (pos - SELECTED_LEFT)/(LAYOUT_POSITION_4 - SELECTED_LEFT) + beginColor; + + color.w = temp.x; + black = temp.y; + } + else if(pos >= LAYOUT_POSITION_4 && pos <= LAYOUT_POSITION_6)/*items between 4.0f and 6.0f*/ + { + if(int(pos) < pos) + { + begin = int(pos) - 1; + end = int(pos); + + beginColor = mColorVecSpread.at(begin); + endColor = mColorVecSpread.at(end); + + temp = (endColor - beginColor) * fabs(pos - int(pos)) + beginColor; + + color.w = temp.x; + black = temp.y; + } + else + { + beginColor = mColorVecSpread.at(int(pos) - 1); + + color.w = beginColor.x; + black = beginColor.y; + } + } + else if(pos > LAYOUT_POSITION_6 && pos < LAYOUT_POSITION_7)/*items between 6.0f and 7.0f*/ + { + beginColor = mColorVecSpread.at(int(LAYOUT_POSITION_6) - 1); + endColor = mColorLeft; + + color.w = (endColor.x - beginColor.x) * fabs(pos - int(pos)) + beginColor.x; + black = (endColor.y - beginColor.y) * fabs(pos - int(pos)) + beginColor.y; + } + else if(pos >= LAYOUT_POSITION_7 && pos <= mStackNum + int(LAYOUT_POSITION_7))/*items of left stack*/ + { + color.w = mColorLeft.x; + black = mColorLeft.y; + } + else if(pos > mStackNum + int(LAYOUT_POSITION_7) && pos < mStackNum + int(LAYOUT_POSITION_7) + 1) + { + beginColor = mColorLeft; + endColor = Vector2(0.0f,0.0f); + + color.w = (endColor.x - beginColor.x) * fabs(pos - int(pos)) + beginColor.x; + black = (endColor.y - beginColor.y) * fabs(pos - int(pos)) + beginColor.y; + } + else if(pos >= mStackNum + int(LAYOUT_POSITION_7) +1) + { + color.w = 0.0f; + black = 0.0f; + } + + color.r = color.r * black; + color.g = color.g * black; + color.b = color.b * black; + + return color; + } + + int mStackNum; + Vector2 mColorRight; + Vector2 mColorLeft; + Vector2 mSelectedItemColor; + vector mColorVecSpread; +}; + +struct AlbumVisibilityConstraintPortrait +{ + AlbumVisibilityConstraintPortrait() + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return true; + } +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +struct AlbumLayout::Impl +{ + Impl() + : mItemSizeFunction(DefaultItemSizeFunction()), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION), + mNumOfItems(0) + { + /*Initialize the variables*/ + mSelectedItemScale = SELECETED_ITEM_SCALE; + mRotationX = ROTATION_X; + mScaleRight = SCALE_RIGHT; + mScaleLeft = SCALE_LEFT; + mRotationRight = ROTATION_RIGHT; + mRotationLeft = ROTATION_LEFT; + mSelectedItemColor = SELECTED_ITEM_COLOR; + mSelectedItemPosition = SELECTED_ITEM_POSITION; + mColorRight = COLOR_RIGHT; + mColorLeft = COLOR_LEFT; + mPositionRight = POSITION_RIGHT; + mPositionLeft = POSITION_LEFT; + mStackNum = 50; + + /*Initialize the position of spread items*/ + mPositionVecSpread.push_back(POSITION_0); + mPositionVecSpread.push_back(POSITION_1); + mPositionVecSpread.push_back(POSITION_2); + mPositionVecSpread.push_back(POSITION_4); + mPositionVecSpread.push_back(POSITION_5); + mPositionVecSpread.push_back(POSITION_6); + + /*Initialize the rotation of spread items*/ + mRotationVecSpread.push_back(ROTATION_0); + mRotationVecSpread.push_back(ROTATION_1); + mRotationVecSpread.push_back(ROTATION_2); + mRotationVecSpread.push_back(ROTATION_4); + mRotationVecSpread.push_back(ROTATION_5); + mRotationVecSpread.push_back(ROTATION_6); + + /*Initialize the scale of spread items*/ + for(unsigned int i=0; i positionList) + { + if(positionList.size() == SPREAD_ITEM_NUM) + { + mPositionVecSpread = positionList; + } + } + + vector GetPosition() const + { + return mPositionVecSpread; + } + + void SetColor(vector colorList) + { + if(colorList.size() == SPREAD_ITEM_NUM) + { + mColorVecSpread = colorList; + } + } + + vector GetColor() const + { + return mColorVecSpread; + } + + void SetScale(vector scaleList) + { + if(scaleList.size() == SPREAD_ITEM_NUM) + { + mScaleVecSpread = scaleList; + } + } + + vector GetScale() const + { + return mScaleVecSpread; + } + + void SetRotationX(float rotat_x) + { + mRotationX = rotat_x; + } + + float GetRotationX() const + { + return mRotationX; + } + + void SetRotationZ(vector rotationList) + { + if(rotationList.size() == SPREAD_ITEM_NUM) + { + mRotationVecSpread = rotationList; + } + } + + vector GetRotationZ() const + { + return mRotationVecSpread; + } + + void SetCenterPosition(Vector3 pos) + { + mSelectedItemPosition = pos; + } + + Vector3 GetCenterPosition() const + { + return mSelectedItemPosition; + } + + void SetCenterScale(float scale) + { + mSelectedItemScale = scale; + } + + float GetCenterScale() const + { + return mSelectedItemScale; + } + + void SetCenterColor(Vector2 color) + { + mSelectedItemColor = color; + } + + Vector2 GetCenterColor() const + { + return mSelectedItemColor; + } + + void SetStackPosition(Vector3 rightPos, Vector3 leftPos) + { + mPositionRight = rightPos; + mPositionLeft = leftPos; + } + + Vector3 GetRightStackPosition() const + { + return mPositionRight; + } + + Vector3 GetLeftStackPosition() const + { + return mPositionLeft; + } + + void SetStackScale(float rightScale, float leftScale) + { + mScaleRight = rightScale; + mScaleLeft = leftScale; + } + + float GetRightStackScale() const + { + return mScaleRight; + } + + float GetLeftStackScale() const + { + return mScaleLeft; + } + + void SetStackColor(Vector2 rightColor, Vector2 leftColor) + { + mColorRight = rightColor; + mColorLeft = leftColor; + } + + Vector2 GetRightStackColor() const + { + return mColorRight; + } + + Vector2 GetLeftStackColor() const + { + return mColorLeft; + } + + void SetNumOfItems(int num) + { + mNumOfItems = num; + + /*Initialize the rotation of stack items*/ + for(int i=0; i mPositionVecSpread;/*positions of the spread items*/ + vector mRotationVecSpread;/*rotations of the spread items*/ + vector mScaleVecSpread;/*scales of the spread items*/ + vector mColorVecSpread;/*colors of the spread items*/ + + vector mRotationVecStack;/*rotations of the stack items*/ + + float mRotationRight;/*rotation of right album stack*/ + float mRotationLeft;/*rotation of left album stack*/ + + float mScaleRight;/*scale of right album stack*/ + float mScaleLeft;/*scale of left album stack*/ + + Vector2 mColorRight;/*color of right album stack*/ + Vector2 mColorLeft;/*color of left album stack*/ + + Vector3 mPositionRight;/*position of right album stack*/ + Vector3 mPositionLeft;/*position of left album stack*/ + + int mNumOfItems;/*num of items*/ + int mStackNum;/*num of items of stack*/ +}; + +AlbumLayoutPtr AlbumLayout::New() +{ + return AlbumLayoutPtr(new AlbumLayout()); +} + +AlbumLayout::~AlbumLayout() +{ + delete mImpl; +} + +void AlbumLayout::SetItemSizeFunction(ItemSizeFunction function) +{ + mImpl->mItemSizeFunction = function; +} + +AlbumLayout::ItemSizeFunction AlbumLayout::GetItemSizeFunction() const +{ + return mImpl->mItemSizeFunction; +} + +void AlbumLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void AlbumLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void AlbumLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float AlbumLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float AlbumLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float AlbumLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float AlbumLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + return - static_cast(numberOfItems) + 1; +} + +float AlbumLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + return round(layoutPosition); +} + +float AlbumLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + return -(static_cast(itemId)); +} + +ItemRange AlbumLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + return ItemRange(0, mImpl->mNumOfItems); +} + +unsigned int AlbumLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + return 0; +} + +bool AlbumLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + itemSize = mImpl->mItemSizeFunction(layoutSize); + return true; +} + +void AlbumLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ + if(animation) + { + animation.Resize(actor, size); + } +} + +bool AlbumLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + constraint = AlbumPositionConstraint(mImpl->mPositionRight,mImpl->mPositionLeft,mImpl->mSelectedItemPosition,mImpl->mPositionVecSpread,mImpl->mRotationX); + return true; +} + +bool AlbumLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + constraint = AlbumRotationConstraint(mImpl->mRotationVecSpread,mImpl->mRotationVecStack,mImpl->mRotationX,mImpl->mNumOfItems); + return true; +} + +bool AlbumLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + constraint = AlbumScaleConstraint(mImpl->mScaleRight,mImpl->mScaleLeft,mImpl->mSelectedItemScale,mImpl->mScaleVecSpread); + return true; +} + +bool AlbumLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = AlbumColorConstraint(mImpl->mColorRight,mImpl->mColorLeft,mImpl->mSelectedItemColor,mImpl->mColorVecSpread,mImpl->mStackNum); + return true; +} + +bool AlbumLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + constraint = AlbumVisibilityConstraintPortrait(); + return true; +} + +Degree AlbumLayout::GetScrollDirection() const +{ + Degree scrollDirection(0); + + scrollDirection = -90.0f; + + return scrollDirection; +} + +void AlbumLayout::SetNumOfItems(int num) +{ + mImpl->SetNumOfItems(num); +} + +int AlbumLayout::GetNumOfItems() const +{ + return mImpl->GetNumOfItems(); +} + +void AlbumLayout::SetStackNum(int num) +{ + mImpl->SetStackNum(num); +} + +int AlbumLayout::GetStackNum() const +{ + return mImpl->GetStackNum(); +} + +void AlbumLayout::SetPosition(vector positionList) +{ + mImpl->SetPosition(positionList); +} + +vector AlbumLayout::GetPosition() const +{ + return mImpl->GetPosition(); +} + +void AlbumLayout::SetScale(vector scaleList) +{ + mImpl->SetScale(scaleList); +} + +vector AlbumLayout::GetScale() const +{ + return mImpl->GetScale(); +} + +void AlbumLayout::SetColor(vector colorList) +{ + mImpl->SetColor(colorList); +} + +vector AlbumLayout::GetColor() const +{ + return mImpl->GetColor(); +} + +void AlbumLayout::SetRotationX(float rotation) +{ + mImpl->SetRotationX(rotation); +} + +float AlbumLayout::GetRotationX() const +{ + return mImpl->GetRotationX(); +} + +void AlbumLayout::SetRotationZ(vector rotationList) +{ + mImpl->SetRotationZ(rotationList); +} + +vector AlbumLayout::GetRotationZ() const +{ + return mImpl->GetRotationZ(); +} + +void AlbumLayout::SetCenterPosition(Vector3 pos) +{ + mImpl->SetCenterPosition(pos); +} + +Vector3 AlbumLayout::GetCenterPosition() const +{ + return mImpl->GetCenterPosition(); +} + +void AlbumLayout::SetCenterScale(float scale) +{ + mImpl->SetCenterScale(scale); +} + +float AlbumLayout::GetCenterScale() const +{ + return mImpl->GetCenterScale(); +} + +void AlbumLayout::SetCenterColor(Vector2 color) +{ + mImpl->SetCenterColor(color); +} + +Vector2 AlbumLayout::GetCenterColor() const +{ + return mImpl->GetCenterColor(); +} + +void AlbumLayout::SetStackPosition(Vector3 rightPos, Vector3 leftPos) +{ + mImpl->SetStackPosition(rightPos, leftPos); +} + +Vector3 AlbumLayout::GetRightStackPosition() const +{ + return mImpl->GetRightStackPosition(); +} + +Vector3 AlbumLayout::GetLeftStackPosition() const +{ + return mImpl->GetLeftStackPosition(); +} + +void AlbumLayout::SetStackScale(float rightScale, float leftScale) +{ + mImpl->SetStackScale(rightScale,leftScale); +} + +float AlbumLayout::GetRightStackScale() const +{ + return mImpl->GetRightStackScale(); +} + +float AlbumLayout::GetLeftStackScale() const +{ + return mImpl->GetLeftStackScale(); +} + +void AlbumLayout::SetStackColor(Vector2 rightColor, Vector2 leftColor) +{ + mImpl->SetStackColor(rightColor, leftColor); +} + +Vector2 AlbumLayout::GetRightStackColor() const +{ + return mImpl->GetRightStackColor(); +} + +Vector2 AlbumLayout::GetLeftStackColor() const +{ + return mImpl->GetLeftStackColor(); +} + +AlbumLayout::AlbumLayout() +: mImpl(NULL) +{ + mImpl = new Impl(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.h b/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.h new file mode 100755 index 0000000..92d2ce7 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/album-layout.h @@ -0,0 +1,369 @@ +#ifndef __DALI_TOOLKIT_ALBUM_LAYOUT_H__ +#define __DALI_TOOLKIT_ALBUM_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +/* +Note:This layout is customized for music player application, so there are some limitations: +1.This layout can only be used with 1280x720 mode (not 720x1280); +2.Need promram in application layer to support it(EX.SetNum). +*/ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class AlbumLayout; + +typedef IntrusivePtr AlbumLayoutPtr; + +/** + * An ItemView layout which arranges items in a album. + */ +class AlbumLayout : public ItemLayout +{ +public: + + typedef boost::function ItemSizeFunction; + typedef boost::function AlbumRadiusFunction; + + /** + * Create a new album layout + */ + static AlbumLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~AlbumLayout(); + + /** + * Set the function used to calculate the item-size, for a given layout-size. + * @param[in] function The item-size function. + */ + void SetItemSizeFunction(ItemSizeFunction function); + + /** + * Get the function used to calculate the item-size + * @return The item-size function. + */ + ItemSizeFunction GetItemSizeFunction() const; + + /** + * Set the function used to calculate the album radius, for a given layout-size. + * @param[in] function The spiral-radius function. + */ + void SetAlbumRadiusFunction(AlbumRadiusFunction function); + + /** + * Get the function used to calculate the album radius. + * @return The album-radius function. + */ + AlbumRadiusFunction GetAlbumRadiusFunction() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + + /** + * Set the num of items. + * @param[in] num The number of items. + */ + void SetNumOfItems(int num); + + /** + * Get the num of items. + */ + int GetNumOfItems() const; + + /** + * Set the items num of stack. + * @param[in] num The number of items on the stack. + */ + void SetStackNum(int num); + + /** + * Get the items num of stack. + */ + int GetStackNum() const; + + /** + * Set the position of each item. + * @param[in] positionList The vector which contains the position for each item. + */ + void SetPosition(std::vector positionList); + + /** + * Get the position of each item. + */ + std::vector GetPosition() const; + + /** + * Set the rotation of each item. + * @param[in] rotation around X, the vector which contains the rotation for each item. + */ + void SetRotationX(float rotation); + + /** + * Set the rotation of each item. + */ + float GetRotationX() const; + + /** + * Set the rotation of each item. + * @param[in] rotationList around Z, the vector which contains the rotation for each item. + */ + void SetRotationZ(std::vector rotationList); + + /** + * Get the rotation of each item. + */ + std::vector GetRotationZ() const; + + /** + * Set the scale of each item. + * @param[in] scaleList The vector which contains the scale for each item. + */ + void SetScale(std::vector scaleList); + + /** + * Get the scale of each item. + */ + std::vector GetScale() const; + + /** + * Set the color of each item. + * @param[in] colorList The vector which contains the color for each item. + */ + void SetColor(std::vector colorList); + + /** + * Get the color of each item. + */ + std::vector GetColor() const; + + /** + * Set the position of center(selected) item. + * @param[in] pos The positon to set. + */ + void SetCenterPosition(Vector3 pos); + + + /** + * Get the position of center(selected) item. + */ + Vector3 GetCenterPosition() const; + + /** + * Set the scale of center(selected) item. + * @param[in] scale The scale to set. + */ + void SetCenterScale(float scale); + + /** + * Get the scale of center(selected) item. + */ + float GetCenterScale() const; + + /** + * Set the color of center(selected) item. + * @param[in] color The color to set. + */ + void SetCenterColor(Vector2 color); + + /** + * Get the color of center(selected) item. + */ + Vector2 GetCenterColor() const; + + /** + * Set the postion of stack item(right stack and left stack). + * @param[in] rightPos The position of right stack. + * @param[in] leftPos The position of left stack,. + */ + void SetStackPosition(Vector3 rightPos, Vector3 leftPos); + + /** + * Get the postion of right stack . + */ + Vector3 GetRightStackPosition() const; + + /** + * Get the postion of left stack . + */ + Vector3 GetLeftStackPosition() const; + + /** + * Set the scale of stack item(right stack and left stack). + * @param[in] rightScale The scale of right stack. + * @param[in] leftScale The scale of left stack,. + */ + void SetStackScale(float rightScale, float leftScale); + + /** + * Get the scale of right stack. + */ + float GetRightStackScale() const; + + /** + * Get the scale of left stack. + */ + float GetLeftStackScale() const; + + /** + * Set the color of stack item(right stack and left stack). + * @param[in] rightColor The color of right stack. + * @param[in] leftColor The color of left stack. + */ + void SetStackColor(Vector2 rightColor, Vector2 leftColor); + + /** + * Get the color of right stack. + */ + Vector2 GetRightStackColor() const; + + /** + * Get the color of left stack. + */ + Vector2 GetLeftStackColor() const; + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also AlbumLayout::New() + */ + AlbumLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_ALBUM_LAYOUT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.cpp new file mode 100644 index 0000000..11b97a7 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.cpp @@ -0,0 +1,761 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ + +const unsigned int DEFAULT_NUMBER_OF_COLUMNS = 3; +const float DEFAULT_NUMBER_OF_ROWS = 20.0f; +const float DEFAULT_ROW_SPACING = 55.0f; +const float DEFAULT_BOTTOM_MARGIN_FACTOR = 0.1f; +const Radian DEFAULT_TILT_ANGLE ( Math::PI*0.12f ); +const Radian DEFAULT_ITEM_TILT_ANGLE ( -Math::PI*0.025f ); +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.02f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 50.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.03f; + +static Vector3 GetItemSizeDefaultFunction(unsigned int numberOfColumns, float layoutWidth) +{ + float width = layoutWidth / static_cast(numberOfColumns + 1); + + // 1x1 aspect ratio + return Vector3(width, width, width); +} + +static float GetBottomMarginDefaultFunction(float layoutHeight) +{ + return layoutHeight * DEFAULT_BOTTOM_MARGIN_FACTOR; +} + +struct GetColumnPositionDefaultFunction +{ + float operator()(unsigned int numberOfColumns, + unsigned int columnNumber, + const Vector3& itemSize, + float layoutWidth) + { + // Share the available space between margins & column spacings + float availableSpace = max(0.0f, (layoutWidth - itemSize.width*numberOfColumns)); + + float leftMargin = availableSpace/numberOfColumns * 0.5f; + + float columnPosition = leftMargin + itemSize.width*0.5f + columnNumber*(itemSize.width + availableSpace/numberOfColumns); + + return columnPosition - layoutWidth*0.5f; + } +}; + +struct DepthPositionConstraint0 +{ + DepthPositionConstraint0(unsigned int numberOfColumns, + unsigned int columnNumber, + DepthLayout::ItemSizeFunction itemSizeFunction, + DepthLayout::BottomMarginFunction bottomMarginFunction, + DepthLayout::ColumnPositionFunction columnPositionFunction, + float heightScale, + float depthScale) + : mNumberOfColumns(numberOfColumns), + mColumnNumber(columnNumber), + mItemSizeFunction(itemSizeFunction), + mBottomMarginFunction(bottomMarginFunction), + mColumnPositionFunction(columnPositionFunction), + mHeightScale(heightScale), + mDepthScale(depthScale) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width); + + float rowLayoutPositon = layoutPosition - static_cast(mColumnNumber); + + return Vector3( mColumnPositionFunction(mNumberOfColumns, mColumnNumber, itemSize, layoutSize.width), + rowLayoutPositon*mHeightScale + layoutSize.height*0.5f - mBottomMarginFunction(layoutSize.height) - itemSize.height * 0.5f, + -rowLayoutPositon*mDepthScale ); + } + + unsigned int mNumberOfColumns; + unsigned int mColumnNumber; + + DepthLayout::ItemSizeFunction mItemSizeFunction; + DepthLayout::BottomMarginFunction mBottomMarginFunction; + DepthLayout::ColumnPositionFunction mColumnPositionFunction; + + float mHeightScale; + float mDepthScale; +}; + +struct DepthPositionConstraint90 +{ + DepthPositionConstraint90(unsigned int numberOfColumns, + unsigned int columnNumber, + DepthLayout::ItemSizeFunction itemSizeFunction, + DepthLayout::BottomMarginFunction bottomMarginFunction, + DepthLayout::ColumnPositionFunction columnPositionFunction, + float heightScale, + float depthScale) + : mNumberOfColumns(numberOfColumns), + mColumnNumber(columnNumber), + mItemSizeFunction(itemSizeFunction), + mBottomMarginFunction(bottomMarginFunction), + mColumnPositionFunction(columnPositionFunction), + mHeightScale(heightScale), + mDepthScale(depthScale) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height); + + float rowLayoutPositon = layoutPosition - static_cast(mColumnNumber) + mNumberOfColumns*0.5f; + + return Vector3( rowLayoutPositon*mHeightScale + layoutSize.width*0.5f - mBottomMarginFunction(layoutSize.width) - itemSize.height * 0.5f, + -mColumnPositionFunction(mNumberOfColumns, mColumnNumber, itemSize, layoutSize.height), + -rowLayoutPositon*mDepthScale ); + } + + unsigned int mNumberOfColumns; + unsigned int mColumnNumber; + + DepthLayout::ItemSizeFunction mItemSizeFunction; + DepthLayout::BottomMarginFunction mBottomMarginFunction; + DepthLayout::ColumnPositionFunction mColumnPositionFunction; + + float mHeightScale; + float mDepthScale; +}; + +struct DepthPositionConstraint180 +{ + DepthPositionConstraint180(unsigned int numberOfColumns, + unsigned int columnNumber, + DepthLayout::ItemSizeFunction itemSizeFunction, + DepthLayout::BottomMarginFunction bottomMarginFunction, + DepthLayout::ColumnPositionFunction columnPositionFunction, + float heightScale, + float depthScale) + : mNumberOfColumns(numberOfColumns), + mColumnNumber(columnNumber), + mItemSizeFunction(itemSizeFunction), + mBottomMarginFunction(bottomMarginFunction), + mColumnPositionFunction(columnPositionFunction), + mHeightScale(heightScale), + mDepthScale(depthScale) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width); + + float rowLayoutPositon = layoutPosition - static_cast(mColumnNumber); + + return Vector3( -mColumnPositionFunction(mNumberOfColumns, mColumnNumber, itemSize, layoutSize.width), + -(rowLayoutPositon*mHeightScale + layoutSize.height*0.5f - mBottomMarginFunction(layoutSize.height) - itemSize.height * 0.5f), + -rowLayoutPositon*mDepthScale ); + } + + unsigned int mNumberOfColumns; + unsigned int mColumnNumber; + + DepthLayout::ItemSizeFunction mItemSizeFunction; + DepthLayout::BottomMarginFunction mBottomMarginFunction; + DepthLayout::ColumnPositionFunction mColumnPositionFunction; + + float mHeightScale; + float mDepthScale; +}; + +struct DepthPositionConstraint270 +{ + DepthPositionConstraint270(unsigned int numberOfColumns, + unsigned int columnNumber, + DepthLayout::ItemSizeFunction itemSizeFunction, + DepthLayout::BottomMarginFunction bottomMarginFunction, + DepthLayout::ColumnPositionFunction columnPositionFunction, + float heightScale, + float depthScale) + : mNumberOfColumns(numberOfColumns), + mColumnNumber(columnNumber), + mItemSizeFunction(itemSizeFunction), + mBottomMarginFunction(bottomMarginFunction), + mColumnPositionFunction(columnPositionFunction), + mHeightScale(heightScale), + mDepthScale(depthScale) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height); + + float rowLayoutPositon = layoutPosition - static_cast(mColumnNumber) + mNumberOfColumns*0.5f; + + return Vector3( -(rowLayoutPositon*mHeightScale + layoutSize.width*0.5f - mBottomMarginFunction(layoutSize.width) - itemSize.height * 0.5f), + mColumnPositionFunction(mNumberOfColumns, mColumnNumber, itemSize, layoutSize.height), + -rowLayoutPositon*mDepthScale ); + } + + unsigned int mNumberOfColumns; + unsigned int mColumnNumber; + + DepthLayout::ItemSizeFunction mItemSizeFunction; + DepthLayout::BottomMarginFunction mBottomMarginFunction; + DepthLayout::ColumnPositionFunction mColumnPositionFunction; + + float mHeightScale; + float mDepthScale; +}; + +struct DepthRotationConstraint0 +{ + DepthRotationConstraint0(float angleRadians) + : mTiltAngle(angleRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.0f, Vector3::ZAXIS) * Quaternion(mTiltAngle, Vector3::XAXIS); + } + + float mTiltAngle; +}; + +struct DepthRotationConstraint90 +{ + DepthRotationConstraint90(float angleRadians) + : mTiltAngle(angleRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(1.5f * Math::PI, Vector3::ZAXIS) * Quaternion(mTiltAngle, Vector3::XAXIS); + } + + float mTiltAngle; +}; + +struct DepthRotationConstraint180 +{ + DepthRotationConstraint180(float angleRadians) + : mTiltAngle(angleRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(mTiltAngle, Vector3::XAXIS); + } + + float mTiltAngle; +}; + +struct DepthRotationConstraint270 +{ + DepthRotationConstraint270(float angleRadians) + : mTiltAngle(angleRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.5f * Math::PI, Vector3::ZAXIS) * Quaternion(mTiltAngle, Vector3::XAXIS); + } + + float mTiltAngle; +}; + +struct DepthColorConstraint +{ + DepthColorConstraint(unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber) + : mNumberOfColumns(numberOfColumns), + mNumberOfRows(numberOfRows), + mColumnNumber(columnNumber) + { + } + + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float row = (layoutPosition - static_cast(mColumnNumber)) / mNumberOfColumns; + + float darkness(1.0f); + float alpha(1.0f); + + if (row < 0.0f) + { + darkness = alpha = max(0.0f, 1.0f + row); + } + else + { + if (row > mNumberOfRows) + { + darkness = 0.0f; + } + else + { + darkness = 1.0f - ( 1.0f * (row / mNumberOfRows) ); + } + + if (row > (mNumberOfRows-1.0f)) + { + alpha = max(0.0f, 1.0f - (row-(mNumberOfRows-1.0f))); + } + } + + return Vector4( darkness, darkness, darkness, current.a * alpha ); + } + + unsigned int mNumberOfColumns; + float mNumberOfRows; + unsigned int mColumnNumber; +}; + +struct DepthVisibilityConstraint +{ + DepthVisibilityConstraint(unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber) + : mNumberOfColumns(numberOfColumns), + mNumberOfRows(numberOfRows), + mColumnNumber(columnNumber) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float row = (layoutPosition - static_cast(mColumnNumber)) / mNumberOfColumns; + return (row > -1.0f) && (row < mNumberOfRows); + } + + unsigned int mNumberOfColumns; + float mNumberOfRows; + unsigned int mColumnNumber; +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +struct PositionConstraintSet +{ + ItemLayout::Vector3Function mOrientation0; + ItemLayout::Vector3Function mOrientation90; + ItemLayout::Vector3Function mOrientation180; + ItemLayout::Vector3Function mOrientation270; +}; + +struct DepthLayout::Impl +{ + Impl() + : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS), + mNumberOfRows(DEFAULT_NUMBER_OF_ROWS), + mRowSpacing(DEFAULT_ROW_SPACING), + mTiltAngle(DEFAULT_TILT_ANGLE), + mItemTiltAngle(DEFAULT_ITEM_TILT_ANGLE), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION), + mItemSizeFunction(GetItemSizeDefaultFunction), + mBottomMarginFunction(GetBottomMarginDefaultFunction), + mColumnPositionFunction(GetColumnPositionDefaultFunction()) + { + } + + unsigned int mNumberOfColumns; + unsigned int mNumberOfRows; + + float mRowSpacing; + + Radian mTiltAngle; + Radian mItemTiltAngle; + + float mScrollSpeedFactor; + float mMaximumSwipeSpeed; + float mItemFlickAnimationDuration; + + ItemSizeFunction mItemSizeFunction; + BottomMarginFunction mBottomMarginFunction; + ColumnPositionFunction mColumnPositionFunction; +}; + +DepthLayoutPtr DepthLayout::New() +{ + return DepthLayoutPtr(new DepthLayout()); +} + +DepthLayout::~DepthLayout() +{ + delete mImpl; +} + +void DepthLayout::SetNumberOfColumns(unsigned int columns) +{ + mImpl->mNumberOfColumns = columns; +} + +unsigned int DepthLayout::GetNumberOfColumns() const +{ + return mImpl->mNumberOfColumns; +} + +void DepthLayout::SetNumberOfRows(unsigned int rows) +{ + mImpl->mNumberOfRows = rows; +} + +unsigned int DepthLayout::GetNumberOfRows() const +{ + return mImpl->mNumberOfRows; +} + +void DepthLayout::SetRowSpacing(float spacing) +{ + mImpl->mRowSpacing = spacing; +} + +float DepthLayout::GetRowSpacing() const +{ + return mImpl->mRowSpacing; +} + +void DepthLayout::SetTiltAngle(Degree angle) +{ + mImpl->mTiltAngle = Degree( Clamp(angle, -45.0f, 45.0f) ); +} + +Degree DepthLayout::GetTiltAngle() const +{ + return mImpl->mTiltAngle; +} + +void DepthLayout::SetItemSizeFunction(ItemSizeFunction function) +{ + mImpl->mItemSizeFunction = function; +} + +DepthLayout::ItemSizeFunction DepthLayout::GetItemSizeFunction() const +{ + return mImpl->mItemSizeFunction; +} + +void DepthLayout::SetBottomMarginFunction(BottomMarginFunction function) +{ + mImpl->mBottomMarginFunction = function; +} + +DepthLayout::BottomMarginFunction DepthLayout::GetBottomMarginFunction() const +{ + return mImpl->mBottomMarginFunction; +} + +void DepthLayout::SetItemTiltAngle(Degree angle) +{ + mImpl->mItemTiltAngle = angle; +} + +Degree DepthLayout::GetItemTiltAngle() const +{ + return mImpl->mItemTiltAngle; +} + +void DepthLayout::SetColumnPositionFunction(ColumnPositionFunction function) +{ + mImpl->mColumnPositionFunction = function; +} + +DepthLayout::ColumnPositionFunction DepthLayout::GetColumnPositionFunction() const +{ + return mImpl->mColumnPositionFunction; +} + +void DepthLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void DepthLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void DepthLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float DepthLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float DepthLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float DepthLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float DepthLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + return static_cast(mImpl->mNumberOfColumns) - static_cast(numberOfItems); +} + +float DepthLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + float rowIndex = static_cast(round(layoutPosition / mImpl->mNumberOfColumns)); + return rowIndex * static_cast(mImpl->mNumberOfColumns); +} + +float DepthLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + float rowIndex = static_cast(itemId / mImpl->mNumberOfColumns); + return -rowIndex * static_cast(mImpl->mNumberOfColumns); +} + +ItemRange DepthLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + float firstRow = -(firstItemPosition/mImpl->mNumberOfColumns); + float lastRow = firstRow + mImpl->mNumberOfRows * 0.5f; + + unsigned int firstItem = static_cast(max(0.0f, firstRow * mImpl->mNumberOfColumns)); + unsigned int lastItem = static_cast(max(0.0f, lastRow * mImpl->mNumberOfColumns)); + + return ItemRange(firstItem, lastItem+1); +} + +unsigned int DepthLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + float itemsWithinLayout = (layoutSize.depth * mImpl->mNumberOfColumns) / (cosf(mImpl->mTiltAngle) * mImpl->mRowSpacing); + + return static_cast(itemsWithinLayout); +} + +bool DepthLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + // Note: itemId is not checked, since every item has the same size + + itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, (IsVertical(mOrientation) ? layoutSize.width : layoutSize.height) ); + return true; +} + +void DepthLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ + if(animation) + { + animation.Resize(actor, size); + } +} + +bool DepthLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + float heightScale = -sinf(mImpl->mTiltAngle) * mImpl->mRowSpacing; + float depthScale = cosf(mImpl->mTiltAngle) * mImpl->mRowSpacing; + + if (mOrientation == ControlOrientation::Up) + { + constraint = DepthPositionConstraint0( mImpl->mNumberOfColumns, + itemId % mImpl->mNumberOfColumns, + mImpl->mItemSizeFunction, + mImpl->mBottomMarginFunction, + mImpl->mColumnPositionFunction, + heightScale, + depthScale ); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = DepthPositionConstraint90( mImpl->mNumberOfColumns, + itemId % mImpl->mNumberOfColumns, + mImpl->mItemSizeFunction, + mImpl->mBottomMarginFunction, + mImpl->mColumnPositionFunction, + heightScale, + depthScale ); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = DepthPositionConstraint180( mImpl->mNumberOfColumns, + itemId % mImpl->mNumberOfColumns, + mImpl->mItemSizeFunction, + mImpl->mBottomMarginFunction, + mImpl->mColumnPositionFunction, + heightScale, + depthScale ); + } + else // mOrientation == ControlOrientation::Right + { + constraint = DepthPositionConstraint270( mImpl->mNumberOfColumns, + itemId % mImpl->mNumberOfColumns, + mImpl->mItemSizeFunction, + mImpl->mBottomMarginFunction, + mImpl->mColumnPositionFunction, + heightScale, + depthScale ); + } + + return true; +} + +bool DepthLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + if (mOrientation == ControlOrientation::Up) + { + constraint = DepthRotationConstraint0(mImpl->mItemTiltAngle); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = DepthRotationConstraint90(mImpl->mItemTiltAngle); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = DepthRotationConstraint180(mImpl->mItemTiltAngle); + } + else // mOrientation == ControlOrientation::Right + { + constraint = DepthRotationConstraint270(mImpl->mItemTiltAngle); + } + + return true; +} + +bool DepthLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + return false; // No scaling +} + +bool DepthLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = DepthColorConstraint(mImpl->mNumberOfColumns, mImpl->mNumberOfRows*0.5f, itemId % mImpl->mNumberOfColumns); + return true; +} + +bool DepthLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + constraint = DepthVisibilityConstraint(mImpl->mNumberOfColumns, mImpl->mNumberOfRows*0.5f, itemId % mImpl->mNumberOfColumns); + return true; +} + +Degree DepthLayout::GetScrollDirection() const +{ + Degree scrollDirection(0.0f); + + if (mOrientation == ControlOrientation::Up) + { + scrollDirection = 180.0f; + } + else if (mOrientation == ControlOrientation::Left) + { + scrollDirection = 270.0f; + } + else if (mOrientation == ControlOrientation::Down) + { + scrollDirection = 0.0f; + } + else // mOrientation == ControlOrientation::Right + { + scrollDirection = 90.0f; + } + + return scrollDirection; +} + +DepthLayout::DepthLayout() +: mImpl(NULL) +{ + mImpl = new Impl(); +} + +float DepthLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) +{ + float scrollTo = currentLayoutPosition; + float row = (currentLayoutPosition + itemID - static_cast(itemID % mImpl->mNumberOfColumns)) / mImpl->mNumberOfColumns; + + // Check whether item is not within viewable area + if(row <= -1.0f) + { + scrollTo = GetItemScrollToPosition(itemID); + } + else if(row > mImpl->mNumberOfRows * 0.5f - 1.0f) + { + scrollTo = GetItemScrollToPosition(itemID) + (mImpl->mNumberOfRows - 1.0f) * 0.5f * mImpl->mNumberOfColumns; + } + + return scrollTo; +} + +int DepthLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + switch( direction ) + { + case Control::Left: + { + itemID--; + if( itemID < 0 ) + { + itemID = loopEnabled ? maxItems - 1 : 0; + } + break; + } + case Control::Up: + { + itemID += mImpl->mNumberOfColumns; + if( itemID >= maxItems ) + { + itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns; + } + break; + } + case Control::Right: + { + itemID++; + if( itemID >= maxItems ) + { + itemID = loopEnabled ? 0 : maxItems - 1; + } + break; + } + case Control::Down: + { + itemID -= mImpl->mNumberOfColumns; + if( itemID < 0 ) + { + itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns; + } + break; + } + } + return itemID; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.h b/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.h new file mode 100644 index 0000000..1615446 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/depth-layout.h @@ -0,0 +1,288 @@ +#ifndef __DALI_TOOLKIT_DEPTH_LAYOUT_H__ +#define __DALI_TOOLKIT_DEPTH_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class DepthLayout; + +typedef IntrusivePtr DepthLayoutPtr; + +/** + * This layout arranges items in a grid, which scrolls along the Z-Axis. + */ +class DepthLayout : public ItemLayout +{ +public: + + typedef boost::function ItemSizeFunction; + + typedef boost::function BottomMarginFunction; + + typedef boost::function ColumnPositionFunction; + + /** + * Create a new spiral layout + */ + static DepthLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~DepthLayout(); + + /** + * Set the number of columns in the layout. + * @param[in] columns The number of columns. + */ + void SetNumberOfColumns(unsigned int columns); + + /** + * Get the number of columns in the layout. + * @return The number of columns. + */ + unsigned int GetNumberOfColumns() const; + + /** + * Set the number of rows in the layout. + * The default is 20, with 10 behind the viewable area. + * @param[in] rows The number-of-rows. + */ + void SetNumberOfRows(unsigned int rows); + + /** + * Get the number of rows in the layout. + * @return The number of rows. + */ + unsigned int GetNumberOfRows() const; + + /** + * Set the spacing between rows. + * @param[in] spacing The row spacing. + */ + void SetRowSpacing(float spacing); + + /** + * Get the spacing between rows. + * @return The row spacing. + */ + float GetRowSpacing() const; + + /** + * Set the tilt angle of the layout; this is clamped between -45 & 45 degrees. + * @param[in] angle The tilt angle in degrees. + */ + void SetTiltAngle(Degree angle); + + /** + * Get the tilt angle of the layout. + * @return The tilt angle in degrees. + */ + Degree GetTiltAngle() const; + + /** + * Set the function used to calculate the item-size, for a given layout-size. + * @param[in] function The item-size function. + */ + void SetItemSizeFunction(ItemSizeFunction function); + + /** + * Get the function used to calculate the item-size. + * @return The item-size function. + */ + ItemSizeFunction GetItemSizeFunction() const; + + /** + * Set the function used to calculate the margin in the bottom of the layout, for a given layout-size. + * @param[in] function The bottom margin function. + */ + void SetBottomMarginFunction(BottomMarginFunction function); + + /** + * Get the function used to calculate the margin in the bottom of the layout. + * @return The bottom margin function. + */ + BottomMarginFunction GetBottomMarginFunction() const; + + /** + * Set the tilt angle of the individual items in the layout. + * @param[in] angle The item tilt angle in degrees. + */ + void SetItemTiltAngle(Degree angle); + + /** + * Get the tilt angle of the individual items in the layout. + * @return The item tilt angle in degrees. + */ + Degree GetItemTiltAngle() const; + + /** + * Set the function used to calculate the horizontal position of each column, for a given column, item-size & layout-size. + * @param[in] function The column-position function. + */ + void SetColumnPositionFunction(ColumnPositionFunction function); + + /** + * Get the function used to calculate the horizontal position of each column + * @return The column-position function. + */ + ColumnPositionFunction GetColumnPositionFunction() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + + /** + * @copydoc ItemLayout::GetClosestOnScreenLayoutPosition() + */ + virtual float GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize); + + /** + * @copydoc ItemLayout::GetNextFocusItemID() + */ + virtual int GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled); + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also DepthLayout::New() + */ + DepthLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_DEPTH_LAYOUT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.cpp new file mode 100644 index 0000000..b10700b --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/grid-layout.cpp @@ -0,0 +1,762 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ + +const unsigned int DEFAULT_NUMBER_OF_COLUMNS = 4; +const float DEFAULT_TOP_MARGIN = 95.0f; +const float DEFAULT_BOTTOM_MARGIN = 20.0f; +const float DEFAULT_SIDE_MARGIN = 20.0f; +const float DEFAULT_COLUMN_SPACING = 20.0f; +const float DEFAULT_ROW_SPACING = 20.0f; +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.03f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 100.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.015f; + +// 4 orientations are supported +static const unsigned int ORIENTATION_COUNT = 4; + +static Vector3 GetItemSizeDefaultFunction(unsigned int numberOfColumns, float layoutWidth, float sideMargin, float columnSpacing) +{ + float width = (layoutWidth - sideMargin * 2.0f - columnSpacing * static_cast(numberOfColumns - 1)) / static_cast(numberOfColumns); + + // 4x3 aspect ratio + return Vector3(width, width * 0.75f, width * 0.75f); +} + +struct GridPositionConstraint0 +{ + GridPositionConstraint0(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mTopMargin(topMargin), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction), + mZGap(gap) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing); + + return Vector3(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.x * 0.5f, + ((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.height * 0.5f + itemSize.y * 0.5f + mTopMargin, + mColumnIndex * mZGap); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mTopMargin; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; + float mZGap; +}; + +struct GridPositionConstraint90 +{ + GridPositionConstraint90(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mTopMargin(topMargin), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction), + mZGap(gap) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing); + + return Vector3(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.width * 0.5f + itemSize.y * 0.5f + mTopMargin, + -(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.y*0.5f), + mColumnIndex * mZGap); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mTopMargin; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; + float mZGap; +}; + +struct GridPositionConstraint180 +{ + GridPositionConstraint180(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mTopMargin(topMargin), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction), + mZGap(gap) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing); + + return Vector3(-(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.x * 0.5f), + -(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.height * 0.5f + itemSize.y * 0.5f + mTopMargin), + mColumnIndex * mZGap); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mTopMargin; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; + float mZGap; +}; + +struct GridPositionConstraint270 +{ + GridPositionConstraint270(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mTopMargin(topMargin), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction), + mZGap(gap) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing); + + return Vector3(-(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.width * 0.5f + itemSize.y * 0.5f + mTopMargin), + mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.y * 0.5f, + mColumnIndex * mZGap); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mTopMargin; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; + float mZGap; +}; + +struct GridRotationConstraint0 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.0f, Vector3::ZAXIS); + } +}; + +struct GridRotationConstraint90 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + } +}; + +struct GridRotationConstraint180 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(Math::PI, Vector3::ZAXIS); + } +}; + +struct GridRotationConstraint270 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } +}; + +struct GridColorConstraint +{ + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Vector4( 1.0f, 1.0f, 1.0f, current.a ); + } +}; + +struct GridVisibilityConstraintPortrait +{ + GridVisibilityConstraintPortrait(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing); + + float row = (layoutPosition - static_cast(mColumnIndex)) / mNumberOfColumns; + int rowsPerPage = ceil(layoutSize.height / (itemSize.y + mRowSpacing)); + + return (row > -2.0f) && (row < rowsPerPage); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct GridVisibilityConstraintLandscape +{ + GridVisibilityConstraintLandscape(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mRowSpacing(rowSpacing), + mColumnSpacing(columnSpacing), + mSideMargin(sideMargin), + mItemSizeFunction(itemSizeFunction) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing); + + float row = (layoutPosition - static_cast(mColumnIndex)) / mNumberOfColumns; + int rowsPerPage = ceil(layoutSize.width / (itemSize.y + mRowSpacing)); + + return (row > -2.0f) && (row < rowsPerPage); + } + +public: + + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mSideMargin; + GridLayout::ItemSizeFunction mItemSizeFunction; +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +struct GridLayout::Impl +{ + Impl() + : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS), + mRowSpacing(DEFAULT_ROW_SPACING), + mColumnSpacing(DEFAULT_COLUMN_SPACING), + mTopMargin(DEFAULT_TOP_MARGIN), + mBottomMargin(DEFAULT_BOTTOM_MARGIN), + mSideMargin(DEFAULT_SIDE_MARGIN), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION), + mItemSizeFunction(GetItemSizeDefaultFunction) + { + mColorConstraint = GridColorConstraint(); + + mRotationConstraint[0] = GridRotationConstraint0(); + mRotationConstraint[1] = GridRotationConstraint90(); + mRotationConstraint[2] = GridRotationConstraint180(); + mRotationConstraint[3] = GridRotationConstraint270(); + } + + unsigned int mNumberOfColumns; + float mRowSpacing; + float mColumnSpacing; + float mTopMargin; + float mBottomMargin; + float mSideMargin; + float mZGap; + + float mScrollSpeedFactor; + float mMaximumSwipeSpeed; + float mItemFlickAnimationDuration; + + ItemLayout::QuaternionFunction mRotationConstraint[ORIENTATION_COUNT]; + ItemLayout::Vector4Function mColorConstraint; + + ItemSizeFunction mItemSizeFunction; +}; + +GridLayoutPtr GridLayout::New() +{ + return GridLayoutPtr(new GridLayout()); +} + +GridLayout::~GridLayout() +{ + delete mImpl; +} + +void GridLayout::SetNumberOfColumns(unsigned int columns) +{ + mImpl->mNumberOfColumns = columns; +} + +unsigned int GridLayout::GetNumberOfColumns() const +{ + return mImpl->mNumberOfColumns; +} + +void GridLayout::SetRowSpacing(float spacing) +{ + mImpl->mRowSpacing = spacing; +} + +float GridLayout::GetRowSpacing() const +{ + return mImpl->mRowSpacing; +} + +void GridLayout::SetColumnSpacing(float spacing) +{ + mImpl->mColumnSpacing = spacing; +} + +float GridLayout::GetColumnSpacing() const +{ + return mImpl->mColumnSpacing; +} + +void GridLayout::SetTopMargin(float margin) +{ + mImpl->mTopMargin = margin; +} + +float GridLayout::GetTopMargin() const +{ + return mImpl->mTopMargin; +} + +void GridLayout::SetBottomMargin(float margin) +{ + mImpl->mBottomMargin = margin; +} + +float GridLayout::GetBottomMargin() const +{ + return mImpl->mBottomMargin; +} + +void GridLayout::SetSideMargin(float margin) +{ + mImpl->mSideMargin = margin; +} + +float GridLayout::GetSideMargin() const +{ + return mImpl->mSideMargin; +} + +void GridLayout::SetZGap(float gap) +{ + mImpl->mZGap = gap; +} + +float GridLayout::GetZGap() const +{ + return mImpl->mZGap; +} + +void GridLayout::SetItemSizeFunction(ItemSizeFunction function) +{ + mImpl->mItemSizeFunction = function; +} + +GridLayout::ItemSizeFunction GridLayout::GetItemSizeFunction() const +{ + return mImpl->mItemSizeFunction; +} + +void GridLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void GridLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void GridLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float GridLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float GridLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float GridLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float GridLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing); + + unsigned int itemsLastRow = numberOfItems % mImpl->mNumberOfColumns; + if (itemsLastRow == 0) + { + itemsLastRow = mImpl->mNumberOfColumns; + } + + float rowsLastPage = (layoutHeight - mImpl->mBottomMargin - mImpl->mTopMargin + mImpl->mRowSpacing) / (itemSize.y + mImpl->mRowSpacing); + float itemsLastPage = (rowsLastPage - 1.0f) * static_cast(mImpl->mNumberOfColumns) + static_cast(itemsLastRow); + + return itemsLastPage - static_cast(numberOfItems); +} + +float GridLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + float rowIndex = static_cast(round(layoutPosition / mImpl->mNumberOfColumns)); + return rowIndex * static_cast(mImpl->mNumberOfColumns); +} + +float GridLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + float rowIndex = static_cast(itemId / mImpl->mNumberOfColumns); + return -rowIndex * static_cast(mImpl->mNumberOfColumns); +} + +ItemRange GridLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing); + + int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing)); + int firstVisibleItem = -(static_cast(firstItemPosition / mImpl->mNumberOfColumns)) * mImpl->mNumberOfColumns; + + int firstItemIndex = std::max(0, firstVisibleItem - static_cast(mImpl->mNumberOfColumns)); + int lastItemIndex = std::max(0, firstVisibleItem + (itemsPerPage - 1)); + + return ItemRange(firstItemIndex, lastItemIndex); +} + +float GridLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) +{ + ItemLayout::Vector3Function positionConstraint; + Vector3 itemPosition = Vector3::ZERO; + if (GetPositionConstraint(itemID, positionConstraint)) + { + itemPosition = positionConstraint(Vector3::ZERO, currentLayoutPosition + itemID, 0.0f, layoutSize); + } + Vector3 itemSize; + GetItemSize(itemID, layoutSize, itemSize); + Vector3 onScreenArea = (layoutSize - (IsVertical(mOrientation) ? itemSize : Vector3(itemSize.y, itemSize.x, itemSize.z))) * 0.5f; + if (itemPosition.x < -onScreenArea.x + || itemPosition.x > onScreenArea.x + || itemPosition.y < -onScreenArea.y + || itemPosition.y > onScreenArea.y) + { + // item not within viewable area + float rowHeight = itemSize.y + mImpl->mRowSpacing; + ItemLayout::Vector3Function firstItemPositionConstraint; + Vector3 firstItemPosition = Vector3::ZERO; + float offset = 0.0f; + if (GetPositionConstraint(0, firstItemPositionConstraint)) + { + firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0.0f, 0.0f, layoutSize); + } + switch( mOrientation ) + { + case ControlOrientation::Up: + { + if(itemPosition.y > onScreenArea.y) + { + offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y; + } + else + { + offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y; + } + break; + } + case ControlOrientation::Down: + { + if(itemPosition.y < -onScreenArea.y) + { + offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y; + } + else + { + offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y; + } + break; + } + case ControlOrientation::Left: + { + if(itemPosition.x > onScreenArea.x) + { + offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x; + } + else + { + offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x; + } + break; + } + case ControlOrientation::Right: + { + if(itemPosition.x < -onScreenArea.x) + { + offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x; + } + else + { + offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x; + } + break; + } + } + // work out number of rows from first item position to an item aligned to bottom of screen + float rowDiff = offset / rowHeight; + float layoutPositionOffset = rowDiff * mImpl->mNumberOfColumns; + float scrollTo = GetItemScrollToPosition(itemID) + layoutPositionOffset; + return scrollTo; + } + return currentLayoutPosition; +} + +unsigned int GridLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing); + int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing)); + return itemsPerPage; +} + +bool GridLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + // Note: itemId is not checked, since every item has the same size + + itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, (IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width), mImpl->mSideMargin, mImpl->mColumnSpacing); + return true; +} + +void GridLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ + if(animation) + { + Vector3 currentSize( actor.GetCurrentSize() ); + Vector3 shrink( currentSize ); + + shrink.width = min(size.width, currentSize.width); + shrink.height = min(size.height, currentSize.height); + + // Do a nonlinear size animation to shrink the actor first when the actor size changes, + // so that we can avoid the actors overlapping during orientation change. + animation.Resize( actor, shrink, AlphaFunctions::EaseOut, 0.0f, durationSeconds * 0.5f ); + animation.Resize( actor, size, AlphaFunctions::EaseIn, 0.0f, durationSeconds ); + animation.SetDestroyAction( Animation::Bake ); + } +} + +bool GridLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + unsigned int columnIndex = itemId % mImpl->mNumberOfColumns; + + if (mOrientation == ControlOrientation::Up) + { + constraint = GridPositionConstraint0(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = GridPositionConstraint90(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = GridPositionConstraint180(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap); + } + else // mOrientation == ControlOrientation::Right + { + constraint = GridPositionConstraint270(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap); + } + + return true; +} + +bool GridLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + constraint = mImpl->mRotationConstraint[mOrientation]; + return true; +} + +bool GridLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + return false; // No scaling +} + +bool GridLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = mImpl->mColorConstraint; + return true; +} + +bool GridLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + unsigned int columnIndex = itemId % mImpl->mNumberOfColumns; + + if (IsVertical(mOrientation)) + { + constraint = GridVisibilityConstraintPortrait(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mSideMargin, mImpl->mItemSizeFunction); + } + else // horizontal + { + constraint = GridVisibilityConstraintLandscape(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mSideMargin, mImpl->mItemSizeFunction); + } + + return true; +} + +Degree GridLayout::GetScrollDirection() const +{ + Degree scrollDirection(0.0f); + + if (mOrientation == ControlOrientation::Up) + { + scrollDirection = 0.0f; + } + else if (mOrientation == ControlOrientation::Left) + { + scrollDirection = 90.0f; + } + else if (mOrientation == ControlOrientation::Down) + { + scrollDirection = 180.0f; + } + else // mOrientation == ControlOrientation::Right + { + scrollDirection = 270.0f; + } + + return scrollDirection; +} + +int GridLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + switch( direction ) + { + case Control::Left: + { + itemID--; + if( itemID < 0 ) + { + itemID = loopEnabled ? maxItems - 1 : 0; + } + break; + } + case Control::Up: + { + itemID -= mImpl->mNumberOfColumns; + if( itemID < 0 ) + { + itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns; + } + break; + } + case Control::Right: + { + itemID++; + if( itemID >= maxItems ) + { + itemID = loopEnabled ? 0 : maxItems - 1; + } + break; + } + case Control::Down: + { + itemID += mImpl->mNumberOfColumns; + if( itemID >= maxItems ) + { + itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns; + } + break; + } + } + return itemID; +} + +GridLayout::GridLayout() +: mImpl(NULL) +{ + mImpl = new Impl(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.cpp new file mode 100644 index 0000000..6a4d5a7 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/item-factory.cpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +ItemFactory::~ItemFactory() +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.cpp new file mode 100644 index 0000000..177c1d8 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/item-layout.cpp @@ -0,0 +1,241 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +ItemLayout::ItemLayout() +: mOrientation(ControlOrientation::Up) +{ +} + +ItemLayout::~ItemLayout() +{ +} + +void ItemLayout::SetOrientation(ControlOrientation::Type orientation) +{ + mOrientation = orientation; +} + +ControlOrientation::Type ItemLayout::GetOrientation() const +{ + return mOrientation; +} + +float ItemLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) +{ + ItemLayout::Vector3Function positionConstraint; + Vector3 itemPosition = Vector3::ZERO; + if (GetPositionConstraint(itemID, positionConstraint)) + { + itemPosition = positionConstraint(Vector3::ZERO, currentLayoutPosition + itemID, 0.0f, layoutSize); + } + Vector3 itemSize; + GetItemSize(itemID, layoutSize, itemSize); + Vector3 onScreenArea = (layoutSize - itemSize) * 0.5f; + if (itemPosition.x < -onScreenArea.x + || itemPosition.x > onScreenArea.x + || itemPosition.y < -onScreenArea.y + || itemPosition.y > onScreenArea.y) + { + // item not within viewable area + // safest thing to do here since we have no idea how the implementation will work is to return the scroll to position + return GetItemScrollToPosition(itemID); + } + return currentLayoutPosition; +} + +void ItemLayout::GetXAxisScrollHint(Vector2& scrollHint) const +{ + scrollHint = Vector2::ZERO; + Radian scrollAngle(GetScrollDirection()); + Vector2 scrollDirection(sinf(scrollAngle), cosf(scrollAngle)); + switch(mOrientation) + { + case ControlOrientation::Up: + { + if(fabsf(scrollDirection.y) < Math::MACHINE_EPSILON_1) + { + // we probably want x scrolling + if(scrollDirection.x > 0.0f) + { + // normal positive scrolling + scrollHint = Vector2::XAXIS; + } + else + { + scrollHint = -Vector2::XAXIS; + } + } + break; + } + case ControlOrientation::Down: + { + if(fabsf(scrollDirection.y) < Math::MACHINE_EPSILON_1) + { + // we probably want x scrolling + if(scrollDirection.x > 0.0f) + { + // normal positive scrolling + scrollHint = -Vector2::XAXIS; + } + else + { + scrollHint = Vector2::XAXIS; + } + } + break; + } + case ControlOrientation::Left: + { + // we probably want x scrolling + if(scrollDirection.x > 0.0f) + { + // normal positive scrolling + scrollHint = Vector2::XAXIS; + } + else + { + scrollHint = -Vector2::XAXIS; + } + break; + } + case ControlOrientation::Right: + { + // we probably want x scrolling + if(scrollDirection.x > 0.0f) + { + // normal positive scrolling + scrollHint = -Vector2::XAXIS; + } + else + { + scrollHint = Vector2::XAXIS; + } + break; + } + } +} + +void ItemLayout::GetYAxisScrollHint(Vector2& scrollHint) const +{ + scrollHint = Vector2::ZERO; + Radian scrollAngle(GetScrollDirection()); + Vector2 scrollDirection(sinf(scrollAngle), cosf(scrollAngle)); + switch(mOrientation) + { + case ControlOrientation::Up: + { + // we probably want x scrolling + if(scrollDirection.y > 0.0f) + { + // normal positive scrolling + scrollHint = Vector2::YAXIS; + } + else + { + scrollHint = -Vector2::YAXIS; + } + break; + } + case ControlOrientation::Down: + { + // we probably want x scrolling + if(scrollDirection.y > 0.0f) + { + // normal positive scrolling + scrollHint = -Vector2::YAXIS; + } + else + { + scrollHint = Vector2::YAXIS; + } + break; + } + case ControlOrientation::Left: + { + if(fabsf(scrollDirection.x) < Math::MACHINE_EPSILON_1) + { + // we probably want x scrolling + if(scrollDirection.y > 0.0f) + { + // normal positive scrolling + scrollHint = -Vector2::YAXIS; + } + else + { + scrollHint = Vector2::YAXIS; + } + } + break; + } + case ControlOrientation::Right: + { + if(fabsf(scrollDirection.x) < Math::MACHINE_EPSILON_1) + { + // we probably want x scrolling + if(scrollDirection.y > 0.0f) + { + // normal positive scrolling + scrollHint = Vector2::YAXIS; + } + else + { + scrollHint = -Vector2::YAXIS; + } + } + break; + } + } +} + +int ItemLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +{ + switch( direction ) + { + case Control::Left: + case Control::Up: + { + itemID--; + if( itemID < 0 ) + { + itemID = loopEnabled ? maxItems - 1 : 0; + } + break; + } + case Control::Right: + case Control::Down: + { + itemID++; + if( itemID >= maxItems ) + { + itemID = loopEnabled ? 0 : maxItems - 1; + } + break; + } + } + return itemID; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/item-view.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/item-view.cpp new file mode 100644 index 0000000..ddafb28 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/item-view.cpp @@ -0,0 +1,192 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +ItemView::ItemView() +{ +} + +ItemView::ItemView(Internal::ItemView& implementation) +: Scrollable(implementation) +{ +} + +ItemView::ItemView( Dali::Internal::CustomActor* internal ) +: Scrollable(internal) +{ + VerifyCustomActorPointer(internal); +} + +ItemView::ItemView( const ItemView& itemView ) +: Scrollable(itemView) +{ +} + +ItemView& ItemView::operator=( const ItemView& itemView ) +{ + if( &itemView != this ) + { + Control::operator=( itemView ); + } + return *this; +} + +ItemView ItemView::New(ItemFactory& factory) +{ + return Internal::ItemView::New(factory); +} + +ItemView ItemView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +ItemView::~ItemView() +{ +} + +unsigned int ItemView::GetLayoutCount() const +{ + return GetImpl(*this).GetLayoutCount(); +} + +void ItemView::AddLayout(ItemLayout& layout) +{ + GetImpl(*this).AddLayout(layout); +} + +void ItemView::RemoveLayout(unsigned int layoutIndex) +{ + GetImpl(*this).RemoveLayout(layoutIndex); +} + +ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const +{ + return GetImpl(*this).GetLayout(layoutIndex); +} + +ItemLayoutPtr ItemView::GetActiveLayout() const +{ + return GetImpl(*this).GetActiveLayout(); +} + +float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const +{ + return GetImpl(*this).GetCurrentLayoutPosition(itemId); +} + +void ItemView::ActivateLayout(unsigned int layoutIndex, Vector3 targetSize, float durationSeconds) +{ + GetImpl(*this).ActivateLayout(layoutIndex, targetSize, durationSeconds); +} + +void ItemView::DeactivateCurrentLayout() +{ + GetImpl(*this).DeactivateCurrentLayout(); +} + +void ItemView::SetMinimumSwipeSpeed(float speed) +{ + GetImpl(*this).SetMinimumSwipeSpeed(speed); +} + +float ItemView::GetMinimumSwipeSpeed() const +{ + return GetImpl(*this).GetMinimumSwipeSpeed(); +} + +void ItemView::SetMinimumSwipeDistance(float distance) +{ + GetImpl(*this).SetMinimumSwipeDistance(distance); +} + +float ItemView::GetMinimumSwipeDistance() const +{ + return GetImpl(*this).GetMinimumSwipeDistance(); +} + +void ItemView::SetMouseWheelScrollDistanceStep(float step) +{ + GetImpl(*this).SetMouseWheelScrollDistanceStep(step); +} + +float ItemView::GetMouseWheelScrollDistanceStep() const +{ + return GetImpl(*this).GetMouseWheelScrollDistanceStep(); +} + +void ItemView::SetAnchoring(bool enabled) +{ + GetImpl(*this).SetAnchoring(enabled); +} + +bool ItemView::GetAnchoring() const +{ + return GetImpl(*this).GetAnchoring(); +} + +void ItemView::SetAnchoringDuration(float durationSeconds) +{ + GetImpl(*this).SetAnchoringDuration(durationSeconds); +} + +float ItemView::GetAnchoringDuration() const +{ + return GetImpl(*this).GetAnchoringDuration(); +} + +void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds) +{ + GetImpl(*this).ScrollToItem(itemId, durationSeconds); +} + +void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds) +{ + GetImpl(*this).SetRefreshInterval(intervalMilliseconds); +} + +unsigned int ItemView::GetRefreshInterval() const +{ + return GetImpl(*this).GetRefreshInterval(); +} + +Actor ItemView::GetItem(unsigned int itemId) const +{ + return GetImpl(*this).GetItem(itemId); +} + +unsigned int ItemView::GetItemId(Actor actor) const +{ + return GetImpl(*this).GetItemId(actor); +} + +void ItemView::RemoveItem(unsigned int itemId, float durationSeconds) +{ + GetImpl(*this).RemoveItem(itemId, durationSeconds); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.cpp new file mode 100755 index 0000000..295c2f4 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.cpp @@ -0,0 +1,579 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ +const unsigned int DEFAULT_NUMBER_OF_COLUMNS = 3; +const float DEFAULT_TOP_MARGIN = 0.3f; +const float DEFAULT_BOTTOM_MARGIN = 0.3f; +const float DEFAULT_SIDE_MARGIN = 0.2f; +const float DEFAULT_COLUMN_SPACING = 20.0f; +const float DEFAULT_ROW_SPACING = 20.0f; +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 3.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.05f; +const float DEFAULT_SIZE_EXTEND = 1.4f; +const float DEFAULT_HEIGHT_FACTOR = 0.6f; + +// 4 orientations are supported +struct NavigationPositionConstraintUp +{ + NavigationPositionConstraintUp(const unsigned int columnIndex, + const unsigned int numberOfColumns, + const float columnSpacing, + const float sizeExtend, + const float bottomMargin, + const float topMargin) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mColumnSpacing(columnSpacing), + mSizeExtend(sizeExtend), + mBottomMargin(bottomMargin), + mTopMargin(topMargin) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemWidth = (layoutSize.width * mSizeExtend- mColumnSpacing * (mNumberOfColumns - 1))/( mNumberOfColumns ); + + Vector3 itemPosition; + + float z = (sinf((layoutPosition + 1.0f) * Math::PI *0.5f) - 1.0f) * itemWidth * 2.0f; + + itemPosition = Vector3( (layoutPosition + 1.0f) * (itemWidth + mColumnSpacing) + itemWidth * 0.5f - layoutSize.width * mSizeExtend * 0.5f, + (- mBottomMargin + mTopMargin) * layoutSize.width * 0.5f , + z); + return itemPosition; + } + +public: + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mColumnSpacing; + float mSizeExtend; + float mBottomMargin; + float mTopMargin; +}; + +struct NavigationPositionConstraintLeft +{ + NavigationPositionConstraintLeft(const unsigned int columnIndex, + const unsigned int numberOfColumns, + const float columnSpacing, + const float sizeExtend, + const float bottomMargin, + const float topMargin) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mColumnSpacing(columnSpacing), + mSizeExtend(sizeExtend), + mBottomMargin(bottomMargin), + mTopMargin(topMargin) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemWidth = (DEFAULT_HEIGHT_FACTOR * layoutSize.height * mSizeExtend- mColumnSpacing * (mNumberOfColumns - 1))/( mNumberOfColumns ); + Vector3 itemPosition; + float z = (sinf((layoutPosition + 1.0f) * Math::PI *0.5f) - 1.0f) * itemWidth * 1.5f; + itemPosition = Vector3( (- mBottomMargin + mTopMargin) * 0.5f * layoutSize.width , + -((layoutPosition+ 1.0) * (itemWidth + mColumnSpacing) + itemWidth * 0.5f - DEFAULT_HEIGHT_FACTOR * layoutSize.height * mSizeExtend * 0.5f ), + z); + return itemPosition; + } + +public: + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mColumnSpacing; + float mSizeExtend; + float mBottomMargin; + float mTopMargin; +}; + +struct NavigationPositionConstraintDown +{ + NavigationPositionConstraintDown(const unsigned int columnIndex, + const unsigned int numberOfColumns, + const float columnSpacing, + const float sizeExtend, + const float bottomMargin, + const float topMargin) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mColumnSpacing(columnSpacing), + mSizeExtend(sizeExtend), + mBottomMargin(bottomMargin), + mTopMargin(topMargin) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemWidth = (layoutSize.width * mSizeExtend- mColumnSpacing * (mNumberOfColumns - 1))/( mNumberOfColumns ); + Vector3 itemPosition; + + float z = (sinf((layoutPosition + 1.0f ) * Math::PI *0.5f) - 1.0f) * itemWidth * 2.0f; + itemPosition = Vector3( -((layoutPosition + 1.0f) * (itemWidth + mColumnSpacing) + itemWidth * 0.5f - layoutSize.width * mSizeExtend * 0.5f), + (- mBottomMargin + mTopMargin)* layoutSize.width * 0.5f, + z); + return itemPosition; + } + + public: + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mColumnSpacing; + float mSizeExtend; + float mBottomMargin; + float mTopMargin; +}; + +struct NavigationPositionConstraintRight +{ + NavigationPositionConstraintRight(const unsigned int columnIndex, + const unsigned int numberOfColumns, + const float columnSpacing, + const float sizeExtend, + const float bottomMargin, + const float topMargin) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mColumnSpacing(columnSpacing), + mSizeExtend(sizeExtend), + mBottomMargin(bottomMargin), + mTopMargin(topMargin) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemWidth = (DEFAULT_HEIGHT_FACTOR * layoutSize.height * mSizeExtend- mColumnSpacing * (mNumberOfColumns - 1))/( mNumberOfColumns ); + Vector3 itemPosition; + float z = (sinf((layoutPosition + 1.0f) * Math::PI *0.5f) - 1.0f) * itemWidth * 1.5f; + itemPosition = Vector3( (- mBottomMargin + mTopMargin) * layoutSize.width * 0.5f, + ((layoutPosition + 1.0f) * (itemWidth + mColumnSpacing) + itemWidth * 0.5f - DEFAULT_HEIGHT_FACTOR * layoutSize.height * mSizeExtend *0.5f ), + z); + return itemPosition; + } + +public: + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mColumnSpacing; + float mSizeExtend; + float mBottomMargin; + float mTopMargin; +}; + +struct NavigationRotationConstraintUp +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = 0.0f; + float _layoutPosition = layoutPosition + 1.0f; + if(_layoutPosition >= 1.0f) + { + angle = - sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + else + { + angle = sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + return Quaternion(angle, Vector3::YAXIS); + } + +}; + +struct NavigationRotationConstraintLeft +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = 0.0f; + float _layoutPosition = layoutPosition + 1.0f; + if(_layoutPosition >= 1.0f) + { + angle = - sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + else + { + angle = sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + return Quaternion(Math::PI * 0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } +}; + +struct NavigationRotationConstraintDown +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = 0.0f; + float _layoutPosition = layoutPosition + 1.0f; + if(_layoutPosition >= 1.0f)//right side + { + //rotation angle by z axis + angle = - sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + else // left side + { + angle = sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + return Quaternion(Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } +}; + +struct NavigationRotationConstraintRight +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = 0.0f; + float _layoutPosition = layoutPosition + 1.0f; + if(_layoutPosition >= 1.0f) + { + angle = - sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + else + { + angle = sinf(Math::PI * _layoutPosition) * Math::PI * 0.2f; + } + return Quaternion(Math::PI * 1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } +}; + +struct NavigationColorConstraint +{ + NavigationColorConstraint(unsigned int numberOfColumns) + : mNumberOfColumns(numberOfColumns) + { + + } + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float darkness = 1.0f; + float alpha = 1.0f; + + float pos = ( layoutPosition + 1.0f); + darkness = (-0.25f) * (pos + 1.0f) * (pos + 1.0f) + 1.0f * (pos + 1.0f) + 0.2f; + + darkness = fabs(darkness); + darkness /= 1.2f; + + return Vector4(darkness, darkness, darkness, current.a * alpha); + } + unsigned int mNumberOfColumns; + +}; + +struct NavigationVisibilityConstraint +{ + NavigationVisibilityConstraint(const unsigned int columnIndex, + const unsigned int numberOfColumns, + const float columnSpacing ) + : mColumnIndex(columnIndex), + mNumberOfColumns(numberOfColumns), + mColumnSpacing(columnSpacing) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float index = layoutPosition + 1.0f; + return (index >= -1.0f) && (index <= mNumberOfColumns ); + } + +public: + unsigned int mColumnIndex; + unsigned int mNumberOfColumns; + float mColumnSpacing; +}; +}//end namespace + +namespace Dali +{ +namespace Toolkit +{ +struct NavigationLayout::Impl +{ + Impl() + : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS), + mColumnSpacing(DEFAULT_COLUMN_SPACING), + mTopMargin(DEFAULT_TOP_MARGIN), + mBottomMargin(DEFAULT_BOTTOM_MARGIN), + mSideMargin(DEFAULT_SIDE_MARGIN), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION), + mSizeExtend(DEFAULT_SIZE_EXTEND) + { + mColorConstraint = NavigationColorConstraint(mNumberOfColumns); + mRotationConstraint[0] = NavigationRotationConstraintUp(); + mRotationConstraint[1] = NavigationRotationConstraintLeft(); + mRotationConstraint[2] = NavigationRotationConstraintDown(); + mRotationConstraint[3] = NavigationRotationConstraintRight(); + } + + unsigned int mNumberOfColumns; + float mColumnSpacing; + float mTopMargin; + float mBottomMargin; + float mSideMargin; + float mScrollSpeedFactor; + float mMaximumSwipeSpeed; + float mItemFlickAnimationDuration; + float mSizeExtend; + + ItemLayout::QuaternionFunction mRotationConstraint[4]; + + ItemLayout::Vector4Function mColorConstraint; + + NavigationLayout::NavigationSignalV2 mSignalPanV2;/*the signal to notify the application the selected item*/ +}; + +NavigationLayoutPtr NavigationLayout::New() +{ + return NavigationLayoutPtr(new NavigationLayout()); +} + +NavigationLayout::~NavigationLayout() +{ + delete mImpl; +} + +void NavigationLayout::SetNumberOfColumns(unsigned int columns) +{ + mImpl->mNumberOfColumns = columns; +} + +unsigned int NavigationLayout::GetNumberOfColumns() const +{ + return mImpl->mNumberOfColumns; +} + +void NavigationLayout::SetColumnSpacing(float spacing) +{ + mImpl->mColumnSpacing = spacing; +} + +float NavigationLayout::GetColumnSpacing() const +{ + return mImpl->mColumnSpacing; +} + +void NavigationLayout::SetTopMargin(float margin) +{ + mImpl->mTopMargin = margin; +} + +float NavigationLayout::GetTopMargin() const +{ + return mImpl->mTopMargin; +} + +void NavigationLayout::SetBottomMargin(float margin) +{ + mImpl->mBottomMargin = margin; +} + +float NavigationLayout::GetBottomMargin() const +{ + return mImpl->mBottomMargin; +} + +void NavigationLayout::SetSideMargin(float margin) +{ + mImpl->mSideMargin = margin; + mImpl->mSizeExtend = (1.0f - margin) * 3.0f; +} + +void NavigationLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void NavigationLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void NavigationLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float NavigationLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float NavigationLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float NavigationLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float NavigationLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + unsigned int itemsLastRow = numberOfItems % mImpl->mNumberOfColumns; + if (itemsLastRow == 0) + { + itemsLastRow = mImpl->mNumberOfColumns; + } + + float itemsLastPage = static_cast(itemsLastRow); + return itemsLastPage - static_cast(numberOfItems) - 2.0f; + +} + +float NavigationLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + return round(layoutPosition); +} + +float NavigationLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + return - static_cast(itemId); +} + +ItemRange NavigationLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + int itemsPerPage = mImpl->mNumberOfColumns; + + int firstItemIndex = std::max(0.0f, -firstItemPosition -1.0f ); + int lastItemIndex = std::max(0.0f, -(firstItemPosition) + itemsPerPage ); + + mImpl->mSignalPanV2.Emit(lastItemIndex - mImpl->mNumberOfColumns); + return ItemRange(firstItemIndex , lastItemIndex ); +} + +unsigned int NavigationLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float itemWidth = (layoutWidth * mImpl->mSizeExtend - mImpl->mColumnSpacing * (mImpl->mNumberOfColumns - 1))/( mImpl->mNumberOfColumns ); + return static_cast(layoutWidth / itemWidth); +} + +bool NavigationLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? (DEFAULT_HEIGHT_FACTOR * layoutSize.height) : layoutSize.width; + layoutWidth = layoutWidth * mImpl->mSizeExtend; + + float itemWidth = (layoutWidth - mImpl->mColumnSpacing*(mImpl->mNumberOfColumns-1)) / mImpl->mNumberOfColumns; + float itemHeight = layoutWidth * (1.0f - mImpl->mBottomMargin - mImpl->mTopMargin); + itemSize = Vector3(itemWidth, itemHeight, (itemWidth/4)*3); + + return true; +} + +void NavigationLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ +} + +bool NavigationLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + unsigned int columnIndex = itemId % mImpl->mNumberOfColumns; + if (mOrientation == ControlOrientation::Left) + { + constraint = NavigationPositionConstraintLeft(columnIndex, mImpl->mNumberOfColumns, mImpl->mColumnSpacing, mImpl->mSizeExtend, mImpl->mBottomMargin, mImpl->mTopMargin); + } + else if (mOrientation == ControlOrientation::Up) + { + constraint = NavigationPositionConstraintUp(columnIndex, mImpl->mNumberOfColumns, mImpl->mColumnSpacing, mImpl->mSizeExtend, mImpl->mBottomMargin, mImpl->mTopMargin); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = NavigationPositionConstraintDown(columnIndex, mImpl->mNumberOfColumns, mImpl->mColumnSpacing, mImpl->mSizeExtend, mImpl->mBottomMargin, mImpl->mTopMargin); + } + else if (mOrientation == ControlOrientation::Right) + { + constraint = NavigationPositionConstraintRight(columnIndex, mImpl->mNumberOfColumns, mImpl->mColumnSpacing, mImpl->mSizeExtend, mImpl->mBottomMargin, mImpl->mTopMargin); + } + + return true; +} + +bool NavigationLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + return false; // No scaling +} + +bool NavigationLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + constraint = mImpl->mRotationConstraint[mOrientation]; + return true; +} + +bool NavigationLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = mImpl->mColorConstraint; + return true; +} + +bool NavigationLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + unsigned int columnIndex = itemId % mImpl->mNumberOfColumns; + constraint = NavigationVisibilityConstraint(columnIndex, mImpl->mNumberOfColumns, mImpl->mColumnSpacing); + return true; +} + +NavigationLayout::NavigationSignalV2& NavigationLayout::PanSignal() +{ + return mImpl->mSignalPanV2; +} + +Degree NavigationLayout::GetScrollDirection() const +{ + Degree scrollDirection(0); + if (mOrientation == ControlOrientation::Down) + { + scrollDirection = 0.0f - 45.0f; + } + else if (mOrientation == ControlOrientation::Right) + { + scrollDirection = 90.0f - 45.0f; + } + else if (mOrientation == ControlOrientation::Up) + { + scrollDirection = 180.0f - 45.0f; + } + else // mOrientation == ControlOrientation::Left + { + scrollDirection = 270.0f - 45.0f; + } + + return scrollDirection; +} + +NavigationLayout::NavigationLayout() + :mImpl(NULL) +{ + mImpl = new Impl(); +} + +} +} diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.h b/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.h new file mode 100755 index 0000000..050827b --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/navigation-layout.h @@ -0,0 +1,246 @@ +#ifndef __DALI_TOOLKIT_NAVIGATION_LAYOUT_H__ +#define __DALI_TOOLKIT_NAVIGATION_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ +namespace Toolkit +{ +class NavigationLayout; + +typedef IntrusivePtr NavigationLayoutPtr; + +/** + * An ItemView layout which arranges items in navigation mode. + */ +class NavigationLayout: public ItemLayout +{ +public: + + typedef boost::function ResizeFunction; + + /** + * @deprecated This should not have been added to an ItemLayout + */ + typedef SignalV2< void (int) > NavigationSignalV2; + + /** + * Create a new navigation layout + */ + static NavigationLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~NavigationLayout(); + + /** + * Get the pan signal + */ + NavigationSignalV2& PanSignal(); + + /** + * Set the number of columns in the layout. + * @param[in] columns The number of columns. + */ + void SetNumberOfColumns(unsigned int columns); + + /** + * Get the number of columns in the layout. + * @return The number of columns. + */ + unsigned int GetNumberOfColumns() const; + + /** + * Set the spacing between columns. + * @param[in] spacing The spacing. + */ + void SetColumnSpacing(float spacing); + + /** + * Get the spacing between columns. + * @return The spacing. + */ + float GetColumnSpacing() const; + + /** + * Set the margin in the top of the layout + * @param[in] margin The layout top margin. + */ + void SetTopMargin(float margin); + + /** + * Get the margin in the top of the layout + * @return The layout top margin. + */ + float GetTopMargin() const; + + /** + * Set the margin in the bottom of the layout + * @param[in] margin The layout bottom margin. + */ + void SetBottomMargin(float margin); + + /** + * Get the margin in the bottom of the layout + * @return The layout bottom margin. + */ + float GetBottomMargin() const; + + /** + * Set the margin in the left and right of the layout + * @param[in] margin The layout side margin. + */ + void SetSideMargin(float margin); + + /** + * Get the margin in the left and right of the layout + * @return The layout side margin. + */ + float GetSideMargin() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, + Actor actor, + Vector3 size, + float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also GridLayout::New() + */ + NavigationLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} +} +#endif //__DALI_TOOLKIT_NAVIGATION_LAYOUT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.cpp new file mode 100644 index 0000000..b55cb05 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.cpp @@ -0,0 +1,588 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ + +const float DEFAULT_ROW_SPACING = 20.0f; +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.0015f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 8.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.4f; + +// 4 orientations are supported +static const unsigned int ORIENTATION_COUNT = 4; + +static Vector3 GetItemSizeDefaultFunction(float layoutWidth, float layoutHeight, float rowSpacing) +{ + float height = (layoutHeight - rowSpacing) * 0.5f; + return Vector3(layoutWidth, height, height); +} + +struct RollPositionConstraint0 +{ + RollPositionConstraint0(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing); + + float adjustedLayoutPosition = layoutPosition; + float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed * scrollSpeed; + float y = 0.0f; + + float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor; + + if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0) + { + float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f); + adjustedLayoutPosition = adjustment * 2.0f; + y = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.height * 0.5f + itemSize.y * 0.5f; + } + else + { + float yStep = max(50.0f, min(itemSize.y, scrollSpeedFactor)); + y = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * yStep : (layoutSize.height * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * yStep; + y += itemSize.y * 0.5f - layoutSize.height * 0.5f; + } + + float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor); + z -= min(3000.0f, scrollSpeedFactor * 2.0f); + + return Vector3(itemSize.x * 0.5f - layoutSize.x * 0.5f, y, z); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct RollPositionConstraint90 +{ + RollPositionConstraint90(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing); + + float adjustedLayoutPosition = layoutPosition; + float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed; + float x = 0.0f; + + float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor; + + if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0) + { + float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f); + adjustedLayoutPosition = adjustment * 2.0f; + x = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.width * 0.5f + itemSize.y * 0.5f; + } + else + { + float xStep = max(50.0f, min(itemSize.y, scrollSpeedFactor)); + x = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * xStep : (layoutSize.width * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * xStep; + x += itemSize.y * 0.5f - layoutSize.width * 0.5f; + } + + float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor); + z -= min(3000.0f, scrollSpeedFactor * 2.0f); + + return Vector3(x, itemSize.x * 0.5f - layoutSize.y * 0.5f, z); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct RollPositionConstraint180 +{ + RollPositionConstraint180(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing); + + float adjustedLayoutPosition = layoutPosition; + float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed; + float y = 0.0f; + + float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor; + + if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0) + { + float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f); + adjustedLayoutPosition = adjustment * 2.0f; + y = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.height * 0.5f + itemSize.y * 0.5f; + } + else + { + float yStep = max(50.0f, min(itemSize.y, scrollSpeedFactor)); + y = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * yStep : (layoutSize.height * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * yStep; + y += itemSize.y * 0.5f - layoutSize.height * 0.5f; + } + + float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor); + z -= min(3000.0f, scrollSpeedFactor * 2.0f); + + + return Vector3(-(itemSize.x * 0.5f - layoutSize.x * 0.5f), + -y, + z); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct RollPositionConstraint270 +{ + RollPositionConstraint270(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing); + + float adjustedLayoutPosition = layoutPosition; + float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed; + float x = 0.0f; + + float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor; + + if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0) + { + float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f); + adjustedLayoutPosition = adjustment * 2.0f; + x = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.width * 0.5f + itemSize.y * 0.5f; + } + else + { + float xStep = max(50.0f, min(itemSize.y, scrollSpeedFactor)); + x = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * xStep : (layoutSize.width * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * xStep; + x += itemSize.y * 0.5f - layoutSize.width * 0.5f; + } + + float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor); + z -= min(3000.0f, scrollSpeedFactor * 2.0f); + + return Vector3(-x, + itemSize.x * 0.5f - layoutSize.y * 0.5f, + z); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct RollRotationConstraint0 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.0f, Vector3::ZAXIS); + } +}; + +struct RollRotationConstraint90 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + } +}; + +struct RollRotationConstraint180 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(Math::PI, Vector3::ZAXIS); + } +}; + +struct RollRotationConstraint270 +{ + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + return Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + } +}; + +struct RollScaleConstraint +{ + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float adjustedLayoutPosition = layoutPosition; + float factor = 0.0f; + if(adjustedLayoutPosition < Math::MACHINE_EPSILON_0) + { + factor = fabsf(adjustedLayoutPosition); + } + if(adjustedLayoutPosition - 1.0f > Math::MACHINE_EPSILON_0) + { + factor = adjustedLayoutPosition - 1.0f; + } + + float scale = min(1.0f, max(0.1f, 1.0f - 0.1f * factor)); + if(scrollSpeed > 0.0f) + { + scale *= min(1.0f, max(0.1f, 1.0f / (scrollSpeed * 0.05f))); + } + + return Vector3(scale, scale, scale); + } +}; + +struct RollColorConstraint +{ + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float adjustedLayoutPosition = layoutPosition; + + float factor = 0.0f; + if(adjustedLayoutPosition < Math::MACHINE_EPSILON_0) + { + factor = fabsf(adjustedLayoutPosition); + } + if(adjustedLayoutPosition - 1.0f > Math::MACHINE_EPSILON_0) + { + factor = adjustedLayoutPosition - 1.0f; + } + + float darkness = min(1.0f, max(0.5f, 1.0f - 0.5f * factor)); + float alpha = min(1.0f, max(0.0f, 1.0f - 0.9f * factor)); + return Vector4(darkness, darkness, darkness, alpha); + } +}; + +struct RollVisibilityConstraintPortrait +{ + RollVisibilityConstraintPortrait(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing); + int rowsPerPage = ceil(layoutSize.height / (itemSize.y + mRowSpacing)); + return (layoutPosition > -rowsPerPage) && (layoutPosition < rowsPerPage); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +struct RollVisibilityConstraintLandscape +{ + RollVisibilityConstraintLandscape(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction) + : mRowSpacing(rowSpacing), + mItemSizeFunction(itemSizeFunction) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing); + int rowsPerPage = ceil(layoutSize.width / (itemSize.y + mRowSpacing)); + return (layoutPosition + 2.0f > Math::MACHINE_EPSILON_0) && (layoutPosition < rowsPerPage); + } + +public: + + float mRowSpacing; + RollLayout::ItemSizeFunction mItemSizeFunction; +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +struct RollLayout::Impl +{ + Impl() + : mRowSpacing(DEFAULT_ROW_SPACING), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION), + mItemSizeFunction(GetItemSizeDefaultFunction) + { + mScaleConstraint = RollScaleConstraint(); + mColorConstraint = RollColorConstraint(); + + mRotationConstraint[0] = RollRotationConstraint0(); + mRotationConstraint[1] = RollRotationConstraint90(); + mRotationConstraint[2] = RollRotationConstraint180(); + mRotationConstraint[3] = RollRotationConstraint270(); + } + + float mRowSpacing; + + float mScrollSpeedFactor; + float mMaximumSwipeSpeed; + float mItemFlickAnimationDuration; + + ItemLayout::QuaternionFunction mRotationConstraint[ORIENTATION_COUNT]; + ItemLayout::Vector3Function mScaleConstraint; + ItemLayout::Vector4Function mColorConstraint; + + ItemSizeFunction mItemSizeFunction; +}; + +RollLayoutPtr RollLayout::New() +{ + return RollLayoutPtr(new RollLayout()); +} + +RollLayout::~RollLayout() +{ + delete mImpl; +} + +void RollLayout::SetRowSpacing(float spacing) +{ + mImpl->mRowSpacing = spacing; +} + +float RollLayout::GetRowSpacing() const +{ + return mImpl->mRowSpacing; +} + +void RollLayout::SetItemSizeFunction(ItemSizeFunction function) +{ + mImpl->mItemSizeFunction = function; +} + +RollLayout::ItemSizeFunction RollLayout::GetItemSizeFunction() const +{ + return mImpl->mItemSizeFunction; +} + +void RollLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void RollLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void RollLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float RollLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float RollLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float RollLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float RollLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + return 2.0f - static_cast(numberOfItems); +} + +float RollLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + return static_cast(round(layoutPosition)); +} + +float RollLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + return 0.0f - static_cast(itemId); +} + +ItemRange RollLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + Vector3 itemSize = mImpl->mItemSizeFunction( layoutWidth,layoutHeight, mImpl->mRowSpacing); + + float itemsPerPage = (layoutHeight / (itemSize.y + mImpl->mRowSpacing)); + if(firstItemPosition + 0.001f >= Math::MACHINE_EPSILON_0) + { + itemsPerPage = std::max(0.0f, itemsPerPage - 1.0f); + } + int firstVisibleItem = -(static_cast(firstItemPosition)); + + int firstItemIndex = std::max(0, firstVisibleItem); + int lastItemIndex = std::max(0, static_cast(ceil(firstVisibleItem + itemsPerPage - 1))); + return ItemRange(firstItemIndex, lastItemIndex); +} + +unsigned int RollLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + Vector3 itemSize = mImpl->mItemSizeFunction(layoutWidth, layoutHeight, mImpl->mRowSpacing); + int itemsPerPage = ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing)); + return itemsPerPage * 5; +} + +bool RollLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + // Note: itemId is not checked, since every item has the same size + float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width; + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + + itemSize = mImpl->mItemSizeFunction(layoutWidth, layoutHeight, mImpl->mRowSpacing); + + return true; +} + +void RollLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ + if(animation) + { + animation.Resize(actor, size); + } +} + +bool RollLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + if (mOrientation == ControlOrientation::Up) + { + constraint = RollPositionConstraint0(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = RollPositionConstraint90(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = RollPositionConstraint180(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + else // mOrientation == ControlOrientation::Right + { + constraint = RollPositionConstraint270(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + + return true; +} + +bool RollLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + constraint = mImpl->mRotationConstraint[mOrientation]; + return true; +} + +bool RollLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + constraint = mImpl->mScaleConstraint; + return true; +} + +bool RollLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = mImpl->mColorConstraint; + return true; +} + +bool RollLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + if (IsVertical(mOrientation)) + { + constraint = RollVisibilityConstraintPortrait(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + else // horizontal + { + constraint = RollVisibilityConstraintLandscape(mImpl->mRowSpacing, mImpl->mItemSizeFunction); + } + + return true; +} + +Degree RollLayout::GetScrollDirection() const +{ + Degree scrollDirection(0.0f); + + if (mOrientation == ControlOrientation::Up) + { + scrollDirection = 0.0f; + } + else if (mOrientation == ControlOrientation::Left) + { + scrollDirection = 90.0f; + } + else if (mOrientation == ControlOrientation::Down) + { + scrollDirection = 180.0f; + } + else // mOrientation == ControlOrientation::Right + { + scrollDirection = 270.0f; + } + + return scrollDirection; +} + +RollLayout::RollLayout() +: mImpl(NULL) +{ + mImpl = new Impl(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.h b/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.h new file mode 100644 index 0000000..67c4e53 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/roll-layout.h @@ -0,0 +1,198 @@ +#ifndef __DALI_TOOLKIT_ROLL_LAYOUT_H__ +#define __DALI_TOOLKIT_ROLL_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class RollLayout; + +typedef IntrusivePtr RollLayoutPtr; + +/** + * An ItemView layout which arranges items in a roll. + */ +class RollLayout : public ItemLayout +{ +public: + + typedef boost::function ItemSizeFunction; + + /** + * Create a new roll layout + */ + static RollLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~RollLayout(); + + /** + * Set the spacing between rows. + * @param[in] spacing The row spacing. + */ + void SetRowSpacing(float spacing); + + /** + * Get the spacing between rows. + * @return The row spacing. + */ + float GetRowSpacing() const; + + /** + * Set the function used to calculate the item-size, for a given layout-size. + * @param[in] function The item-size function. + */ + void SetItemSizeFunction(ItemSizeFunction function); + + /** + * Get the function used to calculate the item-size. + * @return The item-size function. + */ + ItemSizeFunction GetItemSizeFunction() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also RollLayout::New() + */ + RollLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_ROLL_LAYOUT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.cpp b/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.cpp new file mode 100644 index 0000000..fd142f1 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.cpp @@ -0,0 +1,599 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +using namespace Dali; +using namespace Dali::Toolkit; +using namespace std; + +namespace // unnamed namespace +{ + +const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f; +const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN; + +const float DEFAULT_REVOLUTION_DISTANCE = 190.0f; +const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN; + +const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f; + +const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f; +const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f; +const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f; + +struct DefaultItemSizeFunction +{ + Vector3 operator()(const Vector3& layoutSize) + { + float width = layoutSize.width * 0.25f; + + // 4x3 aspect ratio + return Vector3(width, (width/4)*3, (width/4)*3); + } +}; + +struct DefaultSpiralRadiusFunction +{ + float operator()(const Vector3& layoutSize) + { + return layoutSize.width*0.4f; + } +}; + +struct SpiralPositionConstraintUp +{ + SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mSpiralRadius(spiralRadius), + mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float spiralRadius = mSpiralRadius(layoutSize); + + float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition; + + return Vector3( -spiralRadius * cosf(angle), + (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment, + -spiralRadius * sinf(angle) ); + } + + SpiralLayout::SpiralRadiusFunction mSpiralRadius; + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +struct SpiralPositionConstraintLeft +{ + SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mSpiralRadius(spiralRadius), + mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float spiralRadius = mSpiralRadius(layoutSize); + + float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition; + + return Vector3( (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment, + -spiralRadius * cosf(angle), + spiralRadius * sinf(angle) ); + } + + SpiralLayout::SpiralRadiusFunction mSpiralRadius; + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +struct SpiralPositionConstraintDown +{ + SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mSpiralRadius(spiralRadius), + mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float spiralRadius = mSpiralRadius(layoutSize); + + float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition; + + return Vector3( -spiralRadius * cosf(angle), + (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment, + spiralRadius * sinf(angle) ); + } + + SpiralLayout::SpiralRadiusFunction mSpiralRadius; + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +struct SpiralPositionConstraintRight +{ + SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mSpiralRadius(spiralRadius), + mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float spiralRadius = mSpiralRadius(layoutSize); + + float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition; + + return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment, + -spiralRadius * cosf(angle), + -spiralRadius * sinf(angle) ); + } + + SpiralLayout::SpiralRadiusFunction mSpiralRadius; + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +struct SpiralRotationConstraintUp +{ + SpiralRotationConstraintUp(float itemSpacingRadians) + : mItemSpacingRadians(itemSpacingRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = -mItemSpacingRadians * layoutPosition; + + return Quaternion(angle, Vector3::YAXIS); + } + + float mItemSpacingRadians; +}; + +struct SpiralRotationConstraintLeft +{ + SpiralRotationConstraintLeft(float itemSpacingRadians) + : mItemSpacingRadians(itemSpacingRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = -mItemSpacingRadians * layoutPosition; + + return Quaternion(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } + + float mItemSpacingRadians; +}; + +struct SpiralRotationConstraintDown +{ + SpiralRotationConstraintDown(float itemSpacingRadians) + : mItemSpacingRadians(itemSpacingRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = -mItemSpacingRadians * layoutPosition; + + return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } + + float mItemSpacingRadians; +}; + +struct SpiralRotationConstraintRight +{ + SpiralRotationConstraintRight(float itemSpacingRadians) + : mItemSpacingRadians(itemSpacingRadians) + { + } + + Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float angle = -mItemSpacingRadians * layoutPosition; + + return Quaternion(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS); + } + + float mItemSpacingRadians; +}; + +struct SpiralColorConstraint +{ + SpiralColorConstraint(float itemSpacingRadians) + : mItemSpacingRadians(itemSpacingRadians) + { + } + + Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition)); + angle = (float)((int)angle % 360); + + float progress = angle / 360.0f; + progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f; + + float darkness(1.0f); + { + const float startMarker = 0.10f; // The progress at which darkening starts + const float endMarker = 0.35f; // The progress at which darkening ends + const float minDarkness = 0.15f; // The darkness at end marker + + if (progress > endMarker) + { + darkness = minDarkness; + } + else if (progress > startMarker) + { + darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) ); + } + } + + return Vector4( darkness, darkness, darkness, current.a ); + } + + float mItemSpacingRadians; +}; + +struct SpiralVisibilityConstraintPortrait +{ + SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent; + return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f); + } + + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +struct SpiralVisibilityConstraintLandscape +{ + SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment) + : mItemSpacingRadians(itemSpacingRadians), + mItemDescent(itemDescent), + mTopItemAlignment(topItemAlignment) + { + } + + bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize) + { + float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent; + return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f); + } + + float mItemSpacingRadians; + float mItemDescent; + float mTopItemAlignment; +}; + +} // unnamed namespace + +namespace Dali +{ + +namespace Toolkit +{ + +struct SpiralLayout::Impl +{ + Impl() + : mItemSizeFunction(DefaultItemSizeFunction()), + mSpiralRadiusFunction(DefaultSpiralRadiusFunction()), + mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS), + mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE), + mItemDescent(DEFAULT_ITEM_DESCENT), + mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT), + mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR), + mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED), + mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION) + { + } + + ItemSizeFunction mItemSizeFunction; + SpiralRadiusFunction mSpiralRadiusFunction; + + float mItemSpacingRadians; + float mRevolutionDistance; + float mItemDescent; + float mTopItemAlignment; + float mScrollSpeedFactor; + float mMaximumSwipeSpeed; + float mItemFlickAnimationDuration; +}; + +SpiralLayoutPtr SpiralLayout::New() +{ + return SpiralLayoutPtr(new SpiralLayout()); +} + +SpiralLayout::~SpiralLayout() +{ + delete mImpl; +} + +void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function) +{ + mImpl->mItemSizeFunction = function; +} + +SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const +{ + return mImpl->mItemSizeFunction; +} + +void SpiralLayout::SetItemSpacing(Radian itemSpacing) +{ + mImpl->mItemSpacingRadians = itemSpacing; + + float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians); + mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral; +} + +Radian SpiralLayout::GetItemSpacing() const +{ + return Radian( mImpl->mItemSpacingRadians ); +} + +void SpiralLayout::SetRevolutionDistance(float distance) +{ + mImpl->mRevolutionDistance = distance; + + float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians); + mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral; +} + +float SpiralLayout::GetRevolutionDistance() const +{ + return mImpl->mRevolutionDistance; +} + +void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function) +{ + mImpl->mSpiralRadiusFunction = function; +} + +SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const +{ + return mImpl->mSpiralRadiusFunction; +} + +void SpiralLayout::SetTopItemAlignment(float alignment) +{ + mImpl->mTopItemAlignment = alignment; +} + +float SpiralLayout::GetTopItemAlignment() const +{ + return mImpl->mTopItemAlignment; +} + +void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed) +{ + mImpl->mScrollSpeedFactor = scrollSpeed; +} + +void SpiralLayout::SetMaximumSwipeSpeed(float speed) +{ + mImpl->mMaximumSwipeSpeed = speed; +} + +void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds) +{ + mImpl->mItemFlickAnimationDuration = durationSeconds; +} + +float SpiralLayout::GetScrollSpeedFactor() const +{ + return mImpl->mScrollSpeedFactor; +} + +float SpiralLayout::GetMaximumSwipeSpeed() const +{ + return mImpl->mMaximumSwipeSpeed; +} + +float SpiralLayout::GetItemFlickAnimationDuration() const +{ + return mImpl->mItemFlickAnimationDuration; +} + +float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const +{ + return 1.0f - static_cast(numberOfItems); +} + +float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const +{ + return round(layoutPosition); +} + +float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const +{ + return -(static_cast(itemId)); +} + +ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const +{ + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + float itemsPerSpiral = layoutHeight / mImpl->mItemDescent; + float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent; + float itemsViewable = min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f); + + unsigned int firstItem = static_cast(max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f)); + unsigned int lastItem = static_cast(max(0.0f, firstItem + itemsViewable)); + + return ItemRange(firstItem, lastItem+1); +} + +unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const +{ + float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height; + return static_cast(layoutHeight / mImpl->mItemDescent); +} + +bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const +{ + // Note: itemId is not checked, since every item has the same size + + itemSize = mImpl->mItemSizeFunction(layoutSize); + return true; +} + +void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const +{ + if(animation) + { + animation.Resize(actor, size); + } +} + +bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + if (mOrientation == ControlOrientation::Up) + { + constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + else // mOrientation == ControlOrientation::Right + { + constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + + return true; +} + +bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const +{ + if (mOrientation == ControlOrientation::Up) + { + constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians); + } + else if (mOrientation == ControlOrientation::Left) + { + constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians); + } + else if (mOrientation == ControlOrientation::Down) + { + constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians); + } + else // mOrientation == ControlOrientation::Right + { + constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians); + } + + return true; +} + +bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const +{ + return false; // No scaling +} + +bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const +{ + constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians); + return true; +} + +bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const +{ + if (IsVertical(mOrientation)) + { + constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + else // horizontal + { + constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment); + } + + return true; +} + +Degree SpiralLayout::GetScrollDirection() const +{ + Degree scrollDirection(0); + + if (mOrientation == ControlOrientation::Up) + { + scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically + } + else if (mOrientation == ControlOrientation::Left) + { + scrollDirection = 90.0f - 45.0f; + } + else if (mOrientation == ControlOrientation::Down) + { + scrollDirection = 180.0f - 45.0f; + } + else // mOrientation == ControlOrientation::Right + { + scrollDirection = 270.0f - 45.0f; + } + + return scrollDirection; +} + +SpiralLayout::SpiralLayout() +: mImpl(NULL) +{ + mImpl = new Impl(); +} + +float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) +{ + return GetItemScrollToPosition(itemID); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h b/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h new file mode 100644 index 0000000..00fd02e --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h @@ -0,0 +1,242 @@ +#ifndef __DALI_TOOLKIT_SPIRAL_LAYOUT_H__ +#define __DALI_TOOLKIT_SPIRAL_LAYOUT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class SpiralLayout; + +typedef IntrusivePtr SpiralLayoutPtr; + +/** + * An ItemView layout which arranges items in a spiral. + */ +class SpiralLayout : public ItemLayout +{ +public: + + typedef boost::function ItemSizeFunction; + typedef boost::function SpiralRadiusFunction; + + /** + * Create a new spiral layout + */ + static SpiralLayoutPtr New(); + + /** + * Virtual destructor. + */ + virtual ~SpiralLayout(); + + /** + * Set the function used to calculate the item-size, for a given layout-size. + * @param[in] function The item-size function. + */ + void SetItemSizeFunction(ItemSizeFunction function); + + /** + * Get the function used to calculate the item-size + * @return The item-size function. + */ + ItemSizeFunction GetItemSizeFunction() const; + + /** + * Set spacing angle between items. + * @param[in] itemSpacing The angle in radians. + */ + void SetItemSpacing(Radian itemSpacing); + + /** + * Get spacing angle between items. + * @return The angle in radians. + */ + Radian GetItemSpacing() const; + + /** + * Set the vertical distance for one revolution of the spiral. + * @param[in] distance The revolution distance. + */ + void SetRevolutionDistance(float distance); + + /** + * Get the vertical distance for one revolution of the spiral. + * @return The revolution distance. + */ + float GetRevolutionDistance() const; + + /** + * Set the function used to calculate the spiral radius, for a given layout-size. + * @param[in] function The spiral-radius function. + */ + void SetSpiralRadiusFunction(SpiralRadiusFunction function); + + /** + * Get the function used to calculate the spiral radius. + * @return The spiral-radius function. + */ + SpiralRadiusFunction GetSpiralRadiusFunction() const; + + /** + * Set the alignment of the top-item, when at the beginning of the spiral (with a first-item layout-position of zero). + * A value of 0 indicates that the top-item is centered in the middle of the layout. A value of -0.5 or 0.5 indicates + * that the top-item is centred at the top or bottom of the layout respectively. + * @param[in] alignment The top-item alignment. + */ + void SetTopItemAlignment(float alignment); + + /** + * Get the alignment of the top-item, when at the beginning of the spiral + * @return The top-item alignment. + */ + float GetTopItemAlignment() const; + + /** + * Set the factor used to customise the scroll speed while dragging and swiping the layout. + * @param[in] scrollSpeed The scroll speed factor. + */ + void SetScrollSpeedFactor(float scrollSpeed); + + /** + * Set the maximum swipe speed in pixels per second. + * @param[in] speed The maximum swipe speed. + */ + void SetMaximumSwipeSpeed(float speed); + + /** + * Set the duration of the flick animation in second. This is the time taken to animate each + * item to its next layout position (e.g. from 1.0 to 2.0) when a flick animation is triggered + * by a swipe gesture. + * @pre durationSeconds must be greater than zero. + * @param[in] durationSeconds The duration of flick animation in seconds. + */ + void SetItemFlickAnimationDuration(float durationSeconds); + + /** + * @copydoc ItemLayout::GetScrollSpeedFactor() + */ + virtual float GetScrollSpeedFactor() const; + + /** + * @copydoc ItemLayout::GetMaximumSwipeSpeed() + */ + virtual float GetMaximumSwipeSpeed() const; + + /** + * @copydoc ItemLayout::GetItemFlickAnimationDuration() + */ + virtual float GetItemFlickAnimationDuration() const; + + /** + * @copydoc ItemLayout::GetClosestOnScreenLayoutPosition() + */ + virtual float GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize); + +private: + + /** + * @copydoc ItemLayout::GetMinimumLayoutPosition() + */ + virtual float GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetClosestAnchorPosition() + */ + virtual float GetClosestAnchorPosition(float layoutPosition) const; + + /** + * @copydoc ItemLayout::GetItemScrollToPosition() + */ + virtual float GetItemScrollToPosition(unsigned int itemId) const; + + /** + * @copydoc ItemLayout::GetItemsWithinArea() + */ + virtual ItemRange GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetReserveItemCount() + */ + virtual unsigned int GetReserveItemCount(Vector3 layoutSize) const; + + /** + * @copydoc ItemLayout::GetItemSize() + */ + virtual bool GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const; + + /** + * @copydoc ItemLayout::GetResizeAnimation() + */ + virtual void GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const; + + /** + * @copydoc ItemLayout::GetPositionConstraint() + */ + virtual bool GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetRotationConstraint() + */ + virtual bool GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScaleConstraint() + */ + virtual bool GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const; + + /** + * @copydoc ItemLayout::GetColorConstraint() + */ + virtual bool GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const; + + /** + * @copydoc ItemLayout::GetVisibilityConstraint() + */ + virtual bool GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const; + + /** + * @copydoc ItemLayout::GetScrollDirection() + */ + virtual Degree GetScrollDirection() const; + +protected: + + /** + * Protected constructor; see also SpiralLayout::New() + */ + SpiralLayout(); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SPIRAL_LAYOUT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.cpp new file mode 100644 index 0000000..3ccfdaf --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.cpp @@ -0,0 +1,59 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string ScrollViewCarouselEffect::EFFECT_ACTIVATE( "ScrollViewCarouselEffect::EFFECT_ACTIVATE" ); + +ScrollViewCarouselEffect ScrollViewCarouselEffect::New() +{ + return ScrollViewCarouselEffect(new Internal::ScrollViewCarouselEffect()); +} + +ScrollViewCarouselEffect::ScrollViewCarouselEffect() +{ + +} + +ScrollViewCarouselEffect::ScrollViewCarouselEffect(Internal::ScrollViewCarouselEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewCarouselEffect ScrollViewCarouselEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewCarouselEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewCarouselEffect::ApplyToActor(Actor child, const Vector2& angleSwing) +{ + GetImpl(*this).ApplyToActor( child, angleSwing ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.h new file mode 100644 index 0000000..f8070a6 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-carousel-effect.h @@ -0,0 +1,114 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_CAROUSEL_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_CAROUSEL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewCarouselEffect; +} + +/** + * ScrollView Carousel-Effect. + * + * This effect causes Actors to appear to move around around a carousel + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Container + * | + * Child (1..n) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * upon adding children to container, the ApplyToActor(...) method should be called. + * + * Automatic operation: + * not implemented. + * + * Notes: + * * Assumes Actor's AnchorPoint = AnchorPoint::CENTER + */ +class ScrollViewCarouselEffect : public ScrollViewEffect +{ + +public: + + static const std::string EFFECT_ACTIVATE; ///< Activation property (a value between: 0.0 normal/no effect, 1.0 - full effect) + +public: + + /** + * Create an initialized ScrollViewCarouselEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewCarouselEffect New(); + + /** + * Create an uninitialized ScrollViewCarouselEffect; this can be initialized with ScrollViewCarouselEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewCarouselEffect is not allowed. + */ + ScrollViewCarouselEffect(); + + /** + * Downcast an Object handle to ScrollViewCarouselEffect. If handle points to a ScrollViewCarouselEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewCarouselEffect or an uninitialized handle + */ + static ScrollViewCarouselEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) as the page is scrolled. + */ + void ApplyToActor(Actor child, const Vector2& angleSwing); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewCarouselEffect(Internal::ScrollViewCarouselEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_CAROUSEL_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.cpp new file mode 100644 index 0000000..ac29e53 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.cpp @@ -0,0 +1,92 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +Vector3 MoveActorConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty) +{ + return current + scrollPositionProperty.GetVector3(); +} + +Vector3 MoveScaledActorConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollScaleProperty) +{ + return scrollScaleProperty.GetVector3() * (current + scrollPositionProperty.GetVector3()); +} + +Vector3 ScaleActorConstraint(const Vector3& current, + const PropertyInput& scrollScaleProperty) +{ + return current * scrollScaleProperty.GetVector3(); +} + +Vector3 WrapActorConstraint(const Vector3& current, + const PropertyInput& actorScaleProperty, + const PropertyInput& actorAnchorPointProperty, + const PropertyInput& actorSizeProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& scrollWrap) +{ + Vector3 position = current; + bool wrap = scrollWrap.GetBoolean(); + + if(wrap) + { + const Vector3& min = scrollPositionMin.GetVector3(); + const Vector3& max = scrollPositionMax.GetVector3(); + + const Vector3& anchor = actorAnchorPointProperty.GetVector3(); + const Vector3 scale = actorScaleProperty.GetVector3(); + const Vector3 size = actorSizeProperty.GetVector3(); + + if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1) + { + // WRAP X (based on the position of the right side) + float offsetX = (1.0f - anchor.x) * size.x * scale.x; + position.x = WrapInDomain(position.x + offsetX, min.x, max.x) - offsetX; + } + + if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1) + { + // WRAP Y (based on the position of the bottom side) + float offsetY = (1.0f - anchor.y) * size.y * scale.y; + position.y = WrapInDomain(position.y + offsetY, min.y, max.y) - offsetY; + } + } + + return position; +} + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h new file mode 100644 index 0000000..ef93f54 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-constraints.h @@ -0,0 +1,83 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_CONSTRAINTS_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_CONSTRAINTS_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +namespace Dali DALI_IMPORT_API +{ + +struct Vector2; +struct Vector3; +struct Vector4; +class PropertyInput; + +namespace Toolkit +{ + +// Constraints //////////////////////////////////////////////////////////////////////////////////// + +/** + * Useful constraints to apply to a ScrollView. + */ + +/** + * Move Actor constraint. + * + * Moves an Actor in accordance to scroll position. + */ +Vector3 MoveActorConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty); + +/** + * Move-Scaled Actor constraint. + * + * Moves an Actor in accordance to scroll position (and scroll scale). + */ +Vector3 MoveScaledActorConstraint(const Vector3& current, + const PropertyInput& scrollPositionProperty, + const PropertyInput& scrollScaleProperty); + +/** + * Scale Actor constraint. + * + * Scales an Actor in accordance to scroll scale. + */ +Vector3 ScaleActorConstraint(const Vector3& current, + const PropertyInput& scrollScaleProperty); + +/** + * Wrap Actor constraint. + * + * Wraps an Actors position in accordance to min/max bounds of domain. + */ +Vector3 WrapActorConstraint(const Vector3& current, + const PropertyInput& actorScaleProperty, + const PropertyInput& actorAnchorPointProperty, + const PropertyInput& actorSizeProperty, + const PropertyInput& scrollPositionMin, + const PropertyInput& scrollPositionMax, + const PropertyInput& scrollWrap); + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_CONSTRAINTS_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.cpp new file mode 100644 index 0000000..16bc85a --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-cube-effect.cpp @@ -0,0 +1,69 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewCubeEffect ScrollViewCubeEffect::New() +{ + return ScrollViewCubeEffect(new Internal::ScrollViewCubeEffect()); +} + +ScrollViewCubeEffect::ScrollViewCubeEffect() +{ + +} + +ScrollViewCubeEffect::ScrollViewCubeEffect(Internal::ScrollViewCubeEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewCubeEffect ScrollViewCubeEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewCubeEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewCubeEffect::ApplyToActor(Actor child, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing) +{ + GetImpl(*this).ApplyToActor( child, anchor, angleSwing, positionSwing ); +} + +void ScrollViewCubeEffect::ApplyToActor(Actor child, + Actor parentPage, + const Vector3& anchor, + const Vector2& angleSwing, + const Vector2& positionSwing) +{ + GetImpl(*this).ApplyToActor( child, parentPage, anchor, angleSwing, positionSwing ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.cpp new file mode 100644 index 0000000..df0d134 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-custom-effect.cpp @@ -0,0 +1,268 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL HEADERS +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewCustomEffect ScrollViewCustomEffect::New() +{ + return ScrollViewCustomEffect(new Internal::ScrollViewCustomEffect()); +} + +ScrollViewCustomEffect::ScrollViewCustomEffect() +{ + +} + +ScrollViewCustomEffect::ScrollViewCustomEffect(Internal::ScrollViewCustomEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewCustomEffect ScrollViewCustomEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewCustomEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewCustomEffect::SetPageSpacing(const Vector2& spacing) +{ + GetImpl(*this).SetPageSpacing(spacing); +} + +void ScrollViewCustomEffect::SetPageTranslation(const Vector3& translation) +{ + GetImpl(*this).SetPageTranslation(translation); +} + +void ScrollViewCustomEffect::SetPageTranslation(const Vector3& translationIn, const Vector3& translationOut) +{ + GetImpl(*this).SetPageTranslation(translationIn, translationOut); +} + +void ScrollViewCustomEffect::SetPageTranslationIn(const Vector3& translation) +{ + GetImpl(*this).SetPageTranslationIn(translation); +} + +void ScrollViewCustomEffect::SetPageTranslationOut(const Vector3& translation) +{ + GetImpl(*this).SetPageTranslationOut(translation); +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunction(AlphaFunction func) +{ + GetImpl(*this).SetPageTranslateAlphaFunction(func); +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + GetImpl(*this).SetPageTranslateAlphaFunction(funcIn, funcOut); +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunctionIn(AlphaFunction func) +{ + GetImpl(*this).SetPageTranslateAlphaFunctionIn(func); +} + +void ScrollViewCustomEffect::SetPageTranslateAlphaFunctionOut(AlphaFunction func) +{ + GetImpl(*this).SetPageTranslateAlphaFunctionOut(func); +} + +void ScrollViewCustomEffect::SetGlobalPageRotation(float angle, const Vector3& axis) +{ + GetImpl(*this).SetGlobalPageRotation(angle, axis); +} + +void ScrollViewCustomEffect::SetAngledOriginPageRotation(const Vector3& angle) +{ + GetImpl(*this).SetAngledOriginPageRotation(angle); +} + +void ScrollViewCustomEffect::SetGlobalPageRotation(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut) +{ + GetImpl(*this).SetGlobalPageRotation(angleIn, axisIn, angleOut, axisOut); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationIn(float angle, const Vector3& axis) +{ + GetImpl(*this).SetGlobalPageRotationIn(angle, axis); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOut(float angle, const Vector3& axis) +{ + GetImpl(*this).SetGlobalPageRotationOut(angle, axis); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOrigin(const Vector3& origin) +{ + GetImpl(*this).SetGlobalPageRotationOrigin(origin); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOrigin(const Vector3& originIn, const Vector3& originOut) +{ + GetImpl(*this).SetGlobalPageRotationOrigin(originIn, originOut); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOriginIn(const Vector3& origin) +{ + GetImpl(*this).SetGlobalPageRotationOriginIn(origin); +} + +void ScrollViewCustomEffect::SetGlobalPageRotationOriginOut(const Vector3& origin) +{ + GetImpl(*this).SetGlobalPageRotationOriginOut(origin); +} + +void ScrollViewCustomEffect::SetSwingAngle(const float angle, const Vector3& axis) +{ + GetImpl(*this).SetSwingAngle(angle, axis); +} + +void ScrollViewCustomEffect::SetSwingAngle(float angleIn, const Vector3& axisIn, float angleOut, const Vector3& axisOut) +{ + GetImpl(*this).SetSwingAngle(angleIn, axisIn, angleOut, axisOut); +} + +void ScrollViewCustomEffect::SetSwingAngleIn(float angle, const Vector3& axis) +{ + GetImpl(*this).SetSwingAngleIn(angle, axis); +} + +void ScrollViewCustomEffect::SetSwingAngleOut(float angle, const Vector3& axis) +{ + GetImpl(*this).SetSwingAngleOut(angle, axis); +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunction(AlphaFunction func) +{ + GetImpl(*this).SetSwingAngleAlphaFunction(func); +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + GetImpl(*this).SetSwingAngleAlphaFunction(funcIn, funcOut); +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunctionIn(AlphaFunction func) +{ + GetImpl(*this).SetSwingAngleAlphaFunctionIn(func); +} + +void ScrollViewCustomEffect::SetSwingAngleAlphaFunctionOut(AlphaFunction func) +{ + GetImpl(*this).SetSwingAngleAlphaFunctionOut(func); +} + +void ScrollViewCustomEffect::SetSwingAnchor(const Vector3& anchor) +{ + GetImpl(*this).SetSwingAnchor(anchor); +} + +void ScrollViewCustomEffect::SetSwingAnchor(const Vector3& anchorIn, const Vector3& anchorOut) +{ + GetImpl(*this).SetSwingAnchor(anchorIn, anchorOut); +} + +void ScrollViewCustomEffect::SetSwingAnchorIn(const Vector3& anchor) +{ + GetImpl(*this).SetSwingAnchorIn(anchor); +} + +void ScrollViewCustomEffect::SetSwingAnchorOut(const Vector3& anchor) +{ + GetImpl(*this).SetSwingAnchorOut(anchor); +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunction(AlphaFunction func) +{ + GetImpl(*this).SetSwingAnchorAlphaFunction(func); +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + GetImpl(*this).SetSwingAnchorAlphaFunction(funcIn, funcOut); +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunctionIn(AlphaFunction func) +{ + GetImpl(*this).SetSwingAnchorAlphaFunctionIn(func); +} + +void ScrollViewCustomEffect::SetSwingAnchorAlphaFunctionOut(AlphaFunction func) +{ + GetImpl(*this).SetSwingAnchorAlphaFunctionOut(func); +} + +void ScrollViewCustomEffect::SetOpacityThreshold(float thresh) +{ + GetImpl(*this).SetOpacityThreshold(thresh); +} + +void ScrollViewCustomEffect::SetOpacityThreshold(float threshIn, float threshOut) +{ + GetImpl(*this).SetOpacityThreshold(threshIn, threshOut); +} + +void ScrollViewCustomEffect::SetOpacityThresholdIn(float thresh) +{ + GetImpl(*this).SetOpacityThresholdIn(thresh); +} + +void ScrollViewCustomEffect::SetOpacityThresholdOut(float thresh) +{ + GetImpl(*this).SetOpacityThresholdOut(thresh); +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunction(AlphaFunction func) +{ + GetImpl(*this).SetOpacityAlphaFunction(func); +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunction(AlphaFunction funcIn, AlphaFunction funcOut) +{ + GetImpl(*this).SetOpacityAlphaFunction(funcIn, funcOut); +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunctionIn(AlphaFunction func) +{ + GetImpl(*this).SetOpacityAlphaFunctionIn(func); +} + +void ScrollViewCustomEffect::SetOpacityAlphaFunctionOut(AlphaFunction func) +{ + GetImpl(*this).SetOpacityAlphaFunctionOut(func); +} + +void ScrollViewCustomEffect::ApplyToPage(Actor page, Vector3 pageSize) +{ + GetImpl(*this).ApplyToPage(page, pageSize); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.cpp new file mode 100644 index 0000000..b28d9dc --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.cpp @@ -0,0 +1,61 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewDepthEffect ScrollViewDepthEffect::New() +{ + return ScrollViewDepthEffect(new Internal::ScrollViewDepthEffect()); +} + +ScrollViewDepthEffect::ScrollViewDepthEffect() +{ + +} + +ScrollViewDepthEffect::ScrollViewDepthEffect(Internal::ScrollViewDepthEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewDepthEffect ScrollViewDepthEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewDepthEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewDepthEffect::ApplyToActor(Actor child, + const Vector2& positionExtent, + const Vector2& offsetExtent, + float positionScale, + float scaleExtent) +{ + GetImpl(*this).ApplyToActor( child, positionExtent, offsetExtent, positionScale, scaleExtent ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.h new file mode 100644 index 0000000..da55373 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-depth-effect.h @@ -0,0 +1,119 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_DEPTH_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_DEPTH_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewDepthEffect; +} + +/** + * ScrollView Depth-Effect. + * + * This effect causes Actors to appear to scroll off the page + * at different speeds. + * + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * | + * Child (1..m) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * upon adding children to pages, the ApplyToActor(...) method should be called. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewDepthEffect : public ScrollViewEffect +{ + +public: + + /** + * Create an initialized ScrollViewDepthEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewDepthEffect New(); + + /** + * Create an uninitialized ScrollViewDepthEffect; this can be initialized with ScrollViewDepthEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewDepthEffect is not allowed. + */ + ScrollViewDepthEffect(); + + /** + * Downcast an Object handle to ScrollViewDepthEffect. If handle points to a ScrollViewDepthEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewCubeEffect or an uninitialized handle + */ + static ScrollViewDepthEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to an Actor. + * @param[in] child The child Actor to be affected by this effect. + * @param[in] positionExtent Controls how much Actor's X and Y + * position affects their alpha function's exponent value + * @param[in] offsetExtent exponent offset for X and Y scrolling + * axes. + * @param[in] positionScale Changes the amount the page as a whole + * moves by. + * @param[in] scaleExtent Scale factor to reach when page is one whole + * page away from screen. + */ + void ApplyToActor(Actor child, + const Vector2& positionExtent, + const Vector2& offsetExtent, + float positionScale, + float scaleExtent); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewDepthEffect(Internal::ScrollViewDepthEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_DEPTH_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.cpp new file mode 100644 index 0000000..23fbfda --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-effect.cpp @@ -0,0 +1,40 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewEffect::ScrollViewEffect() +{ + +} + +ScrollViewEffect::ScrollViewEffect(Internal::ScrollViewEffect *impl) +: BaseHandle(impl) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.cpp new file mode 100644 index 0000000..ae5340e --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.cpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL HEADERS +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewPageCarouselEffect ScrollViewPageCarouselEffect::New() +{ + return ScrollViewPageCarouselEffect(new Internal::ScrollViewPageCarouselEffect()); +} + +ScrollViewPageCarouselEffect::ScrollViewPageCarouselEffect() +{ + +} + +ScrollViewPageCarouselEffect::ScrollViewPageCarouselEffect(Internal::ScrollViewPageCarouselEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewPageCarouselEffect ScrollViewPageCarouselEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewPageCarouselEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewPageCarouselEffect::ApplyToPage( Actor page, const Vector2& positionToPageSizeRatio ) +{ + GetImpl(*this).ApplyToPage( page, positionToPageSizeRatio ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.h new file mode 100644 index 0000000..016d918 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.h @@ -0,0 +1,103 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollViewPageCarouselEffect; +} + +/** + * ScrollView Page Carousel Effect. + * + * This effect cause each page in a scroll-view to rotate round a 3D carousel. + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * ApplyToPage(...) method should be called on every page. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewPageCarouselEffect : public ScrollViewEffect +{ + +public: + + /** + * Create an initialized ScrollViewPageCarouselEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewPageCarouselEffect New(); + + /** + * Create an uninitialized ScrollViewPageCarouselEffect; this can be initialized with ScrollViewPageCarouselEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewPageCarouselEffect is not allowed. + */ + ScrollViewPageCarouselEffect(); + + /** + * Downcast an Object handle to ScrollViewPageCarouselEffect. If handle points to a ScrollViewPageCarouselEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewPageCarouselEffect or an uninitialized handle + */ + static ScrollViewPageCarouselEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to a page in the scroll-view. + * @param[in] page The page to be affected by this effect. + * @param[in] positionToPageSizeRatio A ratio of the size of the page which determines the amount the page will move as it fades out. + * Default is moving it by the page size. + */ + void ApplyToPage( Actor page, const Vector2& positionToPageSizeRatio = Vector2::ONE ); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewPageCarouselEffect( Internal::ScrollViewPageCarouselEffect *impl ); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CAROUSEL_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.cpp new file mode 100644 index 0000000..bfa29db --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.cpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL HEADERS +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewPageCubeEffect ScrollViewPageCubeEffect::New() +{ + return ScrollViewPageCubeEffect(new Internal::ScrollViewPageCubeEffect()); +} + +ScrollViewPageCubeEffect::ScrollViewPageCubeEffect() +{ + +} + +ScrollViewPageCubeEffect::ScrollViewPageCubeEffect(Internal::ScrollViewPageCubeEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewPageCubeEffect ScrollViewPageCubeEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewPageCubeEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewPageCubeEffect::ApplyToPage(Actor page, const Vector2& angleSwing) +{ + GetImpl(*this).ApplyToPage( page, angleSwing ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.h new file mode 100644 index 0000000..5a7284c --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-cube-effect.h @@ -0,0 +1,103 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +class Actor; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class ScrollViewPageCubeEffect; +} + +/** + * ScrollView Inner Cube-Effect. + * + * This effect cause each page in a scroll-view to rotate round an inner 3D cube. + * It should be used on the following Actor hierarchy: + * + * ScrollView + * | + * Page (1..n) + * + * You should ensure ScrollView's default constraints have been removed, + * by calling ScrollView::RemoveConstraintsFromChildren() before applying + * this effect to ScrollView. + * + * Manual operation: + * ApplyToPage(...) method should be called on every page. + * + * Automatic operation: + * not implemented. + */ +class ScrollViewPageCubeEffect : public ScrollViewEffect +{ + +public: + + /** + * Create an initialized ScrollViewPageCubeEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewPageCubeEffect New(); + + /** + * Create an uninitialized ScrollViewPageCubeEffect; this can be initialized with ScrollViewPageCubeEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewPageCubeEffect is not allowed. + */ + ScrollViewPageCubeEffect(); + + /** + * Downcast an Object handle to ScrollViewPageCubeEffect. If handle points to a ScrollViewPageCubeEffect the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ScrollViewPageCubeEffect or an uninitialized handle + */ + static ScrollViewPageCubeEffect DownCast( BaseHandle handle ); + + /** + * Manually apply effect to a page in the scroll-view. + * @param[in] page The page to be affected by this effect. + * @param[in] angleSwing The maximum amount the child actor should + * rotate in radians for each axis (X and Y) as the page is scrolled. + */ + void ApplyToPage( Actor page, const Vector2& angleSwing ); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewPageCubeEffect( Internal::ScrollViewPageCubeEffect *impl ); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_PAGE_CUBE_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.cpp new file mode 100644 index 0000000..fe92020 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.cpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL HEADERS +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +ScrollViewPageSpiralEffect ScrollViewPageSpiralEffect::New() +{ + return ScrollViewPageSpiralEffect(new Internal::ScrollViewPageSpiralEffect()); +} + +ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect() +{ + +} + +ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect(Internal::ScrollViewPageSpiralEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewPageSpiralEffect ScrollViewPageSpiralEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewPageSpiralEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewPageSpiralEffect::ApplyToPage( Actor page, const Vector2& spiralAngle ) +{ + GetImpl(*this).ApplyToPage( page, spiralAngle ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.cpp new file mode 100644 index 0000000..431d1d3 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-slide-effect.cpp @@ -0,0 +1,94 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string ScrollViewSlideEffect::EFFECT_TIME( "ScrollViewSlideEffect::EFFECT_TIME" ); +const std::string ScrollViewSlideEffect::EFFECT_REFERENCE( "ScrollViewSlideEffect::EFFECT_REFERENCE" ); +const std::string ScrollViewSlideEffect::EFFECT_ACTIVE( "ScrollViewSlideEffect::EFFECT_ACTIVE" ); + +ScrollViewSlideEffect ScrollViewSlideEffect::New() +{ + return ScrollViewSlideEffect(new Internal::ScrollViewSlideEffect()); +} + +ScrollViewSlideEffect::ScrollViewSlideEffect() +{ + +} + +ScrollViewSlideEffect::ScrollViewSlideEffect(Internal::ScrollViewSlideEffect *impl) +: ScrollViewEffect(impl) +{ +} + +bool ScrollViewSlideEffect::GetSlideDirection() const +{ + return GetImpl(*this).GetSlideDirection(); +} + +void ScrollViewSlideEffect::SetSlideDirection(bool vertical) +{ + GetImpl(*this).SetSlideDirection( vertical ); +} + +Vector3 ScrollViewSlideEffect::GetDelayReferenceOffset() const +{ + return GetImpl(*this).GetDelayReferenceOffset(); +} + +void ScrollViewSlideEffect::SetDelayReferenceOffset(const Vector3& offset) +{ + GetImpl(*this).SetDelayReferenceOffset(offset); +} + +float ScrollViewSlideEffect::GetMaxDelayDuration() const +{ + return GetImpl(*this).GetMaxDelayDuration(); +} + +void ScrollViewSlideEffect::SetMaxDelayDuration(float offset) +{ + GetImpl(*this).SetMaxDelayDuration(offset); +} + +ScrollViewSlideEffect ScrollViewSlideEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewSlideEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +void ScrollViewSlideEffect::ApplyToActor( Actor child, + float delayMin, + float delayMax ) +{ + GetImpl(*this).ApplyToActor( child, + delayMin, + delayMax ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.cpp new file mode 100644 index 0000000..d3236ca --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-twist-effect.cpp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string ScrollViewTwistEffect::EFFECT_TIME( "ScrollViewTwistEffect::EFFECT_TIME" ); +const std::string ScrollViewTwistEffect::EFFECT_REFERENCE( "ScrollViewTwistEffect::EFFECT_REFERENCE" ); +const std::string ScrollViewTwistEffect::EFFECT_DEPTH( "ScrollViewTwistEffect::EFFECT_DEPTH"); +const std::string ScrollViewTwistEffect::EFFECT_ACTIVATE( "ScrollViewTwistEffect::EFFECT_ACTIVATE"); + +const float ScrollViewTwistEffect::DEFAULT_MINIMUM_DISTANCE_FOR_SHRINK( 0.0f ); + +ScrollViewTwistEffect ScrollViewTwistEffect::New() +{ + return ScrollViewTwistEffect(new Internal::ScrollViewTwistEffect()); +} + +ScrollViewTwistEffect::ScrollViewTwistEffect() +{ + +} + +ScrollViewTwistEffect::ScrollViewTwistEffect(Internal::ScrollViewTwistEffect *impl) +: ScrollViewEffect(impl) +{ +} + +ScrollViewTwistEffect ScrollViewTwistEffect::DownCast( BaseHandle handle ) +{ + return ScrollViewTwistEffect( dynamic_cast(handle.GetObjectPtr()) ); +} + +float ScrollViewTwistEffect::GetMinimumDistanceForShrink() const +{ + return GetImpl(*this).GetMinimumDistanceForShrink(); +} + +void ScrollViewTwistEffect::SetMinimumDistanceForShrink(float distance) +{ + GetImpl(*this).SetMinimumDistanceForShrink( distance ); +} + +void ScrollViewTwistEffect::EnableEffect(bool enableFlag) +{ + GetImpl(*this).EnableEffect(enableFlag); +} + +void ScrollViewTwistEffect::ApplyToActor( Actor child, + bool additionalEffects, + const Vector2& angleSwing, + float scaleAmount, + float delayMin, + float delayMax ) +{ + GetImpl(*this).ApplyToActor( child, + additionalEffects, + angleSwing, + scaleAmount, + delayMin, + delayMax ); +} + +void ScrollViewTwistEffect::SetMaxSwingAngle(const Vector2& maxSwingAngle) +{ + GetImpl(*this).SetMaxSwingAngle(maxSwingAngle); +} + +void ScrollViewTwistEffect::SetSwingDropOff(const Vector2& dropOff, const Vector2& distance, AlphaFunction function) +{ + GetImpl(*this).SetSwingDropOff(dropOff, distance, function); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.cpp new file mode 100644 index 0000000..b9c11cf --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.cpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string ScrollViewWobbleEffect::EFFECT_OVERSHOOT( "ScrollViewWobbleEffect::EFFECT_OVERSHOOT" ); +const std::string ScrollViewWobbleEffect::EFFECT_TIME( "ScrollViewWobbleEffect::EFFECT_TIME" ); + +ScrollViewWobbleEffect ScrollViewWobbleEffect::New() +{ + return ScrollViewWobbleEffect(new Internal::ScrollViewWobbleEffect()); +} + +ScrollViewWobbleEffect::ScrollViewWobbleEffect() +{ + +} + +ScrollViewWobbleEffect::ScrollViewWobbleEffect(Internal::ScrollViewWobbleEffect *impl) +: ScrollViewEffect(impl) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.h b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.h new file mode 100644 index 0000000..83b3153 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view-wobble-effect.h @@ -0,0 +1,77 @@ +#ifndef __DALI_TOOLKIT_SCROLL_VIEW_WOBBLE_EFFECT_H__ +#define __DALI_TOOLKIT_SCROLL_VIEW_WOBBLE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +class ScrollViewEffect; + +namespace Internal DALI_INTERNAL +{ +class ScrollViewWobbleEffect; +} + +/** + * ScrollView Wobble-Effect. + * + * Using this effect, a %wobble% property is produced which swings + * towards the origin in accordance to how the user pans the ScrollView. + */ +class ScrollViewWobbleEffect : public ScrollViewEffect +{ +public: + + static const std::string EFFECT_OVERSHOOT; + static const std::string EFFECT_TIME; + +public: + + /** + * Create an initialized ScrollViewWobbleEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ScrollViewWobbleEffect New(); + + /** + * Create an uninitialized ScrollViewWobbleEffect; this can be initialized with ScrollViewWobbleEffect::New() + * Calling member functions with an uninitialized Toolkit::ScrollViewWobbleEffect is not allowed. + */ + ScrollViewWobbleEffect(); + +protected: + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + ScrollViewWobbleEffect(Internal::ScrollViewWobbleEffect *impl); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SCROLL_VIEW_WOBBLE_EFFECT_H__ diff --git a/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.cpp b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.cpp new file mode 100644 index 0000000..874e2b8 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scroll-view/scroll-view.cpp @@ -0,0 +1,638 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// RulerDomain +/////////////////////////////////////////////////////////////////////////////////////////////////// + +RulerDomain::RulerDomain(float min, float max, bool enabled) +: min(min), + max(max), + enabled(enabled) +{ +} + +float RulerDomain::Clamp(float x, float length, float scale) const +{ + ClampState clamped; + + return Clamp(x, length, scale, clamped); +} + +float RulerDomain::Clamp(float x, float length, float scale, ClampState &clamped) const +{ + if(!enabled) + { + clamped = NotClamped; + return x; + } + + const float minExtent = min * scale; + const float maxExtent = max * scale - length; + if(x < minExtent) + { + clamped = ClampedToMin; + return minExtent; + } + else if(x > maxExtent) + { + clamped = ClampedToMax; + return maxExtent; + } + + clamped = NotClamped; + return x; +} + +float RulerDomain::GetSize() const +{ + return max-min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Ruler +/////////////////////////////////////////////////////////////////////////////////////////////////// + +Ruler::Ruler() +: mType(Free), + mEnabled(true), + mDomain(RulerDomain(0.0f,1.0f,false)) +{ +} + +Ruler::~Ruler() +{ +} + +Ruler::RulerType Ruler::GetType() const +{ + return mType; +} + +bool Ruler::IsEnabled() const +{ + return mEnabled; +} + +void Ruler::Enable() +{ + mEnabled = true; +} + +void Ruler::Disable() +{ + mEnabled = false; +} + +void Ruler::SetDomain(RulerDomain domain) +{ + mDomain = domain; +} + +const RulerDomain &Ruler::GetDomain() const +{ + return mDomain; +} + +void Ruler::DisableDomain() +{ + mDomain = RulerDomain(0.0f,1.0f,false); +} + +float Ruler::Clamp(float x, float length, float scale) const +{ + return mDomain.Clamp(x, length, scale); +} + +float Ruler::Clamp(float x, float length, float scale, ClampState &clamped) const +{ + return mDomain.Clamp(x, length, scale, clamped); +} + +float Ruler::SnapAndClamp(float x, float bias, float length, float scale) const +{ + return Clamp(Snap(x, bias), length, scale); +} + +float Ruler::SnapAndClamp(float x, float bias, float length, float scale, ClampState &clamped) const +{ + return Clamp(Snap(x, bias), length, scale, clamped); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// DefaultRuler +/////////////////////////////////////////////////////////////////////////////////////////////////// + +DefaultRuler::DefaultRuler() +{ + mType = Free; +} + +float DefaultRuler::Snap(float x, float bias) const +{ + return x; +} + +float DefaultRuler::GetPositionFromPage(unsigned int page, unsigned int &volume, bool wrap) const +{ + volume = 0; + return 0.0f; +} + +unsigned int DefaultRuler::GetPageFromPosition(float position, bool wrap) const +{ + return 0; +} + +unsigned int DefaultRuler::GetTotalPages() const +{ + return 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// FixedRuler +/////////////////////////////////////////////////////////////////////////////////////////////////// + +FixedRuler::FixedRuler(float spacing) +: mSpacing(spacing) +{ + mType = Fixed; +} + +float FixedRuler::Snap(float x, float bias) const +{ + return floor(x / mSpacing + bias) * mSpacing; +} + +float FixedRuler::GetPositionFromPage(unsigned int page, unsigned int &volume, bool wrap) const +{ + float position = mDomain.min; + + volume = 0; + + // spacing must be present. + if(mEnabled && fabsf(mSpacing) > Math::MACHINE_EPSILON_1) + { + unsigned int column = page; + + // In carry mode, a volume (carry) is produced when page exceeds limit within domain + if(wrap) + { + unsigned int pagesPerVolume = mDomain.GetSize() / mSpacing; + if(pagesPerVolume>0) + { + column += pagesPerVolume; + column %= pagesPerVolume; + volume = page/pagesPerVolume; + } + } + + position = mDomain.min + column * mSpacing; + } + else // Domain (or Spacing) is not present, carry page to volume. + { + if(wrap) + { + volume = page; + } + } + + return position; +} + +unsigned int FixedRuler::GetPageFromPosition(float position, bool wrap) const +{ + unsigned int page = 0; + + // spacing must be present. + if(mEnabled && fabsf(mSpacing) > Math::MACHINE_EPSILON_1) + { + page = floor((position - mDomain.min) / mSpacing + 0.5f); + + if(wrap) + { + unsigned int pagesPerVolume = mDomain.GetSize() / mSpacing; + page %= pagesPerVolume; + } + } + + return page; +} + +unsigned int FixedRuler::GetTotalPages() const +{ + unsigned int pagesPerVolume = 1; + + // spacing must be present. + if(mEnabled && fabsf(mSpacing) > Math::MACHINE_EPSILON_1) + { + pagesPerVolume = mDomain.GetSize() / mSpacing; + } + + return pagesPerVolume; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// ScrollView +/////////////////////////////////////////////////////////////////////////////////////////////////// + +const std::string ScrollView::SCROLL_PAGE_CURRENT( "scroll-page-current" ); +const std::string ScrollView::SCROLL_TIME_PROPERTY_NAME( "scroll-time" ); +const std::string ScrollView::SCROLL_POSITION_PROPERTY_NAME( "scroll-position" ); +const std::string ScrollView::SCROLL_PRE_POSITION_PROPERTY_NAME( "scroll-pre-position" ); +const std::string ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME( "scroll-overshoot-x" ); +const std::string ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME( "scroll-overshoot-y" ); +const std::string ScrollView::SCROLL_FINAL_PROPERTY_NAME( "scroll-final" ); +const std::string ScrollView::SCROLL_X_PROPERTY_NAME( "scroll-x" ); +const std::string ScrollView::SCROLL_Y_PROPERTY_NAME( "scroll-y" ); +const std::string ScrollView::SCROLL_SCALE_PROPERTY_NAME( "scroll-scale" ); +const std::string ScrollView::SCROLL_WRAP_PROPERTY_NAME( "scroll-wrap" ); +const std::string ScrollView::SCROLL_PANNING_PROPERTY_NAME( "scroll-panning" ); +const std::string ScrollView::SCROLL_SCROLLING_PROPERTY_NAME( "scroll-scrolling" ); +const std::string ScrollView::SCROLL_POSITION_DELTA_PROPERTY_NAME( "scroll-position-delta" ); +const std::string ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME( "scroll-start-page-position" ); + +const float ScrollView::DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f); +const float ScrollView::DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f); +const float ScrollView::DEFAULT_SNAP_OVERSHOOT_DURATION(1.0f); +const float ScrollView::DEFAULT_MAX_OVERSHOOT(100.0f); // 100 pixels + +const float ScrollView::DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f); +const float ScrollView::DEFAULT_FRICTION_COEFFICIENT(1.0f); +const float ScrollView::DEFAULT_FLICK_SPEED_COEFFICIENT(1.0f); +const float ScrollView::DEFAULT_MAX_FLICK_SPEED(3.0f); + +const char* const ScrollView::SIGNAL_SNAP_STARTED = "snap-started"; + +ScrollView::ScrollView() +{ +} + +ScrollView::ScrollView(Internal::ScrollView& implementation) +: Scrollable(implementation) +{ +} + +ScrollView::ScrollView( Dali::Internal::CustomActor* internal ) +: Scrollable( internal ) +{ + VerifyCustomActorPointer(internal); +} + +ScrollView::ScrollView( const ScrollView& handle ) +: Scrollable( handle ) +{ +} + +ScrollView& ScrollView::operator=( const ScrollView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +ScrollView ScrollView::New() +{ + return Internal::ScrollView::New(); +} + +ScrollView::~ScrollView() +{ +} + +ScrollView ScrollView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const +{ + return GetImpl(*this).GetScrollSnapAlphaFunction(); +} + +void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha) +{ + GetImpl(*this).SetScrollSnapAlphaFunction(alpha); +} + +AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const +{ + return GetImpl(*this).GetScrollFlickAlphaFunction(); +} + +void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha) +{ + GetImpl(*this).SetScrollFlickAlphaFunction(alpha); +} + +float ScrollView::GetScrollSnapDuration() const +{ + return GetImpl(*this).GetScrollSnapDuration(); +} + +void ScrollView::SetScrollSnapDuration(float time) +{ + GetImpl(*this).SetScrollSnapDuration(time); +} + +float ScrollView::GetScrollFlickDuration() const +{ + return GetImpl(*this).GetScrollFlickDuration(); +} + +void ScrollView::SetScrollFlickDuration(float time) +{ + GetImpl(*this).SetScrollFlickDuration(time); +} + +void ScrollView::SetRulerX(RulerPtr ruler) +{ + GetImpl(*this).SetRulerX(ruler); +} + +void ScrollView::SetRulerY(RulerPtr ruler) +{ + GetImpl(*this).SetRulerY(ruler); +} + +void ScrollView::SetRulerScaleX(RulerPtr ruler) +{ + GetImpl(*this).SetRulerScaleX(ruler); +} + +void ScrollView::SetRulerScaleY(RulerPtr ruler) +{ + GetImpl(*this).SetRulerScaleY(ruler); +} + +void ScrollView::SetScrollSensitive(bool sensitive) +{ + GetImpl(*this).SetScrollSensitive(sensitive); +} + +void ScrollView::SetMaxOvershoot(float overshootX, float overshootY) +{ + GetImpl(*this).SetMaxOvershoot(overshootX, overshootY); +} + +void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha) +{ + GetImpl(*this).SetSnapOvershootAlphaFunction(alpha); +} + +void ScrollView::SetSnapOvershootDuration(float duration) +{ + GetImpl(*this).SetSnapOvershootDuration(duration); +} + +void ScrollView::SetTouchesRequiredForPanning(unsigned int minTouches, unsigned int maxTouches, bool endOutside) +{ + GetImpl(*this).SetTouchesRequiredForPanning(minTouches, maxTouches, endOutside); +} + +void ScrollView::SetActorAutoSnap(bool enable) +{ + GetImpl(*this).SetActorAutoSnap(enable); +} + +void ScrollView::SetWrapMode(bool enable) +{ + GetImpl(*this).SetWrapMode(enable); +} + +int ScrollView::GetRefreshInterval() const +{ + return GetImpl(*this).GetRefreshInterval(); +} + +void ScrollView::SetRefreshInterval(int milliseconds) +{ + GetImpl(*this).SetRefreshInterval(milliseconds); +} + +bool ScrollView::GetAxisAutoLock() const +{ + return GetImpl(*this).GetAxisAutoLock(); +} + +void ScrollView::SetAxisAutoLock(bool enable) +{ + GetImpl(*this).SetAxisAutoLock(enable); +} + +float ScrollView::GetAxisAutoLockGradient() const +{ + return GetImpl(*this).GetAxisAutoLockGradient(); +} + +void ScrollView::SetAxisAutoLockGradient(float gradient) +{ + GetImpl(*this).SetAxisAutoLockGradient(gradient); +} + +float ScrollView::GetFrictionCoefficient() const +{ + return GetImpl(*this).GetFrictionCoefficient(); +} + +void ScrollView::SetFrictionCoefficient(float friction) +{ + GetImpl(*this).SetFrictionCoefficient(friction); +} + +float ScrollView::GetFlickSpeedCoefficient() const +{ + return GetImpl(*this).GetFlickSpeedCoefficient(); +} + +void ScrollView::SetFlickSpeedCoefficient(float speed) +{ + GetImpl(*this).SetFlickSpeedCoefficient(speed); +} + +float ScrollView::GetMaxFlickSpeed() const +{ + return GetImpl(*this).GetMaxFlickSpeed(); +} + +void ScrollView::SetMaxFlickSpeed(float speed) +{ + GetImpl(*this).SetMaxFlickSpeed(speed); +} + +Vector2 ScrollView::GetMouseWheelScrollDistanceStep() const +{ + return GetImpl(*this).GetMouseWheelScrollDistanceStep(); +} + +void ScrollView::SetMouseWheelScrollDistanceStep(Vector2 step) +{ + GetImpl(*this).SetMouseWheelScrollDistanceStep(step); +} + +Vector3 ScrollView::GetCurrentScrollPosition() const +{ + return GetImpl(*this).GetCurrentScrollPosition(); +} + +Vector3 ScrollView::GetCurrentScrollScale() const +{ + return GetImpl(*this).GetCurrentScrollScale(); +} + +unsigned int ScrollView::GetCurrentPage() const +{ + return GetImpl(*this).GetCurrentPage(); +} + +void ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation) +{ + GetImpl(*this).TransformTo(position, scale, rotation); +} + +void ScrollView::TransformTo(const Vector3& position, const Vector3& scale, float rotation, float duration) +{ + GetImpl(*this).TransformTo(position, scale, rotation, duration); +} + +void ScrollView::ScrollTo(const Vector3 &position) +{ + GetImpl(*this).ScrollTo(position); +} + +void ScrollView::ScrollTo(const Vector3 &position, float duration) +{ + GetImpl(*this).ScrollTo(position, duration); +} + +void ScrollView::ScrollTo(const Vector3 &position, float duration, + DirectionBias horizontalBias, DirectionBias verticalBias) +{ + GetImpl(*this).ScrollTo(position, duration, horizontalBias, verticalBias); +} + +void ScrollView::ScrollTo(unsigned int page) +{ + GetImpl(*this).ScrollTo(page); +} + +void ScrollView::ScrollTo(unsigned int page, float duration) +{ + GetImpl(*this).ScrollTo(page, duration); +} + +void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias) +{ + GetImpl(*this).ScrollTo(page, duration, bias); +} + +void ScrollView::ScrollTo(Actor &actor) +{ + GetImpl(*this).ScrollTo(actor); +} + +void ScrollView::ScrollTo(Actor &actor, float duration) +{ + GetImpl(*this).ScrollTo(actor, duration); +} + +bool ScrollView::ScrollToSnapPoint() +{ + return GetImpl(*this).ScrollToSnapPoint(); +} + +void ScrollView::ScaleTo(const Vector3 &scale) +{ + GetImpl(*this).ScaleTo(scale); +} + +void ScrollView::ScaleTo(const Vector3 &scale, float duration) +{ + GetImpl(*this).ScaleTo(scale, duration); +} + +void ScrollView::ApplyConstraintToChildren(Constraint constraint) +{ + GetImpl(*this).ApplyConstraintToChildren(constraint); +} + +void ScrollView::RemoveConstraintsFromChildren() +{ + GetImpl(*this).RemoveConstraintsFromChildren(); +} + +void ScrollView::ApplyEffect(ScrollViewEffect effect) +{ + GetImpl(*this).ApplyEffect(effect); +} + +ScrollViewEffect ScrollView::ApplyEffect(ScrollView::PageEffect effect) +{ + return GetImpl(*this).ApplyEffect(effect); +} + +void ScrollView::RemoveEffect(ScrollViewEffect effect) +{ + GetImpl(*this).RemoveEffect(effect); +} + +void ScrollView::RemoveAllEffects() +{ + GetImpl(*this).RemoveAllEffects(); +} + +void ScrollView::BindActor(Actor child) +{ + GetImpl(*this).BindActor(child); +} + +void ScrollView::UnbindActor(Actor child) +{ + GetImpl(*this).UnbindActor(child); +} + +ScrollView::SnapStartedSignalV2& ScrollView::SnapStartedSignal() +{ + return GetImpl(*this).SnapStartedSignal(); +} + +void ScrollView::SetScrollingDirection( Radian direction, Radian threshold ) +{ + GetImpl(*this).SetScrollingDirection( direction, threshold ); +} + +void ScrollView::RemoveScrollingDirection( Radian direction ) +{ + GetImpl(*this).RemoveScrollingDirection( direction ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/scrollable/scrollable.cpp b/dali-toolkit/public-api/controls/scrollable/scrollable.cpp new file mode 100644 index 0000000..9e708d0 --- /dev/null +++ b/dali-toolkit/public-api/controls/scrollable/scrollable.cpp @@ -0,0 +1,113 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +const std::string Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME( "scroll-relative-position" ); +const std::string Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME( "scroll-position-min" ); +const std::string Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME( "scroll-position-max" ); +const std::string Scrollable::SCROLL_DIRECTION_PROPERTY_NAME( "scroll-direction" ); + +const char* const Scrollable::SIGNAL_SCROLL_STARTED = "scroll-started"; +const char* const Scrollable::SIGNAL_SCROLL_COMPLETED = "scroll-completed"; +const char* const Scrollable::SIGNAL_SCROLL_UPDATED = "scroll-updated"; +const char* const Scrollable::SIGNAL_SCROLL_CLAMPED = "scroll-clamped"; + +Scrollable::Scrollable() +{ +} + +Scrollable::Scrollable(Internal::Scrollable& implementation) +: Control(implementation) +{ +} + +Scrollable::Scrollable( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +Scrollable::Scrollable( const Scrollable& handle ) +: Control( handle ) +{ +} + +Scrollable& Scrollable::operator=( const Scrollable& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +Scrollable::~Scrollable() +{ +} + +Scrollable Scrollable::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +Scrollable::ScrollStartedSignalV2& Scrollable::ScrollStartedSignal() +{ + return GetImpl(*this).ScrollStartedSignal(); +} + +Scrollable::ScrollUpdatedSignalV2& Scrollable::ScrollUpdatedSignal() +{ + return GetImpl(*this).ScrollUpdatedSignal(); +} + +Scrollable::ScrollCompletedSignalV2& Scrollable::ScrollCompletedSignal() +{ + return GetImpl(*this).ScrollCompletedSignal(); +} + +Scrollable::ScrollClampedSignalV2& Scrollable::ScrollClampedSignal() +{ + return GetImpl(*this).ScrollClampedSignal(); +} + +bool Scrollable::IsScrollComponentEnabled(Scrollable::ScrollComponentType indicator) const +{ + return GetImpl(*this).IsScrollComponentEnabled(indicator); +} + +void Scrollable::EnableScrollComponent(Scrollable::ScrollComponentType indicator) +{ + GetImpl(*this).EnableScrollComponent(indicator); +} + +void Scrollable::DisableScrollComponent(Scrollable::ScrollComponentType indicator) +{ + GetImpl(*this).DisableScrollComponent(indicator); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/selectors/rotating-selector.cpp b/dali-toolkit/public-api/controls/selectors/rotating-selector.cpp new file mode 100644 index 0000000..4e94693 --- /dev/null +++ b/dali-toolkit/public-api/controls/selectors/rotating-selector.cpp @@ -0,0 +1,120 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const RotatingSelector::SIGNAL_CHECKED = "checked"; + +RotatingSelector::RotatingSelector() +{ +} + +RotatingSelector::RotatingSelector( const RotatingSelector& rotatingSelector ) +: Control( rotatingSelector ) +{ +} + +RotatingSelector& RotatingSelector::operator=( const RotatingSelector& rotatingSelector ) +{ + if( &rotatingSelector != this ) + { + Control::operator=( rotatingSelector ); + } + + return *this; +} + + +RotatingSelector::~RotatingSelector() +{ +} + +RotatingSelector RotatingSelector::New(Actor& unSelectedActor, Actor& selectedActor) +{ + return Internal::RotatingSelector::New(unSelectedActor, selectedActor); +} + +RotatingSelector RotatingSelector::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void RotatingSelector::SetSelected( bool checked ) +{ + Dali::Toolkit::GetImpl( *this ).SetSelected( checked ); +} + +bool RotatingSelector::IsSelected() const +{ + return Dali::Toolkit::GetImpl( *this ).IsSelected(); +} + +void RotatingSelector::SetSelectedActor( Actor& selectedActor ) +{ + Dali::Toolkit::GetImpl( *this ).SetSelectedActor( selectedActor ); +} + +Actor RotatingSelector::GetSelectedActor() +{ + return Dali::Toolkit::GetImpl( *this ).GetSelectedActor(); +} + +void RotatingSelector::SetUnSelectedActor( Actor& unSelectedActor ) +{ + Dali::Toolkit::GetImpl( *this ).SetUnSelectedActor( unSelectedActor ); +} + +Actor RotatingSelector::GetUnSelectedActor() +{ + return Dali::Toolkit::GetImpl( *this ).GetUnSelectedActor(); +} + +void RotatingSelector::SetSelectable( bool selectable ) +{ + Dali::Toolkit::GetImpl( *this ).SetSelectable( selectable ); +} + +bool RotatingSelector::IsSelectable()const +{ + return Dali::Toolkit::GetImpl( *this ).IsSelectable(); +} + +RotatingSelector::SelectedSignalV2& RotatingSelector::SelectedSignal() +{ + return Dali::Toolkit::GetImpl( *this ).SelectedSignal(); +} + +RotatingSelector::RotatingSelector( Internal::RotatingSelector& implementation ) +: Control(implementation) +{ +} + +RotatingSelector::RotatingSelector( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/selectors/rotating-selector.h b/dali-toolkit/public-api/controls/selectors/rotating-selector.h new file mode 100644 index 0000000..b429434 --- /dev/null +++ b/dali-toolkit/public-api/controls/selectors/rotating-selector.h @@ -0,0 +1,160 @@ +#ifndef __DALI_TOOLKIT_CUSTOM_CHECK_ACTOR_H__ +#define __DALI_TOOLKIT_CUSTOM_CHECK_ACTOR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class RotatingSelector; +} + +/** + * RotatingSelector is a simple control to switch between two states (selected/unselected). A signal is emitted when the selector switches between + * the two states. The control has two faces one behind the other, The control is rotated while switching between the two states + */ +class RotatingSelector : public Control +{ +public: + //Signal Names + static const char* const SIGNAL_CHECKED; + +public: + + /** + * Create an uninitialized RotatingSelector; this can be initialized with RotatingSelector::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + RotatingSelector(); + + /** + * Copy constructor. + */ + RotatingSelector( const RotatingSelector& rotatingSelector ); + + /** + * Assignment operator. + */ + RotatingSelector& operator=( const RotatingSelector& rotatingSelector ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + ~RotatingSelector(); + + /** + * Create an initialized RotatingSelector. + * @return A handle to a newly allocated Dali resource. + */ + static RotatingSelector New(Actor& unSelectedActor, Actor& selectedActor); + + /** + * Downcast an Object handle to RotatingSelector. If handle points to a RotatingSelector the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a RotatingSelector or an uninitialized handle + */ + static RotatingSelector DownCast( BaseHandle handle ); + + /** + * Toggles the selection status of the selector. + * @param[in] toggle true for selected and false for un selected. + */ + void SetSelected( bool toggle ); + + /** + * Queries the selection status of the selector. + * @return true if the selector is selected otherwise false + */ + bool IsSelected() const; + + /** + * Sets the actor to be displayed by the selector when it is in selected state + * @param[in] selectedActor The actor to display + */ + void SetSelectedActor( Actor& selectedActor ); + + /** + * Gets the actor to be displayed by the selector when it is in selected state + * @return A handle to the selected actor. If the selected actor has not been set, this handle will be invalid. + */ + Actor GetSelectedActor(); + + /** + * Sets the actor to be displayed by the selector when it is in unselected state + * @param[in] unSelectedActor The actor to display + */ + void SetUnSelectedActor( Actor& unSelectedActor ); + + /** + * Gets the actor to be displayed by the selector when it is in unselected state + * @return A handle to Actor. If the unselected actor has not been set, this handle will be invalid. + */ + Actor GetUnSelectedActor(); + + /** + * Sets whether the Selector is selectable + * @param[in] selectable true to be able to toggle the selector false otherwise + */ + void SetSelectable( bool selectable ); + + /** + * Queries whether the Selector is selectable + * @return true if the selector is selectable, false otherwise + */ + bool IsSelectable()const; + +public: //Signals + + // RotatingSelector Toggled + typedef SignalV2< void( RotatingSelector, bool ) > SelectedSignalV2; + + /** + * Signal emitted when the rotating selector is in switched to a selected state. + */ + SelectedSignalV2& SelectedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + RotatingSelector( Internal::RotatingSelector& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + RotatingSelector( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_CUSTOM_CHECK_ACTOR_H__ diff --git a/dali-toolkit/public-api/controls/shadow-view/shadow-view.cpp b/dali-toolkit/public-api/controls/shadow-view/shadow-view.cpp new file mode 100644 index 0000000..c09a556 --- /dev/null +++ b/dali-toolkit/public-api/controls/shadow-view/shadow-view.cpp @@ -0,0 +1,138 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace +{ + +const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE = 1.0f; +const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE = 1.0f; + +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +ShadowView::ShadowView() +{ +} + +ShadowView::~ShadowView() +{ +} + +ShadowView::ShadowView(const ShadowView& handle) + : Control( handle ) +{ +} + +ShadowView& ShadowView::operator=(const ShadowView& rhs) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +ShadowView ShadowView::New() +{ + return Internal::ShadowView::New(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE, + GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE); +} + +ShadowView ShadowView::New(float downsampleWidthScale, float downsampleHeightScale) +{ + return Internal::ShadowView::New(downsampleWidthScale, downsampleHeightScale); +} + +ShadowView::ShadowView( Internal::ShadowView& implementation ) +: Control( implementation ) +{ +} + +ShadowView::ShadowView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +ShadowView ShadowView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void ShadowView::Add(Actor child) +{ + GetImpl(*this).Add(child); +} + +void ShadowView::Remove(Actor child) +{ + GetImpl(*this).Remove(child); +} + +void ShadowView::SetShadowPlane(ImageActor shadowPlane) +{ + GetImpl(*this).SetShadowPlane(shadowPlane); +} + +void ShadowView::SetPointLight(Actor pointLight) +{ + GetImpl(*this).SetPointLight(pointLight); +} + +void ShadowView::SetPointLightFieldOfView(float fieldOfView) +{ + GetImpl(*this).SetPointLightFieldOfView(fieldOfView); +} + +void ShadowView::SetShadowColor(Vector4 color) +{ + GetImpl(*this).SetShadowColor(color); +} + +void ShadowView::Activate() +{ + GetImpl(*this).Activate(); +} + +void ShadowView::Deactivate() +{ + GetImpl(*this).Deactivate(); +} + +Property::Index ShadowView::GetBlurStrengthPropertyIndex() const +{ + return GetImpl(*this).GetBlurStrengthPropertyIndex(); +} + +Property::Index ShadowView::GetShadowColorPropertyIndex() const +{ + return GetImpl(*this).GetShadowColorPropertyIndex(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/shadow-view/shadow-view.h b/dali-toolkit/public-api/controls/shadow-view/shadow-view.h new file mode 100644 index 0000000..1d8a898 --- /dev/null +++ b/dali-toolkit/public-api/controls/shadow-view/shadow-view.h @@ -0,0 +1,258 @@ +#ifndef __DALI_TOOLKIT_SHADOW_VIEW_H__ +#define __DALI_TOOLKIT_SHADOW_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * ShadowView implementation class + */ +class ShadowView; + +} // namespace Internal + +/** + * + * ShadowView is a class for applying shadows to objects present in the view. + * + * Basic idea:- + * + * 1) The ShadowView object will render all its child actors offscreen from the light's point of view projected on to the shadow plane in a seperate render task.\n + * 2) The ShadowView object then blurs the result of step 1), using a two pass separated Gaussian blur.\n + * 3) The ShadowView object gets rendered automatically in the default render task along with it's children. + * + * Fundamentally, the ShadowView is simply an Actor in the normal actor tree that affects all of its children. It should be added to your Actor tree and manipulated in the + * normal way. It can be considered a 'portal' in the sense that all child actors are clipped to the ShadowView actor bounds. + * + * LIMITATIONS: + * The ShadowView is intended to provide simple planar projection shadows, Which means it needs a flat plane to cast shadows. So Shadows can't be cast on other objects. + * + * ************\n + * NB: It is essential to remove the ShadowView from the stage and also to call Deactivate() on it when you are not using it. This will ensure that resources are freed and + * rendering stops.\n + * ************\n + * + * Usage example:- + * + * @code + * // initialise\n + * ShadowView shadowView = ShadowView::New(); + * + * // create and add some visible actors to the ShadowView, all these child actors will therefore cast a shadow. + * Image image = Image::New(...); + * ImageActor imageActor = ImageActor::New(image); + * imageActor.SetParentOrigin( ParentOrigin::CENTER ); + * imageActor.SetAnchorPoint( AnchorPoint::CENTER ); + * shadowView.Add(imageActor);\n Add the renderable actor to the shadow view + * + * ImageActor shadowPlane = ImageActor::New(); //This will be the shadow plane + * shadowPlane.SetParentOrigin( ParentOrigin::CENTER ); + * shadowPlane.SetAnchorPoint( AnchorPoint::CENTER ); + * shadowPlane.SetSize(700.0f, 700.0f); + * shadowPlane.SetPosition( Vector3(0.0f, 0.0f, -30.0f) ); //Just behind the image actor. + * shadowPlane.SetShadowPlane(ShadowPlane); + * + * Actor pointLight = Actor::New(); // This will be the light source + * pointLight.SetPosition(300.0f, 250.0f, 600.0f); + * Stage::GetCurrent().Add(pointLight); + * shadowView.SetPointLight(pointLight); + * + * // Start rendering the ShadowView + * Stage::GetCurrent().Add(ShadowPlane); + * shadowView.Activate(); + * ... + * + * // animate the strength of the blur - this can fade between no blur and full blur. See GetBlurStrengthPropertyIndex(). + * Animation blurAnimation = Animation::New( ... ); + * blurAnimation.AnimateTo( Property( shadowView, shadowView.GetBlurStrengthPropertyIndex() ), ... ); + * blurAnimation.Play(); + * + * ... + * // Stop rendering the ShadowView + * Stage::GetCurrent().Remove(shadowView); + * shadowView.Deactivate(); + * @endcode + */ +class ShadowView : public Control +{ +public: + + /** + * Create an uninitialized ShadowView; this can be initialized with ShadowView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + ShadowView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + ShadowView(const ShadowView& handle); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + ShadowView& operator=(const ShadowView& view); + + /** + * Virtual destructor. + */ + virtual ~ShadowView(); + + /** + * Downcast an Object handle to ShadowView. If handle points to a ShadowView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ShadowView or an uninitialized handle + */ + static ShadowView DownCast( BaseHandle handle ); + + /** + * Create an initialized ShadowView. Add children and call SetShadowPlane to make shadows visible\n + * @return A handle to a newly allocated Dali resource + */ + static ShadowView New(); + + /** + * Create an initialized ShadowView. Add children and call SetShadowPlane to make shadows visible\n + * @param[in] downsampleWidthScale The width scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @param[in] downsampleHeightScale The height scale factor applied during the blur process, scaling the size of the source image to the size of the final blurred image output. + * Useful for downsampling - trades visual quality for processing speed. A value of 1.0f results in no scaling applied. + * @return A handle to a newly allocated Dali resource + */ + static ShadowView New(float downsampleWidthScale, float downsampleHeightScale); + + /** + * Adds a child Actor to this Actor. + * NOTE! if the child already has a parent, it will be removed from old parent + * and reparented to this actor. This may change childs position, color, shader effect, + * scale etc as it now inherits them from this actor + * @pre This Actor (the parent) has been initialized. + * @pre The child actor has been initialized. + * @pre The child actor is not the same as the parent actor. + * @pre The actor is not the Root actor + * @param [in] child The child. + * @post The child will be referenced by its parent. This means that the child will be kept alive, + * even if the handle passed into this method is reset or destroyed. + * @post This may invalidate ActorContainer iterators. + */ + void Add(Actor child); + + /** + * Removes a child Actor from this Actor. + * If the actor was not a child of this actor, this is a no-op. + * @pre This Actor (the parent) has been initialized. + * @pre The child actor is not the same as the parent actor. + * @param [in] child The child. + * @post This may invalidate ActorContainer iterators. + */ + void Remove(Actor child); + + /** + * Set the Shadow Plane for the shadow effect. + * + * @param[in] shadowPlane An actor representing the shadow + * plane. The position of the actor represents the origin of the + * plane, and the orientation of the actor represents the direction + * of the plane normal. Make the plane sufficiently large if the shadows are + * clipped. + */ + void SetShadowPlane(ImageActor shadowPlane); + + /** + * Set the Point Light for the shadow effect. This is usually NOT a renderable actor. + * The orientation of the actor is not considered for the shadow calculation. + * @param[in] pointLight An actor representing the location of the + * directionless light source that casts the shadow. + */ + void SetPointLight(Actor pointLight); + + /** + * Set the field of view of the point light source. This will be used by an additional + * internal camera to look at the scene form the light source. If you notice any aritifacts + * when the light position is near to the object, Increase the field of view. + * @param[in] fieldOfView New field of view in radians, Typical values are Math::PI / 4.0f, + * Math::PI / 2.0f + */ + void SetPointLightFieldOfView(float fieldOfView); + + /** + * Set shadow color. + * @param[in] color The shadow color + */ + void SetShadowColor(Vector4 color); + + /** + * Start rendering the ShadowView. Must be called after you Add() it to the stage. + * @pre This Actor has been added to the stage. + */ + void Activate(); + + /** + * Stop rendering the ShadowView. Must be called after you Remove() it from the stage. + * @pre This Actor has been removed from the stage. + */ + void Deactivate(); + + /** + * Get the property index that controls the strength of the blur applied to the shadow. Useful for animating this property. + * This property represents a value in the range [0.0 - 1.0] where 0.0 is no blur and 1.0 is full blur. Default 0.2. + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetBlurStrengthPropertyIndex() const; + + /** + * Get the property index that controls the color of the shadow. Useful for animating this property. + * This property represents a value in the Vector4 format. Default color value is Vector4(0.2f, 0.2f, 0.2f, 0.8f) (i.e grey color). + * @return The property index that can be used with e.g. AnimateTo( ... ) + */ + Property::Index GetShadowColorPropertyIndex() const; + + +public: + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The UI Control implementation. + */ + ShadowView( Internal::ShadowView& implementation ); + + /** + * Allows the creation of this UI Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ShadowView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADOW_VIEW_H__ diff --git a/dali-toolkit/public-api/controls/slider/slider.cpp b/dali-toolkit/public-api/controls/slider/slider.cpp new file mode 100644 index 0000000..c81a2c1 --- /dev/null +++ b/dali-toolkit/public-api/controls/slider/slider.cpp @@ -0,0 +1,125 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Slider +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Signals +const char* const Slider::SIGNAL_VALUE_CHANGED = "value-changed"; +const char* const Slider::SIGNAL_MARK = "mark"; + +// Properties +const std::string Slider::LOWER_BOUND_PROPERTY_NAME( "lower-bound" ); +const std::string Slider::UPPER_BOUND_PROPERTY_NAME( "upper-bound" ); +const std::string Slider::VALUE_PROPERTY_NAME( "value" ); +const std::string Slider::HIT_REGION_PROPERTY_NAME( "hit-region" ); +const std::string Slider::BACKING_REGION_PROPERTY_NAME( "backing-region" ); +const std::string Slider::HANDLE_REGION_PROPERTY_NAME( "handle-region" ); + +const std::string Slider::BACKING_IMAGE_NAME_PROPERTY_NAME( "backing-image-name" ); +const std::string Slider::HANDLE_IMAGE_NAME_PROPERTY_NAME( "handle-image-name" ); +const std::string Slider::PROGRESS_IMAGE_NAME_PROPERTY_NAME( "progress-image-name" ); +const std::string Slider::POPUP_IMAGE_NAME_PROPERTY_NAME( "popup-image-name" ); +const std::string Slider::POPUP_ARROW_IMAGE_NAME_PROPERTY_NAME( "popup-arrow-image-name" ); + +const std::string Slider::BACKING_SCALE9_BORDER_PROPERTY_NAME( "backing-scale9-border" ); +const std::string Slider::PROGRESS_SCALE9_BORDER_PROPERTY_NAME( "progress-scale9-border" ); +const std::string Slider::POPUP_SCALE9_BORDER_PROPERTY_NAME( "popup-scale9-border" ); + +const std::string Slider::DISABLE_COLOR_PROPERTY_NAME( "disable-color" ); +const std::string Slider::POPUP_TEXT_COLOR_PROPERTY_NAME( "popup-text-color" ); + +const std::string Slider::VALUE_PRECISION_PROPERTY_NAME( "value-precision" ); + +const std::string Slider::SHOW_POPUP_PROPERTY_NAME( "show-popup" ); +const std::string Slider::SHOW_VALUE_PROPERTY_NAME( "show-value" ); + +const std::string Slider::ENABLED_PROPERTY_NAME( "enabled" ); + +const std::string Slider::MARKS_PROPERTY_NAME( "marks" ); +const std::string Slider::SNAP_TO_MARKS_PROPERTY_NAME( "snap-to-marks" ); +const std::string Slider::MARK_TOLERANCE_PROPERTY_NAME( "mark-tolerance" ); + +Slider::Slider() +{ +} + +Slider::Slider( const Slider& handle ) +: Control( handle ) +{ +} + +Slider& Slider::operator=( const Slider& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +Slider::Slider(Internal::Slider& implementation) +: Control(implementation) +{ +} + +Slider::Slider( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +Slider Slider::New() +{ + return Internal::Slider::New(); +} + +Slider::~Slider() +{ +} + +Slider::ValueChangedSignalType& Slider::ValueChangedSignal() +{ + return GetImpl( *this ).ValueChangedSignal(); +} + +Slider::MarkSignalType& Slider::MarkSignal() +{ + return GetImpl( *this ).MarkSignal(); +} + +Slider Slider::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + + + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/slider/slider.h b/dali-toolkit/public-api/controls/slider/slider.h new file mode 100644 index 0000000..a79fcb4 --- /dev/null +++ b/dali-toolkit/public-api/controls/slider/slider.h @@ -0,0 +1,152 @@ +#ifndef __DALI_TOOLKIT_SLIDER_H__ +#define __DALI_TOOLKIT_SLIDER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class Slider; +} + +/** + * Slider is a control to enable sliding an indicator between two values + */ +class Slider : public Control +{ +public: + + //Signal Names + static const char* const SIGNAL_VALUE_CHANGED; + static const char* const SIGNAL_MARK; + + // Properties + static const std::string LOWER_BOUND_PROPERTY_NAME; ///< Property, name "lower-bound", type FLOAT + static const std::string UPPER_BOUND_PROPERTY_NAME; ///< Property, name "upper-bound", type FLOAT + static const std::string VALUE_PROPERTY_NAME; ///< Property, name "value", type FLOAT + + static const std::string HIT_REGION_PROPERTY_NAME; ///< Property, name "hit-region", type VECTOR2 + static const std::string BACKING_REGION_PROPERTY_NAME; ///< Property, name "backing-region", type VECTOR2 + static const std::string HANDLE_REGION_PROPERTY_NAME; ///< Property, name "handle-region", type VECTOR2 + + static const std::string BACKING_IMAGE_NAME_PROPERTY_NAME; ///< Property, name "backing-image-name", type std::string + static const std::string HANDLE_IMAGE_NAME_PROPERTY_NAME; ///< Property, name "handle-image-name", type std::string + static const std::string PROGRESS_IMAGE_NAME_PROPERTY_NAME; ///< Property, name "progress-image-name", type std::string + static const std::string POPUP_IMAGE_NAME_PROPERTY_NAME; ///< Property, name "popup-image-name", type std::string + static const std::string POPUP_ARROW_IMAGE_NAME_PROPERTY_NAME; ///< Property, name "popup-arrow-image-name", type std::string + + static const std::string BACKING_SCALE9_BORDER_PROPERTY_NAME; ///< Property, name "backing-scale9-border", type VECTOR4 + static const std::string PROGRESS_SCALE9_BORDER_PROPERTY_NAME; ///< Property, name "progress-scale9-border", type VECTOR4 + static const std::string POPUP_SCALE9_BORDER_PROPERTY_NAME; ///< Property, name "popup-scale9-border", type VECTOR4 + + static const std::string DISABLE_COLOR_PROPERTY_NAME; ///< Property, name "disable-color", type VECTOR4 + static const std::string POPUP_TEXT_COLOR_PROPERTY_NAME; ///< Property, name "popup-text-color", type VECTOR4 + + static const std::string VALUE_PRECISION_PROPERTY_NAME; ///< Property, name "value-precision", type INT + + static const std::string SHOW_POPUP_PROPERTY_NAME; ///< Property, name "show-popup", type BOOLEAN + static const std::string SHOW_VALUE_PROPERTY_NAME; ///< Property, name "show-value", type BOOLEAN + + static const std::string ENABLED_PROPERTY_NAME; ///< Property, name "enabled", type BOOLEAN + + static const std::string MARKS_PROPERTY_NAME; ///< Property, name "marks", type Property::Array + static const std::string SNAP_TO_MARKS_PROPERTY_NAME; ///< Property, name "snap-to-marks", type BOOLEAN + static const std::string MARK_TOLERANCE_PROPERTY_NAME; ///< Property, name "mark-tolerance", type FLOAT + +public: + + /** + * Create the Slider control + * @return A handle to the Slider control. + */ + static Slider New(); + + /** + * Creates an empty Slider handle + */ + Slider(); + + /** + * Copy constructor. Creates another handle that points to the same real object + */ + Slider( const Slider& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + Slider& operator=( const Slider& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~Slider(); + + /** + * Downcast an Object handle to Slider. If handle points to a Slider the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Slider or an uninitialized handle + */ + static Slider DownCast( BaseHandle handle ); + +public: + + // Signals + + // Value changed + typedef SignalV2< bool ( Slider, float ) > ValueChangedSignalType; + typedef SignalV2< bool ( Slider, int ) > MarkSignalType; + + /** + * Signal emitted when the slider value changes + */ + ValueChangedSignalType& ValueChangedSignal(); + + /** + * Signal emitted when the slider handle reaches a mark + */ + MarkSignalType& MarkSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + Slider(Internal::Slider& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + Slider( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SLIDER_H__ diff --git a/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.cpp b/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.cpp new file mode 100644 index 0000000..0bb9657 --- /dev/null +++ b/dali-toolkit/public-api/controls/super-blur-view/super-blur-view.cpp @@ -0,0 +1,103 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +SuperBlurView::SuperBlurView() +{ +} + +SuperBlurView SuperBlurView::New( unsigned int blurLevels ) +{ + return Internal::SuperBlurView::New( blurLevels ); +} + +SuperBlurView::SuperBlurView( const SuperBlurView& handle ) +: Control( handle ) +{ +} + +SuperBlurView& SuperBlurView::operator=( const SuperBlurView& rhs ) +{ + if( &rhs != this ) + { + Control::operator=(rhs); + } + return *this; +} + +SuperBlurView::~SuperBlurView() +{ +} + +SuperBlurView SuperBlurView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +SuperBlurView::SuperBlurView(Internal::SuperBlurView& implementation) +: Control( implementation ) +{ +} + +SuperBlurView::SuperBlurView(Dali::Internal::CustomActor* internal) +: Control( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +void SuperBlurView::SetImage(Image inputImage) +{ + GetImpl(*this).SetImage( inputImage ); +} + +Property::Index SuperBlurView::GetBlurStrengthPropertyIndex() const +{ + return GetImpl(*this).GetBlurStrengthPropertyIndex(); +} + +void SuperBlurView::SetBlurStrength( float blurStrength ) +{ + GetImpl(*this).SetBlurStrength( blurStrength ); +} + +float SuperBlurView::GetCurrentBlurStrength() const +{ + return GetImpl(*this).GetCurrentBlurStrength(); +} + +SuperBlurView::SuperBlurViewSignal& SuperBlurView::BlurFinishedSignal() +{ + return GetImpl(*this).BlurFinishedSignal(); +} + +Image SuperBlurView::GetBlurredImage( unsigned int level ) +{ + return GetImpl(*this).GetBlurredImage( level ); +} +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/table-view/table-view.cpp b/dali-toolkit/public-api/controls/table-view/table-view.cpp new file mode 100644 index 0000000..4d24415 --- /dev/null +++ b/dali-toolkit/public-api/controls/table-view/table-view.cpp @@ -0,0 +1,206 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +using std::vector; + +namespace Dali +{ + +namespace Toolkit +{ + +TableView::TableView() +{ +} + +TableView::TableView( const TableView& handle ) +: Control( handle ) +{ +} + +TableView& TableView::operator=( const TableView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +TableView TableView::New( unsigned int initialRows, unsigned int initialColumns ) +{ + return Internal::TableView::New( initialRows, initialColumns ); +} + +TableView::~TableView() +{ +} + +TableView TableView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +bool TableView::AddChild( Actor child, CellPosition position ) +{ + return GetImpl(*this).AddChild( child, position ); +} + +Actor TableView::GetChildAt( CellPosition position ) +{ + return GetImpl(*this).GetChildAt( position ); +} + +Actor TableView::RemoveChildAt( CellPosition position ) +{ + return GetImpl(*this).RemoveChildAt( position ); +} + +bool TableView::FindChildPosition( Actor child, CellPosition& position ) +{ + return GetImpl(*this).FindChildPosition( child, position ); +} + +void TableView::InsertRow( unsigned int rowIndex ) +{ + GetImpl(*this).InsertRow( rowIndex ); +} + +void TableView::DeleteRow( unsigned int rowIndex ) +{ + GetImpl(*this).DeleteRow( rowIndex ); +} + +void TableView::DeleteRow( unsigned int rowIndex, vector& removed ) +{ + GetImpl(*this).DeleteRow( rowIndex, removed ); +} + +void TableView::InsertColumn( unsigned int columnIndex ) +{ + GetImpl(*this).InsertColumn( columnIndex ); +} + +void TableView::DeleteColumn( unsigned int columnIndex ) +{ + GetImpl(*this).DeleteColumn( columnIndex ); +} + +void TableView::DeleteColumn( unsigned int columnIndex, vector& removed ) +{ + GetImpl(*this).DeleteColumn( columnIndex, removed ); +} + +void TableView::Resize( unsigned int rows, unsigned int columns ) +{ + GetImpl(*this).Resize( rows, columns ); +} + +void TableView::Resize( unsigned int rows, unsigned int columns, vector& removed ) +{ + GetImpl(*this).Resize( rows, columns, removed ); +} + +void TableView::SetCellPadding( Size padding ) +{ + GetImpl(*this).SetCellPadding( padding ); +} + +Size TableView::GetCellPadding() +{ + return GetImpl(*this).GetCellPadding(); +} + +void TableView::SetFixedHeight( unsigned int rowIndex, float height ) +{ + GetImpl(*this).SetFixedHeight( rowIndex, height ); +} + +float TableView::GetFixedHeight( unsigned int rowIndex ) const +{ + return GetImpl(*this).GetFixedHeight( rowIndex ); +} + +void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage ) +{ + GetImpl(*this).SetRelativeHeight( rowIndex, heightPercentage ); +} + +float TableView::GetRelativeHeight( unsigned int rowIndex ) const +{ + return GetImpl(*this).GetRelativeHeight( rowIndex ); +} + +void TableView::SetFixedWidth( unsigned int columnIndex, float width ) +{ + GetImpl(*this).SetFixedWidth( columnIndex, width ); +} + +float TableView::GetFixedWidth( unsigned int columnIndex ) const +{ + return GetImpl(*this).GetFixedWidth( columnIndex ); +} + +void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage ) +{ + GetImpl(*this).SetRelativeWidth( columnIndex, widthPercentage ); +} + +float TableView::GetRelativeWidth( unsigned int columnIndex ) const +{ + return GetImpl(*this).GetRelativeWidth( columnIndex ); +} + +void TableView::SetLayoutAnimationDuration( float duration ) +{ + GetImpl(*this).SetLayoutAnimationDuration( duration ); +} + +float TableView::GetLayoutAnimationDuration() +{ + return GetImpl(*this).GetLayoutAnimationDuration(); +} + +unsigned int TableView::GetRows() +{ + return GetImpl(*this).GetRows(); +} + +unsigned int TableView::GetColumns() +{ + return GetImpl(*this).GetColumns(); +} + +TableView::TableView(Internal::TableView& implementation) +: Control(implementation) +{ +} + +TableView::TableView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/table-view/table-view.h b/dali-toolkit/public-api/controls/table-view/table-view.h new file mode 100644 index 0000000..5f9bd5d --- /dev/null +++ b/dali-toolkit/public-api/controls/table-view/table-view.h @@ -0,0 +1,322 @@ +#ifndef __DALI_TOOLKIT_TABLE_VIEW_H__ +#define __DALI_TOOLKIT_TABLE_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class TableView; +} + +/** + * TableView is a layout container for aligning child actors in a grid like layout. + * TableView constrains the x and y position and width and height of the child actors. + * z position and depth are left intact so that 3D model actors can also be laid out + * in a grid without loosing their depth scaling. + */ +class TableView : public Control +{ +public: + + /** + * Structure to specify layout position for child actor + */ + struct CellPosition + { + /** + * Constructor to initialise values to defaults for convenience + */ + CellPosition( unsigned int rowIndex = 0, unsigned int columnIndex = 0, + unsigned int rowSpan = 1, unsigned int columnSpan = 1 ) + : rowIndex( rowIndex ), columnIndex( columnIndex ), + rowSpan( rowSpan ), columnSpan( columnSpan ) + { } + + unsigned int rowIndex; + unsigned int columnIndex; + unsigned int rowSpan; + unsigned int columnSpan; + }; + + /** + * Create a TableView handle; this can be initialised with TableView::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + TableView(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + TableView( const TableView& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + TableView& operator=( const TableView& handle ); + + /** + * Virtual destructor. + * Dali::Object derived classes typically do not contain member data. + */ + virtual ~TableView(); + + /** + * Create the TableView control. + * @param[in] initialRows for the table + * @param[in] initialColumns for the table + * @return A handle to the TableView control. + */ + static TableView New( unsigned int initialRows, unsigned int initialColumns ); + + /** + * Downcast an Object handle to TableView. If handle points to a TableView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a TableView or an uninitialized handle + */ + static TableView DownCast( BaseHandle handle ); + + /** + * Adds a child to the table + * If the row or column index is outside the table, the table gets resized bigger + * @pre The child actor has been initialized. + * @param[in] child to add + * @param[in] position for the child + * @return true if the addition succeeded, false if the cell is already occupied + */ + bool AddChild( Actor child, CellPosition position ); + + /** + * Returns a child from the given layout position + * Note! if there is no child in this position this method returns an uninitialized + * Actor handle + * @param[in] position in the table + * @return child that was in the cell or an uninitialized handle + */ + Actor GetChildAt( CellPosition position ); + + /** + * Removes a child from the given layout position + * Note! if there is no child in this position this method does nothing + * @param[in] position for the child to remove + * @return child that was removed or an uninitialized handle + */ + Actor RemoveChildAt( CellPosition position ); + + /** + * Finds the childs layout position + * @param[in] child to search for + * @param[out] position for the child + * @return true if the child was included in this TableView + */ + bool FindChildPosition( Actor child, CellPosition& position ); + + /** + * Insert a new row to given index + * @param [in] rowIndex of the new row + */ + void InsertRow( unsigned int rowIndex ); + + /** + * Delete a row from given index + * Removed elements are deleted + * @param [in] rowIndex of the row to delete + */ + void DeleteRow( unsigned int rowIndex ); + + /** + * Delete a row from given index + * @param [in] rowIndex of the row to delete + * @param [out] removed elements + */ + void DeleteRow( unsigned int rowIndex, std::vector& removed ); + + /** + * Insert a new column to given index + * @param [in] columnIndex of the new column + */ + void InsertColumn( unsigned int columnIndex ); + + /** + * Delete a column from given index. + * Removed elements are deleted + * @param [in] columnIndex of the column to delete + */ + void DeleteColumn( unsigned int columnIndex ); + + /** + * Delete a column from given index + * @param [in] columnIndex of the column to delete + * @param [out] removed elements + */ + void DeleteColumn( unsigned int columnIndex, std::vector& removed ); + + /** + * Resize the TableView. Note! if the new size is smaller than old, + * superfluous actors get removed. If you want to relayout removed children, + * use the variant that returns the removed Actors and reinsert them into the table + * If an actor spans to a removed row or column it gets removed from the table + * @param[in] rows for the table + * @param[in] columns for the table + */ + void Resize( unsigned int rows, unsigned int columns ); + + /** + * Resize the TableView. Note! if the new size is smaller than old, + * superfluous actors get removed. + * If an actor spans to a removed row or column it gets removed from the table + * @param[in] rows for the table + * @param[in] columns for the table + * @param[out] removed actor handles + */ + void Resize( unsigned int rows, unsigned int columns, std::vector& removed ); + + /** + * Set horizontal and vertical padding between cells + * @param[in] padding width and height + */ + void SetCellPadding( Size padding ); + + /** + * @return the current padding as width and height + */ + Size GetCellPadding(); + + /** + * Sets a row to have fixed height + * Setting a fixed height of 0 has no effect + * @pre The row rowIndex must exist. + * @param rowIndex for row with fixed height + * @param height in world coordinate units + */ + void SetFixedHeight( unsigned int rowIndex, float height ); + + /** + * Gets a row's fixed height. + * Note! The returned value is valid if it has been set before. + * @pre The row rowIndex must exist. + * @return height in world coordinate units. + */ + float GetFixedHeight( unsigned int rowIndex ) const; + + /** + * Sets a row to have relative height. Relative height means percentage of + * the remainder of the table height after subtracting Padding and Fixed height rows + * Setting a relative height of 0 has no effect + * @pre The row rowIndex must exist. + * @param rowIndex for row with relative height + * @param heightPercentage between 0.0f and 1.0f + */ + void SetRelativeHeight( unsigned int rowIndex, float heightPercentage ); + + /** + * Gets a row's relative height. + * Note! The returned value is valid if it has been set before. + * @pre The row rowIndex must exist. + * @return height in percentage units, between 0.0f and 1.0f. + */ + float GetRelativeHeight( unsigned int rowIndex ) const; + + /** + * Sets a column to have fixed width + * Setting a fixed width of 0 has no effect + * @pre The column columnIndex must exist. + * @param columnIndex for column with fixed width + * @param width in world coordinate units + */ + void SetFixedWidth( unsigned int columnIndex, float width ); + + /** + * Gets a column's fixed width. + * Note! The returned value is valid if it has been set before. + * @pre The column columnIndex must exist. + * @return width in world coordinate units. + */ + float GetFixedWidth( unsigned int columnIndex ) const; + + /** + * Sets a column to have relative width. Relative width means percentage of + * the remainder of table width after subtracting Padding and Fixed width columns + * Setting a relative width of 0 has no effect + * @pre The column columnIndex must exist. + * @param columnIndex for column with fixed width + * @param widthPercentage between 0.0f and 1.0f + */ + void SetRelativeWidth( unsigned int columnIndex, float widthPercentage ); + + /** + * Gets a column's relative width. + * Note! The returned value is valid if it has been set before. + * @pre The column columnIndex must exist. + * @return width in percentage units, between 0.0f and 1.0f. + */ + float GetRelativeWidth( unsigned int columnIndex ) const; + + /** + * Sets the layout animation duration for add, remove and relayout + * @param duration for the layout animations + * @note The default duration is 0.0f. + */ + void SetLayoutAnimationDuration( float duration ); + + /** + * Gets the layout animation duration for add, remove and relayout + * @return duration for the layout animations + */ + float GetLayoutAnimationDuration(); + + /** + * @return the amount of rows in the table + */ + unsigned int GetRows(); + + /** + * @return the amount of columns in the table + */ + unsigned int GetColumns(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + TableView(Internal::TableView& implementation); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + TableView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TABLE_VIEW_H__ diff --git a/dali-toolkit/public-api/controls/text-input/text-input.cpp b/dali-toolkit/public-api/controls/text-input/text-input.cpp new file mode 100644 index 0000000..685a5dd --- /dev/null +++ b/dali-toolkit/public-api/controls/text-input/text-input.cpp @@ -0,0 +1,384 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const TextInput::SIGNAL_START_INPUT( "start-input" ); +const char* const TextInput::SIGNAL_END_INPUT( "end-input" ); +const char* const TextInput::SIGNAL_STYLE_CHANGED( "style-changed" ); +const char* const TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED( "max-input-characters-reached" ); +const char* const TextInput::SIGNAL_TOOLBAR_DISPLAYED = "toolbar-displayed"; +const char* const TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES = "text-exceed-boundaries"; + +TextInput::TextInput() +{ +} + +TextInput::TextInput(Internal::TextInput& implementation) +: Control(implementation) +{ +} + +TextInput::TextInput( const TextInput& textInput ) +: Control( textInput ) +{ +} + +TextInput& TextInput::operator=( const TextInput& textInput ) +{ + if( &textInput != this ) + { + Control::operator=( textInput ); + } + + return *this; +} + +TextInput TextInput::New() +{ + return Internal::TextInput::New(); +} + +TextInput TextInput::DownCast( BaseHandle actor ) +{ + return Control::DownCast(actor); +} + +TextInput::~TextInput() +{ +} + +std::string TextInput::GetText() const +{ + return GetImpl(*this).GetText(); +} + +std::string TextInput::GetMarkupText() const +{ + return GetImpl(*this).GetMarkupText(); +} + +void TextInput::SetMaxCharacterLength(std::size_t maxChars) +{ + GetImpl(*this).SetMaxCharacterLength(maxChars); +} + +void TextInput::SetNumberOfLinesLimit(std::size_t maxLines) +{ + GetImpl(*this).SetNumberOfLinesLimit( maxLines ); +} + +std::size_t TextInput::GetNumberOfLinesLimit() const +{ + return GetImpl(*this).GetNumberOfLinesLimit(); +} + +std::size_t TextInput::GetNumberOfCharacters() const +{ + return GetImpl(*this).GetNumberOfCharacters(); +} + +void TextInput::SetPlaceholderText( const std::string& placeHolderText ) +{ + GetImpl(*this).SetPlaceholderText( placeHolderText ); +} + +std::string TextInput::GetPlaceholderText() +{ + return GetImpl(*this).GetPlaceholderText(); +} + +void TextInput::SetInitialText(const std::string& initialText) +{ + GetImpl(*this).SetInitialText(initialText); +} + +void TextInput::SetEditable(bool editMode) +{ + GetImpl(*this).SetEditable(editMode, false); +} + +void TextInput::SetEditable(bool editMode, const Vector2& touchPoint) +{ + GetImpl(*this).SetEditable(editMode, true, touchPoint); +} + +bool TextInput::IsEditable() const +{ + return GetImpl(*this).IsEditable(); +} + +void TextInput::SetEditOnTouch( bool editOnTouch ) +{ + GetImpl(*this).SetEditOnTouch( editOnTouch ); +} + +bool TextInput::IsEditOnTouch() const +{ + return GetImpl(*this).IsEditOnTouch(); +} + +void TextInput::SetTextSelectable( bool textSelectable ) +{ + GetImpl(*this).SetTextSelectable( textSelectable ); +} + +bool TextInput::IsTextSelectable() const +{ + return GetImpl(*this).IsTextSelectable(); +} + +bool TextInput::IsTextSelected() const +{ + return GetImpl(*this).IsTextSelected(); +} + +void TextInput::SelectText(std::size_t start, std::size_t end) +{ + GetImpl(*this).SelectText( start, end ); +} + +void TextInput::DeSelectText() +{ + GetImpl(*this).DeSelectText(); +} + +void TextInput::SetGrabHandleImage( Image image ) +{ + GetImpl(*this).SetGrabHandleImage(image); +} + +void TextInput::SetCursorImage(Dali::Image image, const Vector4& border ) +{ + GetImpl(*this).SetCursorImage(image, border ); +} + +Vector3 TextInput::GetSelectionHandleSize() +{ + return GetImpl(*this).GetSelectionHandleSize(); +} + +void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border ) +{ + GetImpl(*this).SetRTLCursorImage(image, border ); +} + +void TextInput::EnableGrabHandle(bool toggle) +{ + GetImpl(*this).EnableGrabHandle( toggle ); +} + +bool TextInput::IsGrabHandleEnabled() +{ + return GetImpl(*this).IsGrabHandleEnabled(); +} + +void TextInput::EnableSelectionHandleFlip( bool toggle ) +{ + GetImpl(*this).EnableSelectionHandleFlip( toggle ); +} + +bool TextInput::IsSelectionHandleFlipEnabled() +{ + return GetImpl(*this).IsSelectionHandleFlipEnabled(); +} + +void TextInput::SetSelectionHandleFlipMargin( const Vector4& border ) +{ + GetImpl(*this).SetSelectionHandleFlipMargin( border ); +} + +void TextInput::SetBoundingRectangle( const Rect& boundingOriginAndSize ) +{ + GetImpl(*this).SetBoundingRectangle( boundingOriginAndSize ); +} + +const Rect TextInput::GetBoundingRectangle() const +{ + return GetImpl(*this).GetBoundingRectangle(); +} + +const Vector4& TextInput::GetSelectionHandleFlipMargin() +{ + return GetImpl(*this).GetSelectionHandleFlipMargin(); +} + +void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask ) +{ + GetImpl(*this).SetActiveStyle(style,mask); +} + +void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask ) +{ + GetImpl(*this).ApplyStyle( style, mask ); +} + +void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask ) +{ + GetImpl(*this).ApplyStyleToAll( style, mask ); +} + +TextStyle TextInput::GetStyleAtCursor() const +{ + return GetImpl(*this).GetStyleAtCursor(); +} + +void TextInput::SetTextAlignment( Toolkit::Alignment::Type align ) +{ + GetImpl(*this).SetTextAlignment(align); +} + +void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification ) +{ + GetImpl(*this).SetTextLineJustification(justification); +} + +void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary ) +{ + GetImpl(*this).SetFadeBoundary( fadeBoundary ); +} + +const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const +{ + return GetImpl(*this).GetFadeBoundary(); +} + +Alignment::Type TextInput::GetTextAlignment() const +{ + return GetImpl(*this).GetTextAlignment(); +} + +void TextInput::SetMultilinePolicy( TextView::MultilinePolicy policy ) +{ + GetImpl(*this).SetMultilinePolicy(policy); +} + +TextView::MultilinePolicy TextInput::GetMultilinePolicy() const +{ + return GetImpl(*this).GetMultilinePolicy(); +} + +void TextInput::SetWidthExceedPolicy( TextView::ExceedPolicy policy ) +{ + GetImpl(*this).SetWidthExceedPolicy(policy); +} + +TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const +{ + return GetImpl(*this).GetWidthExceedPolicy(); +} + +void TextInput::SetHeightExceedPolicy( TextView::ExceedPolicy policy ) +{ + GetImpl(*this).SetHeightExceedPolicy(policy); +} + +TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const +{ + return GetImpl(*this).GetHeightExceedPolicy(); +} + +void TextInput::SetExceedEnabled( bool enable ) +{ + GetImpl(*this).SetExceedEnabled( enable ); +} + +bool TextInput::GetExceedEnabled() const +{ + return GetImpl(*this).GetExceedEnabled(); +} + +void TextInput::SetSortModifier( float depthOffset ) +{ + GetImpl( *this ).SetSortModifier( depthOffset ); +} + +void TextInput::SetSnapshotModeEnabled( bool enable ) +{ + GetImpl( *this ).SetSnapshotModeEnabled( enable ); +} + +bool TextInput::IsSnapshotModeEnabled() const +{ + return GetImpl( *this ).IsSnapshotModeEnabled(); +} + +void TextInput::SetScrollEnabled( bool enable ) +{ + GetImpl( *this ).SetScrollEnabled( enable ); +} + +bool TextInput::IsScrollEnabled() const +{ + return GetImpl( *this ).IsScrollEnabled(); +} + +void TextInput::SetScrollPosition( const Vector2& position ) +{ + GetImpl( *this ).SetScrollPosition( position ); +} + +Vector2 TextInput::GetScrollPosition() const +{ + return GetImpl( *this ).GetScrollPosition(); +} + +TextInput::InputSignalV2& TextInput::InputStartedSignal() +{ + return GetImpl(*this).InputStartedSignal(); +} + +TextInput::InputSignalV2& TextInput::InputFinishedSignal() +{ + return GetImpl(*this).InputFinishedSignal(); +} + +TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal() +{ + return GetImpl(*this).CutAndPasteToolBarDisplayedSignal(); +} + +TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal() +{ + return GetImpl(*this).StyleChangedSignal(); +} + +TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal() +{ + return GetImpl(*this).MaxInputCharactersReachedSignal(); +} + +TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal() +{ + return GetImpl(*this).InputTextExceedBoundariesSignal(); +} + +TextInput::TextInput( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/text-view/text-view.cpp b/dali-toolkit/public-api/controls/text-view/text-view.cpp new file mode 100644 index 0000000..7ea334e --- /dev/null +++ b/dali-toolkit/public-api/controls/text-view/text-view.cpp @@ -0,0 +1,388 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + + +const char* const TextView::SIGNAL_TEXT_SCROLLED = "scrolled"; +const char* const TextView::PROPERTY_TEXT = "text"; +const char* const TextView::PROPERTY_MULTILINE_POLICY = "multiline-policy"; +const char* const TextView::PROPERTY_WIDTH_EXCEED_POLICY = "width-exceed-policy"; +const char* const TextView::PROPERTY_HEIGHT_EXCEED_POLICY = "height-exceed-policy"; +const char* const TextView::PROPERTY_LINE_JUSTIFICATION = "line-justification"; +const char* const TextView::PROPERTY_FADE_BOUNDARY_LEFT = "fade-boundary-left"; +const char* const TextView::PROPERTY_FADE_BOUNDARY_RIGHT = "fade-boundary-right"; +const char* const TextView::PROPERTY_FADE_BOUNDARY_TOP = "fade-boundary-top"; +const char* const TextView::PROPERTY_FADE_BOUNDARY_BOTTOM = "fade-boundary-bottom"; +const char* const TextView::PROPERTY_LINE_HEIGHT_OFFSET = "line-height-offset"; +const char* const TextView::PROPERTY_HORIZONTAL_ALIGNMENT = "horizontal-alignment"; +const char* const TextView::PROPERTY_VERTICAL_ALIGNMENT = "vertical-alignment"; + +TextView::CharacterLayoutInfo::CharacterLayoutInfo() +: mSize(), + mPosition(), + mIsNewLineChar( false ), + mIsRightToLeftCharacter( false ), + mIsVisible( true ), + mDescender() +{ +} + +TextView::CharacterLayoutInfo::~CharacterLayoutInfo() +{ +} + +TextView::CharacterLayoutInfo::CharacterLayoutInfo( const TextView::CharacterLayoutInfo& characterLayoutInfo ) +: mSize( characterLayoutInfo.mSize ), + mPosition( characterLayoutInfo.mPosition ), + mIsNewLineChar( characterLayoutInfo.mIsNewLineChar ), + mIsRightToLeftCharacter( characterLayoutInfo.mIsRightToLeftCharacter ), + mIsVisible( characterLayoutInfo.mIsVisible ), + mDescender( characterLayoutInfo.mDescender ) +{ +} + +TextView::CharacterLayoutInfo& TextView::CharacterLayoutInfo::operator=( const TextView::CharacterLayoutInfo& characterLayoutInfo ) +{ + mSize = characterLayoutInfo.mSize; + mPosition = characterLayoutInfo.mPosition; + mIsNewLineChar = characterLayoutInfo.mIsNewLineChar; + mIsRightToLeftCharacter = characterLayoutInfo.mIsRightToLeftCharacter; + mIsVisible = characterLayoutInfo.mIsVisible; + mDescender = characterLayoutInfo.mDescender; + + return *this; +} + +TextView::CharacterLayoutInfo::CharacterLayoutInfo( const Size& size, + const Vector3& position, + const bool isNewLineChar, + const bool isRightToLeftCharacter, + const bool isVisible, + const float descender ) +: mSize( size ), + mPosition( position ), + mIsNewLineChar( isNewLineChar ), + mIsRightToLeftCharacter( isRightToLeftCharacter ), + mIsVisible( isVisible ), + mDescender( descender ) +{ +} + +TextView::TextLayoutInfo::TextLayoutInfo() +: mCharacterLayoutInfoTable(), + mLines(), + mCharacterLogicalToVisualMap(), + mCharacterVisualToLogicalMap(), + mTextSize(), + mScrollOffset() +{ +} + +TextView::TextLayoutInfo::~TextLayoutInfo() +{ +} + +TextView::TextLayoutInfo::TextLayoutInfo( const TextView::TextLayoutInfo& textLayoutInfo ) +: mCharacterLayoutInfoTable( textLayoutInfo.mCharacterLayoutInfoTable ), + mLines( textLayoutInfo.mLines ), + mCharacterLogicalToVisualMap( textLayoutInfo.mCharacterLogicalToVisualMap ), + mCharacterVisualToLogicalMap( textLayoutInfo.mCharacterVisualToLogicalMap ), + mTextSize( textLayoutInfo.mTextSize ), + mScrollOffset( textLayoutInfo.mScrollOffset ) +{ +} + +TextView::TextLayoutInfo& TextView::TextLayoutInfo::operator=( const TextView::TextLayoutInfo& textLayoutInfo ) +{ + mCharacterLayoutInfoTable = textLayoutInfo.mCharacterLayoutInfoTable; + mLines = textLayoutInfo.mLines; + mCharacterLogicalToVisualMap = textLayoutInfo.mCharacterLogicalToVisualMap; + mCharacterVisualToLogicalMap = textLayoutInfo.mCharacterVisualToLogicalMap; + mTextSize = textLayoutInfo.mTextSize; + mScrollOffset = textLayoutInfo.mScrollOffset; + + return *this; +} + +TextView::FadeBoundary::FadeBoundary() +: mLeft( 0 ), + mRight( 0 ), + mTop( 0 ), + mBottom( 0 ) +{ +} + +TextView::FadeBoundary::FadeBoundary( const PixelSize left, const PixelSize right, const PixelSize top, const PixelSize bottom ) +: mLeft( left ), + mRight( right ), + mTop( top ), + mBottom( bottom ) +{ +} + +TextView::TextView() +{ +} + +TextView::TextView( const TextView& handle ) +: Control( handle ) +{ +} + +TextView::TextView( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +TextView& TextView::operator=( const TextView& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +TextView TextView::New() +{ + return Internal::TextView::New(); +} + +TextView TextView::New( const std::string& text ) +{ + TextView textView = Internal::TextView::New(); + textView.SetText( text ); + return textView; +} + +TextView TextView::New( const MarkupProcessor::StyledTextArray& text ) +{ + TextView textView = Internal::TextView::New(); + textView.SetText( text ); + return textView; +} + +TextView TextView::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +TextView::~TextView() +{ +} + +void TextView::SetText( const std::string& text ) +{ + GetImpl( *this ).SetText( text ); +} + +void TextView::SetText( const MarkupProcessor::StyledTextArray& text ) +{ + GetImpl( *this ).SetText( text ); +} + +void TextView::InsertTextAt( const std::size_t position, const std::string& text ) +{ + GetImpl( *this ).InsertTextAt( position, text ); +} + +void TextView::InsertTextAt( const std::size_t position, const MarkupProcessor::StyledTextArray& text ) +{ + GetImpl( *this ).InsertTextAt( position, text ); +} + +void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const std::string& text ) +{ + GetImpl( *this ).ReplaceTextFromTo( position, numberOfCharacters, text ); +} + +void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text ) +{ + GetImpl( *this ).ReplaceTextFromTo( position, numberOfCharacters, text ); +} + +void TextView::RemoveTextFrom( const std::size_t position, const std::size_t numberOfCharacters ) +{ + GetImpl( *this ).RemoveTextFrom( position, numberOfCharacters ); +} + +std::string TextView::GetText() const +{ + return GetImpl( *this ).GetText(); +} + +void TextView::SetLineHeightOffset( const PointSize offset ) +{ + GetImpl( *this ).SetLineHeightOffset( offset ); +} + +PointSize TextView::GetLineHeightOffset() const +{ + return GetImpl( *this ).GetLineHeightOffset(); +} + +void TextView::SetStyleToCurrentText( const TextStyle& style, const TextStyle::Mask mask ) +{ + GetImpl( *this ).SetStyleToCurrentText( style, mask ); +} + +void TextView::SetTextAlignment( Alignment::Type align ) +{ + GetImpl( *this ).SetTextAlignment( align ); +} + +Alignment::Type TextView::GetTextAlignment() const +{ + return GetImpl( *this ).GetTextAlignment(); +} + +void TextView::SetMultilinePolicy( TextView::MultilinePolicy policy ) +{ + GetImpl( *this ).SetMultilinePolicy( policy ); +} + +TextView::MultilinePolicy TextView::GetMultilinePolicy() const +{ + return GetImpl( *this ).GetMultilinePolicy(); +} + +void TextView::SetWidthExceedPolicy( TextView::ExceedPolicy policy ) +{ + GetImpl( *this ).SetWidthExceedPolicy( policy ); +} + +TextView::ExceedPolicy TextView::GetWidthExceedPolicy() const +{ + return GetImpl( *this ).GetWidthExceedPolicy(); +} + +void TextView::SetHeightExceedPolicy( ExceedPolicy policy ) +{ + GetImpl( *this ).SetHeightExceedPolicy( policy ); +} + +TextView::ExceedPolicy TextView::GetHeightExceedPolicy() const +{ + return GetImpl( *this ).GetHeightExceedPolicy(); +} + +void TextView::SetLineJustification( TextView::LineJustification justification ) +{ + GetImpl( *this ).SetLineJustification( justification ); +} + +TextView::LineJustification TextView::GetLineJustification() const +{ + return GetImpl( *this ).GetLineJustification(); +} + +void TextView::SetFadeBoundary( const TextView::FadeBoundary& fadeBoundary ) +{ + GetImpl( *this ).SetFadeBoundary( fadeBoundary ); +} + +const TextView::FadeBoundary& TextView::GetFadeBoundary() const +{ + return GetImpl( *this ).GetFadeBoundary(); +} + +void TextView::SetEllipsizeText( const std::string& ellipsizeText ) +{ + GetImpl( *this ).SetEllipsizeText( ellipsizeText ); +} + +void TextView::SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText ) +{ + GetImpl( *this ).SetEllipsizeText( ellipsizeText ); +} + +std::string TextView::GetEllipsizeText() const +{ + return GetImpl( *this ).GetEllipsizeText(); +} + +void TextView::GetTextLayoutInfo( TextLayoutInfo& textLayoutInfo ) +{ + GetImpl( *this ).GetTextLayoutInfo( textLayoutInfo ); +} + +void TextView::SetSortModifier( float depthOffset ) +{ + GetImpl( *this ).SetSortModifier( depthOffset ); +} + +void TextView::SetSnapshotModeEnabled( bool enable ) +{ + GetImpl( *this ).SetSnapshotModeEnabled( enable ); +} + +bool TextView::IsSnapshotModeEnabled() const +{ + return GetImpl( *this ).IsSnapshotModeEnabled(); +} + +void TextView::SetScrollEnabled( bool enable ) +{ + GetImpl( *this ).SetScrollEnabled( enable ); +} + +bool TextView::IsScrollEnabled() const +{ + return GetImpl( *this ).IsScrollEnabled(); +} + +void TextView::SetScrollPosition( const Vector2& position ) +{ + GetImpl( *this ).SetScrollPosition( position ); +} + +const Vector2& TextView::GetScrollPosition() const +{ + return GetImpl( *this ).GetScrollPosition(); +} + +bool TextView::IsScrollPositionTrimmed() const +{ + return GetImpl( *this ).IsScrollPositionTrimmed(); +} + +TextView::ScrolledSignalV2& TextView::ScrolledSignal() +{ + return GetImpl( *this ).ScrolledSignal(); +} + +TextView::TextView( Internal::TextView& implementation ) +: Control( implementation ) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/tool-bar/tool-bar.cpp b/dali-toolkit/public-api/controls/tool-bar/tool-bar.cpp new file mode 100644 index 0000000..d07cbfc --- /dev/null +++ b/dali-toolkit/public-api/controls/tool-bar/tool-bar.cpp @@ -0,0 +1,96 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "tool-bar.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const Toolkit::Alignment::Padding ToolBar::DEFAULT_PADDING( 0.f, 0.f, 0.f, 0.f ); + +ToolBar::ToolBar() +{ +} + +ToolBar::ToolBar( const ToolBar& handle ) +: Control( handle ) +{ +} + +ToolBar& ToolBar::operator=( const ToolBar& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +ToolBar::~ToolBar() +{ +} + +ToolBar ToolBar::New() +{ + return Internal::ToolBar::New(); +} + +ToolBar ToolBar::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +void ToolBar::SetBackground( Actor background ) +{ + GetImpl( *this ).SetBackground( background ); +} + +void ToolBar::AddControl( Actor control, float relativeSize, Toolkit::Alignment::Type alignment, const Toolkit::Alignment::Padding& padding ) +{ + GetImpl( *this ).AddControl( control, relativeSize, alignment, padding ); +} + +void ToolBar::RemoveControl( Actor control ) +{ + GetImpl( *this ).RemoveControl( control ); +} + +ToolBar::ToolBar( Internal::ToolBar& implementation ) +: Control( implementation ) +{ +} + +ToolBar::ToolBar( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/tool-bar/tool-bar.h b/dali-toolkit/public-api/controls/tool-bar/tool-bar.h new file mode 100644 index 0000000..b05439e --- /dev/null +++ b/dali-toolkit/public-api/controls/tool-bar/tool-bar.h @@ -0,0 +1,131 @@ +#ifndef __DALI_TOOLKIT_TOOL_BAR_H__ +#define __DALI_TOOLKIT_TOOL_BAR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class ToolBar; +} + +/** + * Provides a tool bar where other controls (Dali::Actor) could be placed. + * controls could be added into three different groups: on the left, center or right. + * AddControl() and RemoveControl() methods should be used to add and remove controls. The use of Actor's Dali::Actor::Add() method + * is not forbidden, it adds controls on the left group with a size of 10% of the total tool bar size. + * Dali::Actor::Remove() method does nothing. + */ +class ToolBar : public Control +{ +public: + static const Toolkit::Alignment::Padding DEFAULT_PADDING; ///< Default padding space between controls. By default all values are set to 0. + +public: + /** + * Create a ToolBar handle; this can be initialised with ToolBar::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + ToolBar(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + ToolBar( const ToolBar& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + ToolBar& operator=( const ToolBar& handle ); + + /** + * virtual Destructor. + */ + virtual ~ToolBar(); + + /** + * Create an initialized ToolBar. + * @return A handle to a newly allocated Dali resource. + */ + static ToolBar New(); + + /** + * Downcast an Object handle to ToolBar. If handle points to a ToolBar the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a ToolBar or an uninitialized handle + */ + static ToolBar DownCast( BaseHandle handle ); + + /** + * Sets a background image. + * @param background Actor with the tool bar background. + */ + void SetBackground( Actor background ); + + /** + * Adds an additional control to the tool bar. + * @pre The tool bar needs to be initialized. + * @pre The alignment needs to be horizontal. + * @param control An Actor with the additional control. + * @param relativeSize Control's size as a percentage of the tool bar width. + * @param alignment Where to insert controls. Possible values are Toolkit::Alignment::Left, Toolkit::Alignment::Center or Toolkit::Alignment::Right. + * @param padding Padding values used for the added control (left, right, top, bottom). By default, no padding is added. + */ + void AddControl( Actor control, float relativeSize, Toolkit::Alignment::Type alignment, const Toolkit::Alignment::Padding& padding = DEFAULT_PADDING ); + + /** + * Removes a control from the tool bar. + * @pre control must have been added before to this tool bar. + * @param control The control to be removed. + */ + void RemoveControl( Actor control ); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + ToolBar( Internal::ToolBar& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + ToolBar( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TOOL_BAR_H__ diff --git a/dali-toolkit/public-api/controls/view/view.cpp b/dali-toolkit/public-api/controls/view/view.cpp new file mode 100644 index 0000000..aeba700 --- /dev/null +++ b/dali-toolkit/public-api/controls/view/view.cpp @@ -0,0 +1,125 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "view.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const View::SIGNAL_ORIENTATION_ANIMATION_START = "orientation-animation-start"; + +View::View() +{ +} + +View::View( const View& handle ) +: Control( handle ) +{ +} + +View& View::operator=( const View& handle ) +{ + if( &handle != this ) + { + Control::operator=( handle ); + } + return *this; +} + +View::~View() +{ +} + +View View::New( bool fullscreen ) +{ + return Internal::View::New( fullscreen ); +} + +View View::DownCast( BaseHandle handle ) +{ + return Control::DownCast(handle); +} + +Layer View::GetContentLayer( unsigned int index ) const +{ + return GetImpl( *this ).GetContentLayer( index ); +} + +unsigned int View::AddContentLayer( Layer layer ) +{ + return GetImpl( *this ).AddContentLayer( layer ); +} + +void View::RemoveContentLayer( Layer layer ) +{ + GetImpl( *this ).RemoveContentLayer( layer ); +} + +Layer View::GetBackgroundLayer() const +{ + return GetImpl( *this ).GetBackgroundLayer(); +} + +void View::SetBackground( ImageActor image ) +{ + GetImpl( *this ).SetBackground( image ); +} + +void View::SetOrientationFunction( Degree portrait, Degree landscale, Degree portraitInverse, Degree landscapeInverse ) +{ + GetImpl( *this ).SetOrientationFunction( portrait, landscale, portraitInverse, landscapeInverse ); +} + +void View::OrientationChanged( Orientation orientation ) +{ + GetImpl( *this ).OrientationChanged( orientation ); +} + +void View::SetAutoRotate( bool enabled ) +{ + GetImpl( *this ).SetAutoRotate( enabled ); +} + +View::OrientationAnimationStartedSignalV2& View::OrientationAnimationStartedSignal() +{ + return GetImpl( *this ).OrientationAnimationStartedSignal(); +} + +View::View( Internal::View& implementation ) +: Control( implementation ) +{ +} + +View::View( Dali::Internal::CustomActor* internal ) +: Control( internal ) +{ + VerifyCustomActorPointer(internal); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/controls/view/view.h b/dali-toolkit/public-api/controls/view/view.h new file mode 100644 index 0000000..08826c0 --- /dev/null +++ b/dali-toolkit/public-api/controls/view/view.h @@ -0,0 +1,216 @@ +#ifndef __DALI_TOOLKIT_VIEW_H__ +#define __DALI_TOOLKIT_VIEW_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class CustomActor; +} + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +// Forward declarations +class View; +} + +/** + * View provides a container where different Dali::Layer instances and a background could be added. It + * also provides a View::OrientationChanged() method which could be connected to the Dali::Orientation::SignalChange() signal. + * This method rotates all layers accordingly with the given orientation, it emits an OrientationAnimationStartsSignal signal just before the rotation animation starts. + * + * By default view anchor point and parent origin are centered, the size is full screen and is got directly from the Dali::Stage. However, by passing \e false to the + * Toolkit::View::New() method a custom size could be specified, and after initialization, anchor point and parent origin could be updated. + * + * If a background is set, a background layer will be created and dropped to the bottom. + * + * Use example (application is a Dali::Application object): + * \code{.cpp} + * Stage stage = Stage::GetCurrent(); + * + * // Create default View. By default it gets the stage size. + * Toolkit::View view = Toolkit::View::New(); + * + * // Add the view to the stage before setting the background. + * stage.Add( view ); + * + * // Set background image. BACKGROUND_IMAGE is a string with the background image file path. + * Image backgroundImage = Image::New( BACKGROUND_IMAGE ); + * ImageActor backgroundImageActor = ImageActor::New( backgroundImage ); + * mView.SetBackground( backgroundImageActor ); + * + * // Connects the orientation signal with the View::OrientationChanged method. + * application.GetOrientation().ChangedSignal().Connect( &view, &Toolkit::View::OrientationChanged ); + * + * // Create a content layer. + * Layer contentLayer = Layer::New(); + * contentLayer.SetAnchorPoint( AnchorPoint::CENTER ); + * contentLayer.SetParentOrigin( ParentOrigin::CENTER ); + * contentLayer.ApplyConstraint( ParentConstraint::Size::New( ParentSize() ) ); + * view.AddContentLayer( contentLayer ); + * \endcode + */ +class View : public Control +{ +public: + //Signal Names + static const char* const SIGNAL_ORIENTATION_ANIMATION_START; + +public: + + /** + * Create a View handle; this can be initialised with View::New() + * Calling member functions with an uninitialised handle is not allowed. + */ + View(); + + /** + * Copy constructor. Creates another handle that points to the same real object + * @param handle to copy from + */ + View( const View& handle ); + + /** + * Assignment operator. Changes this handle to point to another real object + */ + View& operator=( const View& handle ); + + /** + * virtual Destructor. + */ + virtual ~View(); + + /** + * Create an initialized View. + * @param fullscreen If true, the view's size is going to be set with the Dali::Stage size. Otherwise a size must be provided. By default fullscreen is set to true. + * @return A handle to a newly allocated Dali resource. + */ + static View New( bool fullscreen = true ); + + /** + * Downcast an Object handle to View. If handle points to a View the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a View or an uninitialized handle + */ + static View DownCast( BaseHandle handle ); + + /** + * Returns a content layer. + * @param index to the layer container. + * @return A Layer handle if it exists, otherwise it returns an uninitialized handle. + */ + Layer GetContentLayer( unsigned int index ) const; + + /** + * Adds a new layer in the view. + * @pre layer must be initialized. + * @param layer A Layer handle. + * @return the an index that can be used to access the layer stores in the view. + */ + unsigned int AddContentLayer( Layer layer ); + + /** + * Removes a layer from the view. + * @param layer The layer to be removed. + */ + void RemoveContentLayer( Layer layer ); + + /** + * Returns the background layer. + * @return the background layer or an empty handle if any background has been set before. + */ + Layer GetBackgroundLayer() const; + + /** + * Sets a background image. + * + * It creates a background layer the first time this method is called and it is dropped to the bottom. + * Any previous set background will be replaced by the new one. + * + * @pre View must be on stage before calling SetBackground. + * @param image An image actor. + */ + void SetBackground( ImageActor image ); + + /** + * Sets the angle values for portrait, landscape, portrait inverse and landscape inverse. + * + * These angles are used to rotate the views. + * By default, orientation angles are initialized as follows: portrait 0, landscape 90, portrait inverse 180, landscape inverse 270. + * + * @param portrait angle in degrees. + * @param landscale angle in degrees. + * @param portraitInverse angle in degrees. + * @param landscapeInverse angle in degrees. + */ + void SetOrientationFunction( Degree portrait, Degree landscale, Degree portraitInverse, Degree landscapeInverse ); + + /** + * It rotates all layers to the new given orientation. + * + * @param orientation The new orientation. + */ + void OrientationChanged( Orientation orientation ); + + /** + * Enables or disables the view's rotation when device orientation changes. + * @param enabled Whether auto-rotate should be enabled or disabled. By default is enabled. + */ + void SetAutoRotate( bool enabled ); + +public: //Signals + + // Orientation change animation starts. + typedef SignalV2< void ( View, Animation&, const Orientation& ) > OrientationAnimationStartedSignalV2; + + /** + * Signal emitted just before the rotate animation starts when the device orientation changes. + */ + OrientationAnimationStartedSignalV2& OrientationAnimationStartedSignal(); + +public: // Not intended for application developers + + /** + * Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The Control implementation. + */ + View( Internal::View& implementation ); + + /** + * Allows the creation of this Control from an Internal::CustomActor pointer. + * @param[in] internal A pointer to the internal CustomActor. + */ + View( Dali::Internal::CustomActor* internal ); +}; + + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_VIEW_H__ diff --git a/dali-toolkit/public-api/enums.cpp b/dali-toolkit/public-api/enums.cpp new file mode 100644 index 0000000..4aea835 --- /dev/null +++ b/dali-toolkit/public-api/enums.cpp @@ -0,0 +1,39 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +bool IsVertical(ControlOrientation::Type orientation) +{ + return (orientation == ControlOrientation::Up || + orientation == ControlOrientation::Down); +} + +bool IsHorizontal(ControlOrientation::Type orientation) +{ + return (orientation == ControlOrientation::Left || + orientation == ControlOrientation::Right); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/factory/localized-control-factory.cpp b/dali-toolkit/public-api/factory/localized-control-factory.cpp new file mode 100644 index 0000000..ae24eec --- /dev/null +++ b/dali-toolkit/public-api/factory/localized-control-factory.cpp @@ -0,0 +1,77 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +Dali::Toolkit::TextView LocalizedControlFactory::CreateLocalizedTextView( const std::string& textID, const std::string& textDomain, const std::string& textViewTheme ) +{ + LocalizedControlFactory factory = Get(); + return GetImpl(factory).CreateLocalizedTextView(textID, textDomain, textViewTheme); +} + +LocalizedControlFactory::LocalizedControlFactory() +{ +} + +LocalizedControlFactory::~LocalizedControlFactory() +{ +} + +LocalizedControlFactory LocalizedControlFactory::Get() +{ + LocalizedControlFactory factory; + + // Check whether the focus factory is already created + Dali::Adaptor& adaptor = Dali::Adaptor::Get(); + Dali::BaseHandle handle = adaptor.GetSingleton(typeid(LocalizedControlFactory)); + if(handle) + { + // If so, downcast the handle of singleton to focus factory + factory = LocalizedControlFactory(dynamic_cast(handle.GetObjectPtr())); + } + + if(!factory) + { + // If not, create the focus factory and register it as a singleton + factory = LocalizedControlFactory(new Internal::LocalizedControlFactory()); + adaptor.RegisterSingleton(typeid(factory), factory); + } + + return factory; +} + +LocalizedControlFactory::LocalizedControlFactory(Internal::LocalizedControlFactory *impl) + : BaseHandle(impl) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/file.list b/dali-toolkit/public-api/file.list new file mode 100755 index 0000000..dc82d05 --- /dev/null +++ b/dali-toolkit/public-api/file.list @@ -0,0 +1,233 @@ +# Add local source files here + +public_api_src_files = \ + $(public_api_src_dir)/enums.cpp \ + $(public_api_src_dir)/controls/control.cpp \ + $(public_api_src_dir)/controls/control-impl.cpp \ + $(public_api_src_dir)/controls/alignment/alignment.cpp \ + $(public_api_src_dir)/controls/bloom-view/bloom-view.cpp \ + $(public_api_src_dir)/controls/buttons/button.cpp \ + $(public_api_src_dir)/controls/buttons/check-box-button.cpp \ + $(public_api_src_dir)/controls/buttons/push-button.cpp \ + $(public_api_src_dir)/controls/cluster/cluster.cpp \ + $(public_api_src_dir)/controls/cluster/cluster-style.cpp \ + $(public_api_src_dir)/controls/default-controls/solid-color-actor.cpp \ + $(public_api_src_dir)/controls/default-controls/check-button-factory.cpp \ + $(public_api_src_dir)/controls/default-controls/push-button-factory.cpp \ + $(public_api_src_dir)/controls/effects-view/effects-view.cpp \ + $(public_api_src_dir)/controls/gaussian-blur-view/gaussian-blur-view.cpp \ + $(public_api_src_dir)/controls/image-view/image-view.cpp \ + $(public_api_src_dir)/controls/image-view/masked-image-view.cpp \ + $(public_api_src_dir)/controls/magnifier/magnifier.cpp \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-view.cpp \ + $(public_api_src_dir)/controls/page-turn-view/page-factory.cpp \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-portrait-view.cpp \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-landscape-view.cpp \ + $(public_api_src_dir)/controls/popup/popup.cpp \ + $(public_api_src_dir)/controls/scroll-component/scroll-bar.cpp \ + $(public_api_src_dir)/controls/scroll-component/scroll-component.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/item-factory.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/item-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/item-view.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/depth-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/grid-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/navigation-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/roll-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/spiral-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/item-view/album-layout.cpp \ + $(public_api_src_dir)/controls/scrollable/scrollable.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-constraints.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-custom-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-carousel-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-cube-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-depth-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-page-cube-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-page-spiral-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-slide-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-twist-effect.cpp \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-wobble-effect.cpp \ + $(public_api_src_dir)/controls/selectors/rotating-selector.cpp \ + $(public_api_src_dir)/controls/shadow-view/shadow-view.cpp \ + $(public_api_src_dir)/controls/slider/slider.cpp \ + $(public_api_src_dir)/controls/super-blur-view/super-blur-view.cpp \ + $(public_api_src_dir)/controls/table-view/table-view.cpp \ + $(public_api_src_dir)/controls/text-input/text-input.cpp \ + $(public_api_src_dir)/controls/text-view/text-view.cpp \ + $(public_api_src_dir)/controls/tool-bar/tool-bar.cpp \ + $(public_api_src_dir)/controls/view/view.cpp \ + $(public_api_src_dir)/controls/navigation-frame/navigation-control.cpp \ + $(public_api_src_dir)/controls/navigation-frame/page.cpp \ + $(public_api_src_dir)/controls/bubble-effect/bubble-emitter.cpp \ + $(public_api_src_dir)/factory/localized-control-factory.cpp \ + $(public_api_src_dir)/focus-manager/focus-manager.cpp \ + $(public_api_src_dir)/focus-manager/keyboard-focus-manager.cpp \ + $(public_api_src_dir)/focus-manager/keyinput-focus-manager.cpp \ + $(public_api_src_dir)/markup-processor/markup-processor.cpp \ + $(public_api_src_dir)/shader-effects/alpha-discard-effect.cpp \ + $(public_api_src_dir)/shader-effects/bendy-effect.cpp \ + $(public_api_src_dir)/shader-effects/blind-effect.cpp \ + $(public_api_src_dir)/shader-effects/carousel-effect.cpp \ + $(public_api_src_dir)/shader-effects/displacement-effect.cpp \ + $(public_api_src_dir)/shader-effects/dissolve-effect.cpp \ + $(public_api_src_dir)/shader-effects/dissolve-local-effect.cpp \ + $(public_api_src_dir)/shader-effects/distance-field-effect.cpp \ + $(public_api_src_dir)/shader-effects/image-region-effect.cpp \ + $(public_api_src_dir)/shader-effects/iris-effect.cpp \ + $(public_api_src_dir)/shader-effects/mask-effect.cpp \ + $(public_api_src_dir)/shader-effects/mirror-effect.cpp \ + $(public_api_src_dir)/shader-effects/motion-blur-effect.cpp \ + $(public_api_src_dir)/shader-effects/motion-stretch-effect.cpp \ + $(public_api_src_dir)/shader-effects/nine-patch-mask-effect.cpp \ + $(public_api_src_dir)/shader-effects/overlay-effect.cpp \ + $(public_api_src_dir)/shader-effects/page-turn-effect.cpp \ + $(public_api_src_dir)/shader-effects/page-turn-book-spine-effect.cpp \ + $(public_api_src_dir)/shader-effects/ripple-effect.cpp \ + $(public_api_src_dir)/shader-effects/ripple2d-effect.cpp \ + $(public_api_src_dir)/shader-effects/shear-effect.cpp \ + $(public_api_src_dir)/shader-effects/soft-button-effect.cpp \ + $(public_api_src_dir)/shader-effects/spot-effect.cpp \ + $(public_api_src_dir)/shader-effects/square-dissolve-effect.cpp \ + $(public_api_src_dir)/shader-effects/swirl-effect.cpp \ + $(public_api_src_dir)/shader-effects/water-effect.cpp \ + $(public_api_src_dir)/shader-effects/bubble-effect/bubble-effect.cpp \ + $(public_api_src_dir)/shader-effects/bubble-effect/color-adjuster.cpp \ + $(public_api_src_dir)/builder/builder.cpp \ + $(public_api_src_dir)/builder/json-parser.cpp \ + $(public_api_src_dir)/builder/tree-node.cpp \ + $(public_api_src_dir)/transition-effects/cube-transition-effect.cpp \ + $(public_api_src_dir)/transition-effects/cube-transition-wave-effect.cpp \ + $(public_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \ + $(public_api_src_dir)/transition-effects/cube-transition-fold-effect.cpp + +# Add public header files here + +public_api_header_files = + +public_api_controls_header_files = + +public_api_alignment_header_files = + +public_api_bloom_view_header_files = \ + $(public_api_src_dir)/controls/bloom-view/bloom-view.h + +public_api_buttons_header_files = \ + $(public_api_src_dir)/controls/buttons/check-box-button.h + +public_api_cluster_header_files = \ + $(public_api_src_dir)/controls/cluster/cluster.h + +public_api_default_controls_header_files = \ + $(public_api_src_dir)/controls/default-controls/check-button-factory.h + +public_api_effects_view_header_files = \ + $(public_api_src_dir)/controls/effects-view/effects-view.h + +public_api_gaussian_blur_view_header_files = \ + $(public_api_src_dir)/controls/gaussian-blur-view/gaussian-blur-view.h + +public_api_image_view_header_files = \ + $(public_api_src_dir)/controls/image-view/image-view.h + +public_api_item_view_header_files = \ + $(public_api_src_dir)/controls/scrollable/item-view/depth-layout.h \ + $(public_api_src_dir)/controls/scrollable/item-view/roll-layout.h \ + $(public_api_src_dir)/controls/scrollable/item-view/spiral-layout.h \ + $(public_api_src_dir)/controls/scrollable/item-view/navigation-layout.h \ + $(public_api_src_dir)/controls/scrollable/item-view/album-layout.h + +public_api_magnifier_header_files = \ + $(public_api_src_dir)/controls/magnifier/magnifier.h + +public_api_page_turn_view_header_files = \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-view.h \ + $(public_api_src_dir)/controls/page-turn-view/page-factory.h \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-portrait-view.h \ + $(public_api_src_dir)/controls/page-turn-view/page-turn-landscape-view.h + +public_api_popup_header_files = + +public_api_scroll_component_header_files = + +public_api_scrollable_header_files = + +public_api_scroll_view_header_files = \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-constraints.h \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-carousel-effect.h \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-depth-effect.h \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-page-cube-effect.h \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-page-carousel-effect.h \ + $(public_api_src_dir)/controls/scrollable/scroll-view/scroll-view-wobble-effect.h + +public_api_slider_header_files = \ + $(public_api_src_dir)/controls/slider/slider.h + +public_api_selectors_header_files = \ + $(public_api_src_dir)/controls/selectors/rotating-selector.h + +public_api_shadow_view_header_files = \ + $(public_api_src_dir)/controls/shadow-view/shadow-view.h + +public_api_super_blur_view_header_files = + +public_api_table_view_header_files = \ + $(public_api_src_dir)/controls/table-view/table-view.h + +public_api_text_input_header_files = + +public_api_text_view_header_files = + +public_api_tool_bar_header_files = \ + $(public_api_src_dir)/controls/tool-bar/tool-bar.h + +public_api_view_header_files = \ + $(public_api_src_dir)/controls/view/view.h + +public_api_navigation_frame_header_files = \ + $(public_api_src_dir)/controls/navigation-frame/navigation-control.h \ + $(public_api_src_dir)/controls/navigation-frame/page.h \ + $(public_api_src_dir)/controls/navigation-frame/navigation-bar-style.h + +public_api_factory_header_files = + +public_api_focus_manager_header_files = \ + $(public_api_src_dir)/focus-manager/keyinput-focus-manager.h + +public_api_markup_processor_header_files = + +public_api_shader_effects_header_files = \ + $(public_api_src_dir)/shader-effects/alpha-discard-effect.h \ + $(public_api_src_dir)/shader-effects/bendy-effect.h \ + $(public_api_src_dir)/shader-effects/blind-effect.h \ + $(public_api_src_dir)/shader-effects/carousel-effect.h \ + $(public_api_src_dir)/shader-effects/displacement-effect.h \ + $(public_api_src_dir)/shader-effects/dissolve-local-effect.h \ + $(public_api_src_dir)/shader-effects/distance-field-effect.h \ + $(public_api_src_dir)/shader-effects/mirror-effect.h \ + $(public_api_src_dir)/shader-effects/motion-blur-effect.h \ + $(public_api_src_dir)/shader-effects/motion-stretch-effect.h \ + $(public_api_src_dir)/shader-effects/overlay-effect.h \ + $(public_api_src_dir)/shader-effects/shear-effect.h \ + $(public_api_src_dir)/shader-effects/soft-button-effect.h \ + $(public_api_src_dir)/shader-effects/spot-effect.h \ + $(public_api_src_dir)/shader-effects/square-dissolve-effect.h \ + $(public_api_src_dir)/shader-effects/water-effect.h + +public_api_bubble_effect_header_files = \ + $(public_api_src_dir)/shader-effects/bubble-effect/bubble-effect.h \ + $(public_api_src_dir)/shader-effects/bubble-effect/color-adjuster.h + +public_api_bubble_emitter_header_files = + +public_api_builder_header_files = \ + $(public_api_src_dir)/builder/builder.h \ + $(public_api_src_dir)/builder/json-parser.h \ + $(public_api_src_dir)/builder/tree-node.h + +public_api_transition_effects_header_files = \ + $(public_api_src_dir)/transition-effects/cube-transition-effect.h \ + $(public_api_src_dir)/transition-effects/cube-transition-wave-effect.h \ + $(public_api_src_dir)/transition-effects/cube-transition-cross-effect.h \ + $(public_api_src_dir)/transition-effects/cube-transition-fold-effect.h diff --git a/dali-toolkit/public-api/focus-manager/focus-manager.cpp b/dali-toolkit/public-api/focus-manager/focus-manager.cpp new file mode 100644 index 0000000..8364ae2 --- /dev/null +++ b/dali-toolkit/public-api/focus-manager/focus-manager.cpp @@ -0,0 +1,207 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const FocusManager::SIGNAL_FOCUS_CHANGED = "focus-changed"; +const char* const FocusManager::SIGNAL_FOCUS_OVERSHOT = "focus-overshot"; +const char* const FocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED = "focused-actor-activated"; +const std::string FocusManager::ACTOR_FOCUSABLE("focusable"); +const std::string FocusManager::IS_FOCUS_GROUP("is-focus-group"); + +FocusManager::FocusManager() +{ +} + +FocusManager::~FocusManager() +{ +} + +FocusManager FocusManager::Get() +{ + FocusManager manager; + + // Check whether the focus manager is already created + Dali::Adaptor& adaptor = Dali::Adaptor::Get(); + Dali::BaseHandle handle = adaptor.GetSingleton(typeid(FocusManager)); + if(handle) + { + // If so, downcast the handle of singleton to focus manager + manager = FocusManager(dynamic_cast(handle.GetObjectPtr())); + } + + if(!manager) + { + // If not, create the focus manager and register it as a singleton + manager = FocusManager(new Internal::FocusManager()); + adaptor.RegisterSingleton(typeid(manager), manager); + } + + return manager; +} + +FocusManager::FocusManager(Internal::FocusManager *impl) + : BaseHandle(impl) +{ +} + +void FocusManager::SetAccessibilityAttribute(Actor actor, AccessibilityAttribute type, const std::string& text) +{ + GetImpl(*this).SetAccessibilityAttribute(actor, type, text); +} + +std::string FocusManager::GetAccessibilityAttribute(Actor actor, AccessibilityAttribute type) const +{ + return GetImpl(*this).GetAccessibilityAttribute(actor, type); +} + +void FocusManager::SetFocusOrder(Actor actor, const unsigned int order) +{ + GetImpl(*this).SetFocusOrder(actor, order); +} + +unsigned int FocusManager::GetFocusOrder(Actor actor) const +{ + return GetImpl(*this).GetFocusOrder(actor); +} + +unsigned int FocusManager::GenerateNewFocusOrder() const +{ + return GetImpl(*this).GenerateNewFocusOrder(); +} + +Actor FocusManager::GetActorByFocusOrder(const unsigned int order) +{ + return GetImpl(*this).GetActorByFocusOrder(order); +} + +bool FocusManager::SetCurrentFocusActor(Actor actor) +{ + return GetImpl(*this).SetCurrentFocusActor(actor); +} + +Actor FocusManager::GetCurrentFocusActor() +{ + return GetImpl(*this).GetCurrentFocusActor(); +} + +Actor FocusManager::GetCurrentFocusGroup() +{ + return GetImpl(*this).GetCurrentFocusGroup(); +} + +unsigned int FocusManager::GetCurrentFocusOrder() +{ + return GetImpl(*this).GetCurrentFocusOrder(); +} + +bool FocusManager::MoveFocusForward() +{ + return GetImpl(*this).MoveFocusForward(); +} + +bool FocusManager::MoveFocusBackward() +{ + return GetImpl(*this).MoveFocusBackward(); +} + +void FocusManager::ClearFocus() +{ + GetImpl(*this).ClearFocus(); +} + +void FocusManager::Reset() +{ + GetImpl(*this).Reset(); +} + +void FocusManager::SetFocusGroup(Actor actor, bool isFocusGroup) +{ + GetImpl(*this).SetFocusGroup(actor, isFocusGroup); +} + +bool FocusManager::IsFocusGroup(Actor actor) const +{ + return GetImpl(*this).IsFocusGroup(actor); +} + +void FocusManager::SetGroupMode(bool enabled) +{ + GetImpl(*this).SetGroupMode(enabled); +} + +bool FocusManager::GetGroupMode() const +{ + return GetImpl(*this).GetGroupMode(); +} + +void FocusManager::SetWrapMode(bool wrapped) +{ + GetImpl(*this).SetWrapMode(wrapped); +} + +bool FocusManager::GetWrapMode() const +{ + return GetImpl(*this).GetWrapMode(); +} + +void FocusManager::SetFocusIndicatorActor(Actor indicator) +{ + GetImpl(*this).SetFocusIndicatorActor(indicator); +} + +Actor FocusManager::GetFocusIndicatorActor() +{ + return GetImpl(*this).GetFocusIndicatorActor(); +} + +Actor FocusManager::GetFocusGroup(Actor actor) +{ + return GetImpl(*this).GetFocusGroup(actor); +} + +FocusManager::FocusChangedSignalV2& FocusManager::FocusChangedSignal() +{ + return GetImpl(*this).FocusChangedSignal(); +} + +FocusManager::FocusOvershotSignalV2& FocusManager::FocusOvershotSignal() +{ + return GetImpl(*this).FocusOvershotSignal(); +} + +FocusManager::FocusedActorActivatedSignalV2& FocusManager::FocusedActorActivatedSignal() +{ + return GetImpl(*this).FocusedActorActivatedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.cpp b/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.cpp new file mode 100644 index 0000000..e48d1b6 --- /dev/null +++ b/dali-toolkit/public-api/focus-manager/keyboard-focus-manager.cpp @@ -0,0 +1,135 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const KeyboardFocusManager::SIGNAL_PRE_FOCUS_CHANGE = "keyboard-pre-focus-change"; +const char* const KeyboardFocusManager::SIGNAL_FOCUS_CHANGED = "keyboard-focus-changed"; +const char* const KeyboardFocusManager::SIGNAL_FOCUS_GROUP_CHANGED = "keyboard-focus-group-changed"; +const char* const KeyboardFocusManager::SIGNAL_FOCUSED_ACTOR_ACTIVATED = "keyboard-focused-actor-activated"; + +KeyboardFocusManager::KeyboardFocusManager() +{ +} + +KeyboardFocusManager::~KeyboardFocusManager() +{ +} + +KeyboardFocusManager KeyboardFocusManager::Get() +{ + return Internal::KeyboardFocusManager::Get(); +} + +KeyboardFocusManager::KeyboardFocusManager(Internal::KeyboardFocusManager *impl) + : BaseHandle(impl) +{ +} + +bool KeyboardFocusManager::SetCurrentFocusActor(Actor actor) +{ + return GetImpl(*this).SetCurrentFocusActor(actor); +} + +Actor KeyboardFocusManager::GetCurrentFocusActor() +{ + return GetImpl(*this).GetCurrentFocusActor(); +} + +bool KeyboardFocusManager::MoveFocus(Control::KeyboardFocusNavigationDirection direction) +{ + return GetImpl(*this).MoveFocus(direction); +} + +void KeyboardFocusManager::ClearFocus() +{ + GetImpl(*this).ClearFocus(); +} + +void KeyboardFocusManager::SetAsFocusGroup(Actor actor, bool isFocusGroup) +{ + // deprecated method. + GetImpl(*this).SetAsFocusGroup(actor, isFocusGroup); +} + +bool KeyboardFocusManager::IsFocusGroup(Actor actor) const +{ + // deprecated method. + return GetImpl(*this).IsFocusGroup(actor); +} + +Actor KeyboardFocusManager::GetFocusGroup(Actor actor) +{ + return GetImpl(*this).GetFocusGroup(actor); +} + +void KeyboardFocusManager::SetFocusGroupLoop(bool enabled) +{ + GetImpl(*this).SetFocusGroupLoop(enabled); +} + +bool KeyboardFocusManager::GetFocusGroupLoop() const +{ + return GetImpl(*this).GetFocusGroupLoop(); +} + +void KeyboardFocusManager::SetFocusIndicatorActor(Actor indicator) +{ + GetImpl(*this).SetFocusIndicatorActor(indicator); +} + +Actor KeyboardFocusManager::GetFocusIndicatorActor() +{ + return GetImpl(*this).GetFocusIndicatorActor(); +} + +KeyboardFocusManager::PreFocusChangeSignalV2& KeyboardFocusManager::PreFocusChangeSignal() +{ + return GetImpl(*this).PreFocusChangeSignal(); +} + +KeyboardFocusManager::FocusChangedSignalV2& KeyboardFocusManager::FocusChangedSignal() +{ + return GetImpl(*this).FocusChangedSignal(); +} + +KeyboardFocusManager::FocusGroupChangedSignalV2& KeyboardFocusManager::FocusGroupChangedSignal() +{ + return GetImpl(*this).FocusGroupChangedSignal(); +} + +KeyboardFocusManager::FocusedActorActivatedSignalV2& KeyboardFocusManager::FocusedActorActivatedSignal() +{ + return GetImpl(*this).FocusedActorActivatedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.cpp b/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.cpp new file mode 100644 index 0000000..470977a --- /dev/null +++ b/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.cpp @@ -0,0 +1,105 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "keyinput-focus-manager.h" + +// EXTERNAL INCLUDES + +// INTERNAL INCLUDES + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const KeyInputFocusManager::SIGNAL_KEY_INPUT_FOCUS_CHANGED = "key-input-focus-changed"; +const char* const KeyInputFocusManager::SIGNAL_UNHANDLED_KEY_EVENT = "unhandled-key-event"; + +KeyInputFocusManager::KeyInputFocusManager() +{ +} + +KeyInputFocusManager::~KeyInputFocusManager() +{ +} + +KeyInputFocusManager KeyInputFocusManager::Get() +{ + KeyInputFocusManager manager; + + // Check whether the focus manager is already created + Dali::Adaptor& adaptor = Dali::Adaptor::Get(); + Dali::BaseHandle handle = adaptor.GetSingleton(typeid(KeyInputFocusManager)); + if(handle) + { + // If so, downcast the handle of singleton to focus manager + manager = KeyInputFocusManager(dynamic_cast(handle.GetObjectPtr())); + } + + if(!manager) + { + // If not, create the focus manager and register it as a singleton + manager = KeyInputFocusManager(new Internal::KeyInputFocusManager()); + adaptor.RegisterSingleton(typeid(manager), manager); + } + + return manager; +} + +KeyInputFocusManager::KeyInputFocusManager(Internal::KeyInputFocusManager *impl) + : BaseHandle(impl) +{ +} + +void KeyInputFocusManager::SetFocus(Control control) +{ + GetImpl(*this).SetFocus(control); +} + +Control KeyInputFocusManager::GetCurrentFocusControl() const +{ + return GetImpl(*this).GetCurrentFocusControl(); +} + +void KeyInputFocusManager::RemoveFocus(Control control) +{ + GetImpl(*this).RemoveFocus(control); +} + +bool KeyInputFocusManager::IsKeyboardListener(Control control) +{ + return GetImpl(*this).IsKeyboardListener(control); +} + +KeyInputFocusManager::KeyInputFocusChangedSignalV2& KeyInputFocusManager::KeyInputFocusChangedSignal() +{ + return GetImpl(*this).KeyInputFocusChangedSignal(); +} + +KeyInputFocusManager::UnhandledKeyEventSignalV2& KeyInputFocusManager::UnhandledKeyEventSignal() +{ + return GetImpl(*this).UnhandledKeyEventSignal(); +} + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h b/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h new file mode 100644 index 0000000..da67b2f --- /dev/null +++ b/dali-toolkit/public-api/focus-manager/keyinput-focus-manager.h @@ -0,0 +1,143 @@ +#ifndef __DALI_TOOLKIT_KEYINPUT_FOCUS_MANAGER_H__ +#define __DALI_TOOLKIT_KEYINPUT_FOCUS_MANAGER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class KeyInputFocusManager; +} + +/** + * KeyInputFocusManager + * This class provides the functionality of registering for keyboard events for controls. + * The keyinput focus manager maintains a stack of controls, With the last added control receiving + * all the keyboard events first. And if the conrol doesn't consume the event it is passed to + * the next control in the stack. If none of the controls in the stack consume the key event then + * UnhandledKeyEventSignal() is emitted. + */ + + class KeyInputFocusManager : public BaseHandle + { + public: + //Signal Names + static const char* const SIGNAL_KEY_INPUT_FOCUS_CHANGED; + static const char* const SIGNAL_UNHANDLED_KEY_EVENT; + + // KeyInputFocusChanged + typedef SignalV2< void (Control, Control) > KeyInputFocusChangedSignalV2; + + // Unhandled Key Event + typedef SignalV2< void (const KeyEvent&) > UnhandledKeyEventSignalV2; + + public: + + /** + * Create a KeyInputFocusManager handle; this can be initialised with KeyInputFocusManager::Get() + * Calling member functions with an uninitialised handle is not allowed. + */ + KeyInputFocusManager(); + + /** + * Virtual destructor. + */ + virtual ~KeyInputFocusManager(); + + /** + * Get the singleton of KeyInputFocusManager object. + * @return A handle to the KeyInputFocusManager control. + */ + static KeyInputFocusManager Get(); + + /** + * Sets keyboard focus for a control. + * Note: A control can be set to be in focus and still not receive all the key events if another control has over ridden it. + * As the key input focus mechanism works like a stack, the top most control receives all the key events, and passes on the + * unhandled events to the controls below in the stack. A control in the stack will regain key input focus when there are no more + * controls above it in the focus stack. + * + * @pre The Control is not in the focus stack. If it is allready present in the top of the stack it results in a no-op, If it is + * present in the stack but not on the top of the stack, then the control is moved to the top of the focus stack. + * @param[in] control The Control to receive keyboard input + */ + void SetFocus(Control control); + + /** + * Query for the control that is currently set to be on top of the fcous stack and receives all + * keyboard input events first. + * @return Pointer to the control set to receive keyboard inputs. + */ + Control GetCurrentFocusControl() const; + + /** + * Removes focus for the given control, The control will no longer receive events from keyboard. + * @param [in] control which should be removed from focus. + */ + void RemoveFocus(Control control); + + /** + * Queries whether a control is currently part of the focus stack. + * @param [in] control which should be queried. + * @return True if it is part of the foucus stack False otherwise. + */ + bool IsKeyboardListener(Control control); + + public: // Signals + + /** + * This signal is emitted when the key input focus control changes. + * Two control parameters are sent as part of this signal, the first being the signal that now has the focus, the second + * being the one that has lost focus. + * A callback of the following type may be connected: + * @code + * void YourCallback(Control focusGainedControl, Control focusLostActor); + * @endcode + * @return The signal to connect to. + */ + KeyInputFocusChangedSignalV2& KeyInputFocusChangedSignal(); + + /** + * This signal is emitted when a key event was received, and none of the focused controls on the stage have consumed it. + * A callback of the following type may be connected: + * @code + * void YourCallbackName(const KeyEvent& event); + * @endcode + * @return The signal to connect to. + */ + UnhandledKeyEventSignalV2& UnhandledKeyEventSignal(); + +private: + + KeyInputFocusManager(Internal::KeyInputFocusManager *impl); + +}; // class KeyInputFocusManager + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_KEYINPUT_FOCUS_MANAGER_H__ diff --git a/dali-toolkit/public-api/markup-processor/markup-processor.cpp b/dali-toolkit/public-api/markup-processor/markup-processor.cpp new file mode 100644 index 0000000..41a070e --- /dev/null +++ b/dali-toolkit/public-api/markup-processor/markup-processor.cpp @@ -0,0 +1,1107 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// HEADER INCLUDE +#include + +// INTERNAL INCLUDES +#include + + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace MarkupProcessor +{ + +namespace +{ +const std::string WEB_COLOR_TOKEN( "#" ); +const std::string HEX_COLOR_TOKEN( "0x" ); +const std::string ALPHA_ONE( "FF" ); + +const std::string BLACK_COLOR( "black" ); +const std::string WHITE_COLOR( "white" ); +const std::string RED_COLOR( "red" ); +const std::string GREEN_COLOR( "green" ); +const std::string BLUE_COLOR( "blue" ); +const std::string YELLOW_COLOR( "yellow" ); +const std::string MAGENTA_COLOR( "magenta" ); +const std::string CYAN_COLOR( "cyan" ); +const std::string TRANSPARENT_COLOR( "transparent" ); + +const std::string XHTML_B_TAG("b"); +const std::string XHTML_I_TAG("i"); +const std::string XHTML_U_TAG("u"); +const std::string XHTML_BR_TAG("br"); +const std::string XHTML_FONT_TAG("font"); +const std::string XHTML_SHADOW_TAG("shadow"); +const std::string XHTML_GLOW_TAG("glow"); +const std::string XHTML_OUTLINE_TAG("outline"); +const std::string XHTML_SMOOTH_EDGE_TAG("smooth"); +const std::string XHTML_SIZE_PROPERTY("size"); +const std::string XHTML_COLOR_PROPERTY("color"); +const std::string XHTML_FACE_PROPERTY("face"); +const std::string XHTML_STYLE_PROPERTY("style"); +const std::string XHTML_PARAM_PROPERTY("param"); +const std::string XHTML_PARAM_X_PROPERTY("paramx"); +const std::string XHTML_PARAM_Y_PROPERTY("paramy"); + +const char LESS_THAN( '<' ); +const char GREATER_THAN( '>' ); +const char EQUAL( '=' ); +const char QUOTATION_MARK( '\''); +const char LINE_SEPARATOR_CR( 0x0D ); // Carriage return character CR +const char LINE_SEPARATOR_LF( 0x0A ); // New line character LF +const char SLASH( '/' ); +const char BACK_SLASH( '\\' ); + +const std::string LINE_SEPARATOR_LF_STRING(1 , LINE_SEPARATOR_LF); ///< a string with 1 line separator character +/** + * Stores a property pair: name, value. + */ +struct Property +{ + Property( const std::string& n, const std::string& v ) + : name( n ), + value( v ) + {} + + std::string name; + std::string value; +}; + + +/** + * Compare if two strings are equal. + * + * The comparison is case insensitive. + * + * @param[in] string1 First of the two strings to be compared. + * @param[in] string2 Second of the strings to be compared. + * + * @return \e true if both strings are equal (case insensitive). + */ +bool CaseInsensitiveComparison( const std::string& string1, const std::string& string2 ) +{ + const std::size_t stringSize = string1.size(); + if( stringSize != string2.size() ) + { + // Early return. Strings have different sizes. + return false; + } + + bool equal = true; + for( std::size_t index = 0; equal && ( index < stringSize ); ++index ) + { + if( std::toupper( *( string1.begin() + index ) ) != std::toupper( *( string2.begin() + index ) ) ) + { + equal = false; + } + } + + return equal; +} + +/** + * Converts a string into a float value. + * @param[in] floatStr A float packed inside a string. + * @return The float value. + */ +float StringToFloat( const std::string& floatStr ) +{ + float ret = 0.f; + + std::istringstream( floatStr ) >> ret; + + return ret; +} + +/** + * Converts a float into a string. + * @param[in] value. A float. + * @return The float packed into a string. + */ +std::string FloatToString( float value ) +{ + std::ostringstream stream; + stream << value; + + return stream.str(); +} + +/** + * Converts a string into an hexadecimal unsigned int. + * @param[in] uintStr An hexadecimal unsigned int packed inside a string. + * @return The hexadecimal value. + */ +unsigned int StringToHex( const std::string& uintStr ) +{ + unsigned int ret = 0; + + std::string str( uintStr ); + if( uintStr.size() <= 8 ) + { + str.insert( 2, std::string( "ff" ) ); + } + + std::istringstream( str ) >> std::hex >> ret; + + return ret; +} + +/** + * Converts an hexadecimal value into a string. + * @param[in] value. An hexadecimal value. + * @return The hexadecimal value packed inside a string. + */ +std::string HexToString( unsigned int value ) +{ + std::ostringstream stream; + stream << std::hex << value; + return stream.str(); +} + +/** + * Converts an ARGB color packed in 4 byte unsigned int into a Vector4 color used in Dali. + * @param[in] color An ARGB color packed in an unsigned int. + * @param[out] retColor A Vector4 with the converted color. + */ +void UintColorToVector4( unsigned int color, Vector4& retColor ) +{ + retColor.a = static_cast( ( color & 0xFF000000 ) >> 24 ) / 255.f; + retColor.r = static_cast( ( color & 0x00FF0000 ) >> 16 ) / 255.f; + retColor.g = static_cast( ( color & 0x0000FF00 ) >> 8 ) / 255.f; + retColor.b = static_cast( color & 0x000000FF ) / 255.f; +} + +/** + * Converts an ARGB Vector4 color into a 4 byte packed inside an unsigned int. + * @param[in] color A Vector4 with an ARGB color. + * @return An unsigned int with the converted color. + */ +unsigned int Vector4ColorToUint( const Vector4& color ) +{ + unsigned int retColor = 0; + + retColor = static_cast( color.a * 255.f ) << 24; + retColor += static_cast( color.r * 255.f ) << 16; + retColor += static_cast( color.g * 255.f ) << 8; + retColor += static_cast( color.b * 255.f ); + + return retColor; +} + +/** + * Converts a color packed inside a string into an ARGB Vector4 color. + * The string color could be in hexadecimal ( 0xFF0000FF ), webcolor ( #0000FF or #00F ) or some constant values: + * black, white, red, green, blue, yellow, magenta, cyan, transparent. + * @param[in] colorStr A color packed inside a string.. + * @param[out] retColor A color packed inside a Vector4. + */ +void ColorStringToVector4( const std::string& colorStr, Vector4& retColor ) +{ + std::string subStr1 = colorStr.substr( 0, 1 ); + std::string subStr2 = colorStr.substr( 0, 2 ); + + if( WEB_COLOR_TOKEN == subStr1 ) + { + std::string webColor( colorStr.begin() + 1, colorStr.end() ); + if( 3 == webColor.size() ) // 3 component web color #F00 (red) + { + webColor.insert( 2, &( webColor[2] ), 1 ); + webColor.insert( 1, &( webColor[1] ), 1 ); + webColor.insert( 0, &( webColor[0] ), 1 ); + webColor.insert( 0, ALPHA_ONE ); + } + else if( 6 == webColor.size() ) // 6 component web color #FF0000 (red) + { + webColor.insert( 0, ALPHA_ONE ); + } + webColor.insert( 0, HEX_COLOR_TOKEN ); + + UintColorToVector4( StringToHex( webColor ), retColor ); + } + else if( CaseInsensitiveComparison( HEX_COLOR_TOKEN, subStr2 ) ) + { + UintColorToVector4( StringToHex( colorStr ), retColor ); + } + else if( CaseInsensitiveComparison( BLACK_COLOR, colorStr ) ) + { + retColor = Color::BLACK; + } + else if( CaseInsensitiveComparison( WHITE_COLOR, colorStr ) ) + { + retColor = Color::WHITE; + } + else if( CaseInsensitiveComparison( RED_COLOR, colorStr ) ) + { + retColor = Color::RED; + } + else if( CaseInsensitiveComparison( GREEN_COLOR, colorStr ) ) + { + retColor = Color::GREEN; + } + else if( CaseInsensitiveComparison( BLUE_COLOR, colorStr ) ) + { + retColor = Color::BLUE; + } + else if( CaseInsensitiveComparison( YELLOW_COLOR, colorStr ) ) + { + retColor = Color::YELLOW; + } + else if( CaseInsensitiveComparison( MAGENTA_COLOR, colorStr ) ) + { + retColor = Color::MAGENTA; + } + else if( CaseInsensitiveComparison( CYAN_COLOR, colorStr ) ) + { + retColor = Color::CYAN; + } + else if( CaseInsensitiveComparison( TRANSPARENT_COLOR, colorStr ) ) + { + retColor = Color::TRANSPARENT; + } +} + +/** + * Converts a color packed inside a Vector4 into a string. + * The string color could be in hexadecimal ( 0xFF0000FF ), webcolor ( #0000FF or #00F ) or some constant values: + * black, white, red, green, blue, yellow, magenta, cyan, transparent. + * @param[in] color A color packed inside a Vector4 + * @return The color packed inside a string. + */ +std::string Vector4ToColorString( const Vector4& color ) +{ + std::string colorStr; + + if( Color::BLACK == color ) + { + colorStr = BLACK_COLOR; + } + else if( Color::WHITE == color ) + { + colorStr = WHITE_COLOR; + } + else if( Color::RED == color ) + { + colorStr = RED_COLOR; + } + else if( Color::GREEN == color ) + { + colorStr = GREEN_COLOR; + } + else if( Color::BLUE == color ) + { + colorStr = BLUE_COLOR; + } + else if( Color::YELLOW == color ) + { + colorStr = YELLOW_COLOR; + } + else if( Color::MAGENTA == color ) + { + colorStr = MAGENTA_COLOR; + } + else if( Color::CYAN == color ) + { + colorStr = CYAN_COLOR; + } + else if( Color::TRANSPARENT == color ) + { + colorStr = TRANSPARENT_COLOR; + } + else + { + colorStr = HEX_COLOR_TOKEN + HexToString( Vector4ColorToUint( color ) ); + } + + return colorStr; +} + +/** + * Skips any unnecessary white space. + * @param[in,out] it Iterator pointing to the current character of the markup string which is being parsed. + * @param[in] endIt Iterator pointing to the end of the markup string. + */ +void SkipWhiteSpace( std::string::const_iterator& it, const std::string::const_iterator& endIt ) +{ + bool found = false; + for( ; ( !found ) && ( it != endIt ); ++it ) + { + if( !isspace( *it ) ) + { + found = true; + --it; + } + } +} + +/** + * Adds a line separator 'LF' character to the given styled text array. + * @param[in] style The current style. + * @param[in,out] styledTextArray The given styled text array. + */ +void AddNewLineChar( const TextStyle& style, StyledTextArray& styledTextArray ) +{ + const Text text( LINE_SEPARATOR_LF_STRING ); + styledTextArray.push_back( StyledText( text, style ) ); +} + +/** + * Adds text to the given styled text array. + * It splits the text in characters. + * @param[in] textToBeStored The text to be stored. + * @param[in] styleToBeStored The current style. + * @param[in,out] styledTextArray The given styled text array. + */ +void AddText( const std::string& textToBeStored, const TextStyle& styleToBeStored, StyledTextArray& styledTextArray ) +{ + const Text text( textToBeStored ); + for( size_t i = 0, length = text.GetLength(); i < length; ++i ) + { + styledTextArray.push_back( StyledText( Text( text[i] ), styleToBeStored ) ); + } +} + +/** + * Splits the tag string into the tag name and its properties. + * @param[in] tag The tag string with its pairs property, value. + * @param[out] tagName The name of the tag. + * @param[out] properties Vector of properties. + */ +void ParseProperties( const std::string& tag, std::string& tagName, std::vector& properties ) +{ + // Find first the tag name. + bool found = false; + bool isQuotationOpen = false; + std::string::const_iterator it, endIt; + for( it = tag.begin(), endIt = tag.end(); ( !found ) && ( it != endIt ); ++it ) + { + const char character( *it ); + if( !isspace( character ) ) + { + tagName += character; + } + else + { + found = true; + } + } + SkipWhiteSpace( it, endIt ); + + // Find the properties. + std::string name; + std::string value; + bool addToNameValue = true; + for( ; it != endIt; ++it ) + { + const char character( *it ); + if( isspace( character ) && !isQuotationOpen ) + { + if( !name.empty() && !value.empty() ) + { + // Every time a white space is found, a new property is created and stored in the properties vector. + properties.push_back( Property( name, value ) ); + name .clear(); + value.clear(); + addToNameValue = true; // next read characters will be added to the name. + } + } + else if( EQUAL == character ) // '=' + { + addToNameValue = false; // next read characters will be added to the value. + SkipWhiteSpace( it, endIt ); + } + else if( QUOTATION_MARK == character ) // '\'' + { + // Do not add quotation marks to neither name nor value. + isQuotationOpen = !isQuotationOpen; + } + else + { + // Adds characters to the name or the value. + if( addToNameValue ) + { + name += character; + } + else + { + value += character; + } + } + } + if( !name.empty() && !value.empty() ) + { + // Checks if the last property needs to be added. + properties.push_back( Property( name, value ) ); + } +} + +/** + * It parses a tag and its properties if the given iterator \e it is pointing at a tag beginning. + * @param[in,out] it Iterator pointing to the current character of the markup string which is being parsed. + * @param[in] endIt Iterator pointing to the end of the markup string. + * @param[out] tag Name of the tag. + * @param[out] isEndTag Whether the tag is and end tag i.e or not. + * @param[out] properties The vector with tag properties. + * @return \e true if the iterator \e it is pointing a markup tag. Otherwise \e false. + */ +bool IsTag( std::string::const_iterator& it, const std::string::const_iterator& endIt, std::string& tag, bool& isEndTag, std::vector& properties ) +{ + bool isTag = false; + bool isQuotationOpen = false; + bool propertiesFound = false; + std::string tagString; + + const char character( *it ); + if( LESS_THAN == character ) // '<' + { + // if the iterator is pointing to a '<' character, then check if it's a markup tag is needed. + ++it; + if( it != endIt ) + { + SkipWhiteSpace( it, endIt ); + + for( ; ( !isTag ) && ( it != endIt ); ++it ) + { + const char character( *it ); + + if( SLASH == character ) // '/' + { + // if the tag has a '/' then it's an end or empty tag. + isEndTag = true; + + if( ( it + 1 != endIt ) && ( isspace( *( it + 1 ) ) ) && ( !isQuotationOpen ) ) + { + ++it; + SkipWhiteSpace( it, endIt ); + --it; + } + } + else if( GREATER_THAN == character ) // '>' + { + isTag = true; + } + else if(QUOTATION_MARK == character) + { + isQuotationOpen = !isQuotationOpen; + tagString += character; + } + else if( isspace( character ) ) // ' ' + { + // If the tag contains white spaces then it may have properties. + if ( !isQuotationOpen ) + { + propertiesFound = true; + } + tagString += character; + } + else + { + // If it's not any of the 'special' characters then just add it to the tag string. + tagString += character; + } + } + --it; + } + } + + // If the tag string has white spaces, then parse the properties is needed. + if( propertiesFound ) + { + ParseProperties( tagString, tag, properties ); + } + else + { + tag = tagString; + } + + return isTag; +} + +} // namespace + +void GetStyledTextArray( const std::string& markupString, StyledTextArray& styledTextArray ) +{ + styledTextArray.clear(); + + TextStyle defaultStyle; + std::stack styleStack; + + styleStack.push( defaultStyle ); + TextStyle currentStyle = styleStack.top(); + std::string textToBeStored; + TextStyle styleToBeStored( currentStyle ); + for( std::string::const_iterator it = markupString.begin(), endIt = markupString.end(); it != endIt; ++it ) + { + std::string tag; + bool isEndTag = false; + std::vector tagProperties; + if( IsTag( it, endIt, tag, isEndTag, tagProperties ) ) + { + if( CaseInsensitiveComparison( XHTML_I_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + newStyle.SetItalics( true ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_U_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + newStyle.SetUnderline( true ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_B_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + newStyle.SetWeight( TextStyle::BOLD ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_BR_TAG, tag ) ) + { + if( isEndTag ) + { + AddText( textToBeStored, styleToBeStored, styledTextArray ); + AddNewLineChar( currentStyle, styledTextArray ); + textToBeStored.clear(); + } + } //
+ else if( CaseInsensitiveComparison( XHTML_FONT_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + for( std::vector::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it ) + { + const Property& property( *it ); + if( CaseInsensitiveComparison( XHTML_FACE_PROPERTY, property.name ) ) + { + newStyle.SetFontName( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_STYLE_PROPERTY, property.name ) ) + { + newStyle.SetFontStyle( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) ) + { + Vector4 color; + ColorStringToVector4( property.value, color ); + newStyle.SetTextColor( color ); + } + else if( CaseInsensitiveComparison( XHTML_SIZE_PROPERTY, property.name ) ) + { + newStyle.SetFontPointSize( PointSize( StringToFloat( property.value ) ) ); + } + } + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_SHADOW_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + Vector4 color( TextStyle::DEFAULT_SHADOW_COLOR ); + Vector2 offset( TextStyle::DEFAULT_SHADOW_OFFSET ); + for( std::vector::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it ) + { + const Property& property( *it ); + if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) ) + { + offset.x = StringToFloat( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) ) + { + offset.y = StringToFloat( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) ) + { + ColorStringToVector4( property.value, color ); + } + } + newStyle.SetShadow( true, color, offset ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_GLOW_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + Vector4 color( TextStyle::DEFAULT_GLOW_COLOR ); + float intensity = TextStyle::DEFAULT_GLOW_INTENSITY; + for( std::vector::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it ) + { + const Property& property( *it ); + if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) ) + { + intensity = StringToFloat( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) ) + { + ColorStringToVector4( property.value, color ); + } + } + newStyle.SetGlow( true, color, intensity ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_OUTLINE_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + Vector4 color( TextStyle::DEFAULT_OUTLINE_COLOR ); + Vector2 thickness( TextStyle::DEFAULT_OUTLINE_THICKNESS ); + for( std::vector::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it ) + { + const Property& property( *it ); + if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) ) + { + thickness.x = StringToFloat( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) ) + { + thickness.y = StringToFloat( property.value ); + } + else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) ) + { + ColorStringToVector4( property.value, color ); + } + } + newStyle.SetOutline( true, color, thickness ); + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + else if( CaseInsensitiveComparison( XHTML_SMOOTH_EDGE_TAG, tag ) ) + { + if( !isEndTag ) + { + TextStyle newStyle( currentStyle ); + for( std::vector::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it ) + { + const Property& property( *it ); + if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) ) + { + newStyle.SetSmoothEdge( StringToFloat( property.value ) ); + } + } + styleStack.push( newStyle ); + currentStyle = styleStack.top(); + } + else + { + styleStack.pop(); + currentStyle = styleStack.top(); + } + } // + } // end if( IsTag() ) + else + { + char character( *it ); + + // Adding < or > special character. + if( ( BACK_SLASH == character ) && ( it + 1 != endIt ) ) + { + const char nextChar( *( it + 1 ) ); + if( ( LESS_THAN == nextChar ) || ( GREATER_THAN == nextChar ) ) + { + character = nextChar; + ++it; + } + } + else if( ( LINE_SEPARATOR_CR == character ) && ( it + 1 != endIt ) ) + { + if( LINE_SEPARATOR_LF == *( it + 1 ) ) + { + character = LINE_SEPARATOR_LF; + ++it; + } + } + + if( styleToBeStored != currentStyle ) + { + if( !textToBeStored.empty() ) + { + AddText( textToBeStored, styleToBeStored, styledTextArray ); + textToBeStored.clear(); + } + styleToBeStored = currentStyle; + } + textToBeStored.insert( textToBeStored.end(), character ); + } + } + if( !textToBeStored.empty() ) + { + AddText( textToBeStored, styleToBeStored, styledTextArray ); + textToBeStored.clear(); + } +} + +void GetPlainString( const StyledTextArray& styledTextArray, std::string& plainString ) +{ + // First step is put all simultaneous characters with same style together. + for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it ) + { + const StyledText& styledText( *it ); + plainString += styledText.mText.GetText(); + } +} + +void GetMarkupString( const StyledTextArray& styledTextArray, std::string& markupString ) +{ + const std::string WHITE_SPACE( " " ); + + TextStyle previousStyle; + StyledText newStyledText; + StyledTextArray compressedStyledTextArray; + + markupString.clear(); + + // First step is put all simultaneous characters with same style together. + for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it ) + { + const StyledText& styledText( *it ); + + if( previousStyle != styledText.mStyle ) + { + if( !newStyledText.mText.IsEmpty() ) + { + compressedStyledTextArray.push_back( newStyledText ); + } + newStyledText = StyledText(); + newStyledText.mStyle = styledText.mStyle; + } + + if( !styledText.mText.IsEmpty() ) + { + const char character = styledText.mText.GetText()[0]; + if( ( character == LESS_THAN ) || ( character == GREATER_THAN ) ) + { + newStyledText.mText.Append( Text( std::string( &BACK_SLASH, 1 ) ) ); + } + } + + newStyledText.mText.Append( styledText.mText ); + + previousStyle = newStyledText.mStyle; + } + + //Add the last characters. + if( !newStyledText.mText.IsEmpty() ) + { + compressedStyledTextArray.push_back( newStyledText ); + } + + // Write markup string. + const std::string lineSeparatorStr( &LINE_SEPARATOR_LF ); + const Text lineSeparator( lineSeparatorStr ); + + const TextStyle defaultStyle; + for( StyledTextArray::const_iterator it = compressedStyledTextArray.begin(), endIt = compressedStyledTextArray.end(); it != endIt; ++it ) + { + const StyledText& styledText( *it ); + + bool isItalics = styledText.mStyle.GetItalics(); + bool isBold = defaultStyle.GetWeight() != styledText.mStyle.GetWeight(); + bool isUnderline = styledText.mStyle.GetUnderline(); + bool hasFontFace = defaultStyle.GetFontName() != styledText.mStyle.GetFontName(); + bool hasFontStyle = defaultStyle.GetFontStyle() != styledText.mStyle.GetFontStyle(); + bool hasFontSize = fabsf( defaultStyle.GetFontPointSize() - styledText.mStyle.GetFontPointSize() ) > GetRangedEpsilon( defaultStyle.GetFontPointSize(), styledText.mStyle.GetFontPointSize() ); + bool hasFontColor = defaultStyle.GetTextColor() != styledText.mStyle.GetTextColor(); + + bool hasSmooth = fabsf( defaultStyle.GetSmoothEdge() - styledText.mStyle.GetSmoothEdge() ) > GetRangedEpsilon( defaultStyle.GetSmoothEdge(), styledText.mStyle.GetSmoothEdge() ); + bool hasShadowColor = defaultStyle.GetShadowColor() != styledText.mStyle.GetShadowColor(); + bool hasShadowParams = defaultStyle.GetShadowOffset() != styledText.mStyle.GetShadowOffset(); + bool hasGlowColor = defaultStyle.GetGlowColor() != styledText.mStyle.GetGlowColor(); + bool hasGlowParams = fabsf( defaultStyle.GetGlowIntensity() - styledText.mStyle.GetGlowIntensity() ) > GetRangedEpsilon( defaultStyle.GetGlowIntensity(), styledText.mStyle.GetGlowIntensity() ); + bool hasOutlineColor = defaultStyle.GetOutlineColor() != styledText.mStyle.GetOutlineColor(); + bool hasOutlineParams = defaultStyle.GetOutlineThickness() != styledText.mStyle.GetOutlineThickness(); + + // Write font info. + if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor ) + { + markupString += LESS_THAN + XHTML_FONT_TAG; + + if( hasFontFace ) + { + markupString += WHITE_SPACE + XHTML_FACE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontName() + QUOTATION_MARK; // face='' + } + + if( hasFontStyle ) + { + markupString += WHITE_SPACE + XHTML_STYLE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontStyle() + QUOTATION_MARK; // style='' + } + + if( hasFontSize ) + { + markupString += WHITE_SPACE + XHTML_SIZE_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetFontPointSize() ) + QUOTATION_MARK; // size='' + } + + if( hasFontColor ) + { + markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetTextColor() ) + QUOTATION_MARK; // color='' + } + + markupString += GREATER_THAN; + } // + + // Write italics. + if( isItalics ) + { + markupString += LESS_THAN + XHTML_I_TAG + GREATER_THAN; + } // + + // Write bold. + if( isBold ) + { + markupString += LESS_THAN + XHTML_B_TAG + GREATER_THAN; + } // + + // Write underline. + if( isUnderline ) + { + markupString += LESS_THAN + XHTML_U_TAG + GREATER_THAN; + } // + + // Write smooth. + if( hasSmooth ) + { + markupString += LESS_THAN + XHTML_SMOOTH_EDGE_TAG + WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetSmoothEdge() ) + QUOTATION_MARK + GREATER_THAN; + } + + // Write shadow. + if( styledText.mStyle.GetShadow() ) + { + markupString += LESS_THAN + XHTML_SHADOW_TAG; + + if( hasShadowColor ) + { + markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetShadowColor() ) + QUOTATION_MARK; + } + + if( hasShadowParams ) + { + markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().x ) + QUOTATION_MARK; + markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().y ) + QUOTATION_MARK; + } + + markupString += GREATER_THAN; + } + + // Write glow. + if( styledText.mStyle.GetGlow() ) + { + markupString += LESS_THAN + XHTML_GLOW_TAG; + + if( hasGlowColor ) + { + markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetGlowColor() ) + QUOTATION_MARK; // color='' + } + + if( hasGlowParams ) + { + markupString += WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetGlowIntensity() ) + QUOTATION_MARK; // param='' + } + + markupString += GREATER_THAN; + } // + + // Write outline. + if( styledText.mStyle.GetOutline() ) + { + markupString += LESS_THAN + XHTML_OUTLINE_TAG; + + if( hasOutlineColor ) + { + markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetOutlineColor() ) + QUOTATION_MARK; // color = '' + } + + if( hasOutlineParams ) + { + markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().x ) + QUOTATION_MARK; // paramx='' + markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().y ) + QUOTATION_MARK; // paramy='' + } + + markupString += GREATER_THAN; + } // + + // Write text. + if( styledText.mText[0] == lineSeparator[0] ) + { + markupString += LESS_THAN + XHTML_BR_TAG + WHITE_SPACE + SLASH + GREATER_THAN; //
+ } + else + { + markupString += styledText.mText.GetText(); + } + + // Write outline close tag. + if( styledText.mStyle.GetOutline() ) + { + markupString += LESS_THAN + ( SLASH + XHTML_OUTLINE_TAG + GREATER_THAN ); //
+ } + + // Write glow close tag. + if( styledText.mStyle.GetGlow() ) + { + markupString += LESS_THAN + ( SLASH + XHTML_GLOW_TAG + GREATER_THAN ); //
+ } + + // Write shadow close tag. + if( styledText.mStyle.GetShadow() ) + { + markupString += LESS_THAN + ( SLASH + XHTML_SHADOW_TAG + GREATER_THAN ); // + } + + // Write smooth close tag. + if( hasSmooth ) + { + markupString += LESS_THAN + ( SLASH + XHTML_SMOOTH_EDGE_TAG + GREATER_THAN ); // + } + + // Write underline close tag. + if( isUnderline ) + { + markupString += LESS_THAN + ( SLASH + XHTML_U_TAG + GREATER_THAN ); //
+ } + + // Write bold close tag. + if( isBold ) + { + markupString += LESS_THAN + ( SLASH + XHTML_B_TAG + GREATER_THAN ); //
+ } + + // Write italics close tag. + if( isItalics ) + { + markupString += LESS_THAN + ( SLASH + XHTML_I_TAG + GREATER_THAN ); //
+ } + + // Write font close tag. + if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor ) + { + markupString += LESS_THAN + ( SLASH + XHTML_FONT_TAG + GREATER_THAN ); //
+ } + } +} + +void SetTextStyle( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask ) +{ + if( !styledTextArray.empty() ) + { + const size_t size = styledTextArray.size() - 1; + SetTextStyleToRange( styledTextArray, style, mask, 0, size ); + } +} + +void SetTextStyle( const Text& text, StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask ) +{ + if( !text.IsEmpty() ) + { + const size_t size = text.GetLength(); + + for( size_t i = 0; i < size; ++i ) + { + StyledText styledText; + styledText.mText = Text( text[i] ); + styledText.mStyle = style; + + styledTextArray.push_back( styledText ); + } + + SetTextStyleToRange( styledTextArray, style, mask, 0, size - 1 ); + } +} + +void SetTextStyleToRange( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end ) +{ + const size_t size = styledTextArray.size(); + DALI_ASSERT_ALWAYS( begin < size ); + DALI_ASSERT_ALWAYS( end < size ); + + for( StyledTextArray::iterator it = styledTextArray.begin() + std::min(begin, end), endIt = styledTextArray.begin() + std::max(begin, end) + 1; it != endIt; ++it ) + { + StyledText& styledText( *it ); + + styledText.mStyle.Copy( style, mask ); + } // for loop +} + +} // namespace MarkupProcessor + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/alpha-discard-effect.cpp b/dali-toolkit/public-api/shader-effects/alpha-discard-effect.cpp new file mode 100644 index 0000000..ab34479 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/alpha-discard-effect.cpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +AlphaDiscardEffect::AlphaDiscardEffect() +{ +} + +AlphaDiscardEffect::~AlphaDiscardEffect() +{ +} + +AlphaDiscardEffect AlphaDiscardEffect::New() +{ + const char* ALPHA_DISCARD_FRAGMENT_SHADER_SOURCE = + "void main() \n" + "{ \n" + " vec4 color = texture2D( sTexture, vTexCoord ); \n" + " if(color.a <= 0.0001) \n" + " { \n" + " discard; \n" + " } \n" + " gl_FragColor = color * uColor; \n" + "} \n"; + + ShaderEffect shader = ShaderEffect::New( "", // Use default + ALPHA_DISCARD_FRAGMENT_SHADER_SOURCE ); + return AlphaDiscardEffect( shader ); +} + +//Call the Parent copy constructor to add reference to the implementation for this object +AlphaDiscardEffect::AlphaDiscardEffect( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/alpha-discard-effect.h b/dali-toolkit/public-api/shader-effects/alpha-discard-effect.h new file mode 100644 index 0000000..2761001 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/alpha-discard-effect.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_ALPHA_DISCARD_EFFECT_H__ +#define __DALI_TOOLKIT_ALPHA_DISCARD_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * Alpha discard effect is used to discard fragments when the alpha colour value is below a threshold. + * This is useful for stenciling. + * + * Usage example: + * + * ImageActor actor = ImageActor::New( Image( EXAMPLE_IMAGE_PATH ) ); + * AlphaDiscardEffect alphaDiscardEffect = AlphaDiscardEffect::New(); + * actor.SetShaderEffect( alphaDiscardEffect ); + */ +class DALI_IMPORT_API AlphaDiscardEffect : public ShaderEffect +{ +public: + + /** + * Create an empty AlphaDiscardEffect handle. + */ + AlphaDiscardEffect(); + + /** + * Virtual destructor. + */ + virtual ~AlphaDiscardEffect(); + + /** + * Create a AlphaDiscardEffect. + * @return A handle to a newly allocated AlphaDiscardEffect. + */ + static AlphaDiscardEffect New(); + +private: // Not intended for application developers + + DALI_INTERNAL AlphaDiscardEffect( ShaderEffect handle ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_ALPHA_DISCARD_EFFECT_H__ diff --git a/dali-toolkit/public-api/shader-effects/bendy-effect.cpp b/dali-toolkit/public-api/shader-effects/bendy-effect.cpp new file mode 100644 index 0000000..b0dad35 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/bendy-effect.cpp @@ -0,0 +1,146 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string DIRECTION_PROPERTY_NAME( "uDirection" ); +const std::string RADIUS_PROPERTY_NAME( "uRadius" ); + +} // namespace + +BendyEffect::BendyEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +BendyEffect::BendyEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +BendyEffect::~BendyEffect() +{ +} + + +BendyEffect BendyEffect::New() +{ + // append the default version + std::string vertextShader( + "uniform mediump vec2 uCenter;\n" + "uniform mediump vec2 uDirection;\n" + "uniform mediump float uRadius;\n" + "\n" + "varying mediump float vShade;\n" + "\n" + "void main()\n" + "{\n" + " mediump float lighting = 0.25;\n" + " mediump vec4 position = uModelView * vec4(aPosition,1.0);\n" + "\n" + " mediump vec2 d = position.xy - uCenter;\n" + " mediump float dist = max( 0.0, dot(d,uDirection) );\n" + " mediump float radius = max(0.0, uRadius - dist * 0.01);\n" + "\n" + " mediump float cs = cos(dist / radius / 2.0);\n" + " mediump float sn = sin(dist / radius / 2.0);\n" + "\n" + "position.xy = position.xy - uDirection * dist;\n" + "\n" + "position.xy += uDirection * sn * radius;\n" + "position.z += (1.0 - cs) * radius;\n" + "\n" + "gl_Position = uProjection * position;\n" + "\n" + "vShade = 1.0 - abs(sn) * lighting;\n" + "\n" + "vTexCoord = aTexCoord;\n" + "}" ); + + std::string fragmentShader( + "varying mediump float vShade;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * uColor * vec4(vShade,vShade,vShade,1.0);\n" + "}" ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + vertextShader, + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( HINT_GRID | HINT_DEPTH_BUFFER )); + + /* Pass ownership to BendyEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::BendyEffect handle( shaderEffectCustom ); + + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.0f, 0.0f), COORDINATE_TYPE_VIEWPORT_POSITION ); + handle.SetUniform( DIRECTION_PROPERTY_NAME, Vector2(0.0f, 0.0f), COORDINATE_TYPE_VIEWPORT_DIRECTION ); + handle.SetUniform( RADIUS_PROPERTY_NAME, 0.0f ); + + return handle; +} + +void BendyEffect::SetCenter( const Vector2& center ) +{ + SetUniform( CENTER_PROPERTY_NAME, center, ShaderEffect::COORDINATE_TYPE_VIEWPORT_POSITION ); +} + +void BendyEffect::SetDirection( const Vector2& direction ) +{ + Vector2 temp(direction); + temp.Normalize(); + + Vector2 newDirection(temp.x, temp.y); + + SetUniform( DIRECTION_PROPERTY_NAME, newDirection, ShaderEffect::COORDINATE_TYPE_VIEWPORT_DIRECTION ); +} + +void BendyEffect::SetRadius( float radius ) +{ + SetUniform( RADIUS_PROPERTY_NAME, radius ); +} + +const std::string& BendyEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& BendyEffect::GetDirectionPropertyName() const +{ + return DIRECTION_PROPERTY_NAME; +} + +const std::string& BendyEffect::GetRadiusPropertyName() const +{ + return RADIUS_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/bendy-effect.h b/dali-toolkit/public-api/shader-effects/bendy-effect.h new file mode 100644 index 0000000..e443f69 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/bendy-effect.h @@ -0,0 +1,100 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_BENDY_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_BENDY_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * BendyEffect is a custom shader effect to achieve bendy effects in Image actors + */ +class BendyEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized BendyEffect; this can be initialized with BendyEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + BendyEffect(); + + /** + * Virtual destructor. + */ + virtual ~BendyEffect(); + + /** + * Create an initialized BendyEffect. + * @return A handle to a newly allocated Dali resource. + */ + static BendyEffect New(); + + /** + * Set the center point of the bendy effect. + * @param [in] center The new center point. + */ + void SetCenter(const Vector2& center); + + /** + * Set the direction of the bendy effect. + * @param [in] direction The new direction. + */ + void SetDirection(const Vector2& direction); + + /** + * Set the radius of the bendy effect. + * @param [in] radius The new radius. + */ + void SetRadius(float radius); + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the direction property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetDirectionPropertyName() const; + + /** + * Get the name for the radius property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetRadiusPropertyName() const; + + +private: // Not intended for application developers + BendyEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_BENDY_H__ diff --git a/dali-toolkit/public-api/shader-effects/blind-effect.cpp b/dali-toolkit/public-api/shader-effects/blind-effect.cpp new file mode 100644 index 0000000..4dd11a1 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/blind-effect.cpp @@ -0,0 +1,96 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string STEP_PROPERTY_NAME( "uStep" ); + +} // namespace + +BlindEffect::BlindEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +BlindEffect::BlindEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +BlindEffect::~BlindEffect() +{ +} + + +BlindEffect BlindEffect::New() +{ + std::string fragmentShader( + "uniform float uStep; \n" + "void main() \n" + "{ \n" + " vec4 alphaColor; \n" + " vec4 baseColor; \n" + " baseColor = texture2D( sTexture, vTexCoord); \n" + " alphaColor = vec4(0.1,0.1,0.1,1.0); \n" + " float index = 0.0; \n" + " index = floor(vTexCoord.y/0.1); \n" + " if((vTexCoord.y < (index * 0.1 + uStep * 0.005)) && (vTexCoord.y > index * 0.1))\n" + " { \n" + " gl_FragColor = alphaColor; \n" + " } \n" + " else \n" + " { \n" + " gl_FragColor = baseColor; \n" + " } \n" + " gl_FragColor*=uColor; \n" + "} \n" + ); + + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + "", + fragmentShader, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID )); + + Dali::Toolkit::BlindEffect handle( shaderEffectCustom ); + + handle.SetUniform( STEP_PROPERTY_NAME, 0.0f ); + + return handle; +} + +void BlindEffect::SetStep(float step) +{ + SetUniform( STEP_PROPERTY_NAME, step ); +} + +const std::string& BlindEffect::GetStepPropertyName() const +{ + return STEP_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/blind-effect.h b/dali-toolkit/public-api/shader-effects/blind-effect.h new file mode 100644 index 0000000..10fa857 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/blind-effect.h @@ -0,0 +1,75 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_BLIND_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_BLIND_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * BlindEffect is a custom shader effect to achieve blind effects in Image actors + */ +class BlindEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized BlindEffect; this can be initialized with BlindEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + BlindEffect(); + + /** + * Virtual destructor. + */ + virtual ~BlindEffect(); + + /** + * Create an initialized ~BlindEffect. + * @return A handle to a newly allocated Dali resource. + */ + static BlindEffect New(); + + + /** + * Set the step of the blind effect. + * @param [in] step The step + */ + void SetStep(float step); + + /** + * Get the name for the step property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetStepPropertyName() const; + +private: // Not intended for application developers + BlindEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_BLIND_H__ diff --git a/dali-toolkit/public-api/shader-effects/bubble-effect/bubble-effect.cpp b/dali-toolkit/public-api/shader-effects/bubble-effect/bubble-effect.cpp new file mode 100644 index 0000000..71e7f05 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/bubble-effect/bubble-effect.cpp @@ -0,0 +1,251 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 HEADERS +#include + +// CLASS HEADER +#include "bubble-effect.h" + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string MAGNIFICATIOB_PROPERTY_NAME( "uMagnification" ); +const float EACH_WIDTH_PER_SHAPE(32.0f); + +} // namespace + +BubbleEffect::BubbleEffect() +: mNumberOfBubbles(0) +{ +} + +BubbleEffect::BubbleEffect( ShaderEffect handle ) +: ShaderEffect( handle ), + mNumberOfBubbles(0) +{ +} + +BubbleEffect::~BubbleEffect() +{ +} + +BubbleEffect BubbleEffect::New( unsigned int numberOfBubble, const std::string& shapeImagePath) +{ + std::ostringstream vertexShaderStringStream; + vertexShaderStringStream << "#define NUMBER_OF_BUBBLE "<< numberOfBubble << "\n"; + std::string vertexShader( + // the gravity applied to the y direction + " uniform float uGravity; \n" + // Width of the texture in pixels + " uniform float uShapeWidth; \n" + // xy: the emit position of the bubble; zw: the destinationof the bubble. + // The bubble is moving from (xy) to (zw plus the y drop influenced by gravity). + " uniform vec4 uStartAndEndPos[NUMBER_OF_BUBBLE];\n" + // The undergoing percentage of the bubble movement. 0.0: start from emit position, 1.0: reach the destination + " uniform float uPercentage[NUMBER_OF_BUBBLE];\n" + " uniform vec2 uInvertedMovementArea; \n" + // The bubble number is restricted by the available uniform num. + // To increase the displayed bubble, every uStartAndEndPos and uPercentage uniform is applied to a small bunch of bubbles (9 here) + // The offset defines the random offset between bubbles within the bunch. + " uniform vec2 offset[9]; \n" + // This uniform is specially for increase part of the bubble size and spread the bubble to the whole screen when unlock to home screen + " uniform float uMagnification; \n" + // This uniform is used to change the bubble size during running time + " uniform float uDynamicScale; \n" + " varying float vPercentage;\n" + " varying vec2 vEffectTexCoord;\n" + " void main()\n" + " {\n" + " mediump vec4 position = vec4( aPosition.xy, 0.0, 1.0 );\n" + // The Z coordinate is used to record the bubble index within current mesh actor + " int zCoord = int(aPosition.z); \n" + // for some i between 0 ~ NUMBER_OF_BUBBLE-1: i,i+NUMBER_OF_BUBBLE, i+NUMBER_OF_BUBBLE*2, ... (up to i+NUMBER_OF_BUBBLE*8) belongs to the same bunch. + " int groupIdx = zCoord / NUMBER_OF_BUBBLE;\n" + // The bubbles within the same bunch applies the same uniforms uStartAndEndPos[idx] & uPercentage[idx] + " int idx = zCoord - groupIdx*NUMBER_OF_BUBBLE;\n" + // early out if uPercentage is (zero || one) setting position to zero (zero sized triangles) + " if( uPercentage[idx] <= 0.0 || uPercentage[idx] >= 1.0 )\n" + " {\n" + " gl_Position = vec4(0.0);\n" + " return;\n" + " }\n" + " vec4 startAndEnd = uStartAndEndPos[idx]; \n" + // The final position is added up different offset for bubbles + " startAndEnd.zw += offset[groupIdx];\n" + // Notice: Only animate the uMagnification for unlock (bubble explosion animation)! + // In other cases, uMagnification = 1.0! + // Increase the Size of part of bubbles and increase the speed of movement for unlock. + // Performance acceptable: Branch on a uniform variable. + " if( uMagnification > 1.0)\n" + " {\n" + " if(mod(aPosition.z,24.0) < 1.0 )\n" + " {\n" + " position.xy *= uMagnification;\n" + " }\n" + " }\n" + " float percentage = uPercentage[idx]*min(uMagnification,2.5);\n" + "\n" + // increase the bubble size from 0% to 100% during the first 1/5 of movement & apply the dynamic scale + // the new xy value containes both the new scale and new bubble position + " position.xy *= uDynamicScale*min(percentage*5.0, 1.0);\n" + " position.xy += mix(startAndEnd.xy, startAndEnd.zw, percentage*uMagnification);\n" + // The gravity is g*t*t on the y direction + " position.y += uGravity * pow(percentage, 2.0);\n" + " gl_Position = uMvpMatrix * position;\n" + "\n" + // Add multiple bubble shapes in the effect + " mediump float texCoordX = floor( mod(startAndEnd.z, uShapeWidth) );\n " + " mediump float texCoordY = floor( mod(startAndEnd.w, uShapeWidth) );\n " + " vTexCoord = vec2( (texCoordX + aTexCoord.x)/ uShapeWidth,(texCoordY + aTexCoord.y)/ uShapeWidth );\n" + " vPercentage = percentage;\n" + // Use the emit position color for the bubble + " vEffectTexCoord = startAndEnd.xy * uInvertedMovementArea;\n" + " }\n" ); + vertexShaderStringStream << vertexShader; + + std::string fragmentShader( + " varying float vPercentage;\n" + " varying vec2 vEffectTexCoord;\n" + "\n" + " void main()\n" + " {\n" + // Get the emit pisition color, and Mix with the actor color + " vec4 fragColor = texture2D(sEffect, vEffectTexCoord)*uColor;\n" + // Apply the shape defined by the texture contained in the material + // And make the opacity being 0.7, and animate from 0.7 to 0 during the last 1/5 of movement + " fragColor.a *= texture2D(sTexture, vTexCoord).a * ( 3.5 - max( vPercentage*3.5, 2.8 ) );\n" + " gl_FragColor = fragColor;\n" + " }\n"); + + ShaderEffect shaderEffect = ShaderEffect::New( vertexShaderStringStream.str(), fragmentShader, + GeometryType( GEOMETRY_TYPE_TEXTURED_MESH), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) ); + BubbleEffect handle( shaderEffect ); + + handle.mNumberOfBubbles = numberOfBubble; + handle.SetMovementArea( Stage::GetCurrent().GetSize() ); + + + handle.SetUniform( "uGravity", 50.f ); + handle.SetUniform( "uMagnification", 1.f ); + handle.SetUniform( "uDynamicScale", 1.f ); + + //Get pixel width of the shape + float width = Image::GetImageSize(shapeImagePath).width; + handle.SetUniform( "uShapeWidth", (width/EACH_WIDTH_PER_SHAPE) ); + + Vector4 zeroVector; + for( unsigned int i=0; i + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * BubbleEffect is a custom shader to achieve similar effect of particle system by applying on a specially created MeshActor + * Each bubble is rendered on a patch with two triangles; and each mesh can contain multiple such patches. + */ +class BubbleEffect : public ShaderEffect +{ +public: + + /** + * Create an empty BubbleEffect handle + */ + BubbleEffect(); + + /** + * Virtual destructor + */ + virtual ~BubbleEffect(); + + /** + * Create an initialized BubbleEffect + * @param[in] numberOfBubble How many groups of uniforms are used to control the bubble movement. + * Note: Limited by the maximum available uniforms, this parameter cannot be bigger than 100. + * Ideally use one group of uniform to control one bubble. + * If the num of patches in the MeshActor is more than groups of uniforms, + * the uniform values will be shared by multiple bubbles. Allow up to 9 times. + * @param shapeImagePath File path of the image that will be used as a texture for each bubble. + * @return A handle to a newly allocated Dali resource. + */ + static BubbleEffect New( unsigned int numberOfBubble, const std::string& shapeImagePath); + + /** + * Set the bubble movement area for the BubbleEffect + * @param[in] movementArea The size of bubble movement area; by default, it is the stage size + */ + void SetMovementArea( const Vector2& movementArea ); + + /** + * Set the start and end positions of the index-th bubble's movement. + * @param[in] index Indicate which bubble these properties are applied on. + * @param[in] startAndEndPosition The start and the end position of movement. + */ + void SetStartAndEndPosition( unsigned int index, const Vector4& startAndEndPosition ); + + /** + * Set the movement completed percentage of the index-th bubble. + * The bubble will appear at start position when percentage equals to zero, + * and disappear near end position (affected by gravity) when percentage equals to one. + * This percentage property is used to animate the bubble movement. + * @param[in] index Indicate which bubble this property is applied on. + * @param[in] percentage Set the percentage property value ( between zero and one ). + */ + void SetPercentage( unsigned int index, float percentage ); + + /** + * Set the gravity applied to the y direction, which makes the bubbles no longer moving on a straight line. + * @param[in] gravity The gravity on the y direction. + */ + void SetGravity( float gravity ); + + /* + * Set the width of shape image + * If one image has multiple shape, bubble effect will parse one shape from the image randomly. + * @param[in] imageWidth width of shape image + */ + void SetShapeImageWidth( float imageWidth ); + + /** + * Set the scale factor applied to the bubbles + * @param[in] scale The scale factor applied on all bubbles. + */ + void SetDynamicScale( float scale ); + + /** + * Increase both the bubble size and moving speed. + * Animate this peoperty to create special effect such as all the bubbles blow up on the screen. + * @param[in] magnification The manified factor applied on the bubble size and moving speed. + */ + void SetMagnification( float magnification ); + + /** + * Get the name for the idx-th percentage property. + * @param[in] index The percentage property index. + * @return std::string containing the property name. + */ + std::string GetPercentagePropertyName( unsigned int index ) const; + + /** + * Get the name for the magnification property. + * @return std::string containinf the property name. + */ + std::string GetMagnificationPropertyName() const; + + /** + * Reset the uniform values to default. + */ + void ResetParameters(); + +private:// Not intended for application developers + + BubbleEffect( ShaderEffect handle ); + +private: + + unsigned int mNumberOfBubbles; + Vector2 mMovementArea; +}; + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_SHADER_BUBBLE_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.cpp b/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.cpp new file mode 100644 index 0000000..885d9e8 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.cpp @@ -0,0 +1,97 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include "color-adjuster.h" + +namespace Dali +{ + +namespace Toolkit +{ +namespace +{ + const std::string HSVDELTA_PROPERTY_NAME("uHSVDelta"); +} +ColorAdjuster::ColorAdjuster() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +ColorAdjuster::ColorAdjuster( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +ColorAdjuster::~ColorAdjuster() +{ +} + +ColorAdjuster ColorAdjuster::New( const Vector3& hsvDelta, bool ignoreAlpha ) +{ + std::string fragmentShader( + " precision highp float;\n" + " uniform vec3 uHSVDelta;\n" + " uniform float uIgnoreAlpha;\n" + " float rand(vec2 co) \n" + " {\n" + " return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n" + " }\n" + " vec3 rgb2hsv(vec3 c)\n" + " {\n" + " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n" + " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n" + " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n" + " \n" + " float d = q.x - min(q.w, q.y);\n" + " float e = 1.0e-10;\n" + " return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n" + " }\n" + " vec3 hsv2rgb(vec3 c)\n" + " {\n" + " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n" + " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n" + " return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n" + " }\n" + " void main() {\n" + " vec4 color = texture2D(sTexture, vTexCoord); \n" + " vec3 hsvColor = rgb2hsv( color.rgb );\n" + // modify the hsv Value + " hsvColor += uHSVDelta * rand(vTexCoord); \n" + // if the new vale exceeds one, then decrease it + " hsvColor -= max(hsvColor*2.0 - vec3(2.0), 0.0);\n" + // if the new vale drops below zero, then increase it + " hsvColor -= min(hsvColor*2.0, 0.0);\n" + " color.rgb = hsv2rgb( hsvColor ); \n" + // uIgnoreAlpha decide the result alpha will be 1.0 or source's alpha + " color.a = clamp(color.a + uIgnoreAlpha, 0.0, 1.0);\n" + " gl_FragColor = color; \n" + " }\n"); + + ShaderEffect effect = ShaderEffect::New("", fragmentShader); + ColorAdjuster handle( effect ); + handle.SetUniform( "uHSVDelta", hsvDelta ); + handle.SetUniform( "uIgnoreAlpha", ignoreAlpha?1.0f:0.0f ); + return handle; +} + +std::string ColorAdjuster::GetHsvDeltaPropertyName() const +{ + return HSVDELTA_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.h b/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.h new file mode 100644 index 0000000..8c1884a --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_SHADER_COLOR_ADJUSTER_H__ +#define __DALI_TOOLKIT_SHADER_COLOR_ADJUSTER_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * ColorAdjuster is a custom shader effect to adjust the image color in HSV space. + */ +class ColorAdjuster : public ShaderEffect +{ +public: + + /** + * Create an empty ColorAdjuster handle. + */ + ColorAdjuster(); + + /** + * Virtual destructor. + */ + virtual ~ColorAdjuster(); + + /** + * Create an initialized ColorAdjuster. + * @param[in] hsvDelta The color difference to apply to the HSV channel. + * @param[in] ignoreAlpha If true, the result color will be opaque even though source has alpha value + * @return A handle to a newly allocated Dali resource. + */ + static ColorAdjuster New( const Vector3& hsvDelta, bool ignoreAlpha = false ); + + /** + * Get the name of the uHSVDelta uniform so that it can be animated + */ + std::string GetHsvDeltaPropertyName() const; + +private: // Not intended for application developers + +ColorAdjuster( ShaderEffect handle ); + +}; + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_COLOR_ADJUSTER_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/shader-effects/carousel-effect.cpp b/dali-toolkit/public-api/shader-effects/carousel-effect.cpp new file mode 100644 index 0000000..c6119f6 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/carousel-effect.cpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string RADIUS_PROPERTY_NAME( "uRadius" ); +const std::string ANGLE_PER_UNIT_PROPERTY_NAME( "uAnglePerUnit" ); +const std::string CENTER_PROPERTY_NAME( "uCenter" ); + +} // namespace + +CarouselEffect::CarouselEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +CarouselEffect::CarouselEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +CarouselEffect::~CarouselEffect() +{ +} + + +CarouselEffect CarouselEffect::New() +{ + // append the default version + std::string vertexShader( + "uniform float uRadius;\n" + "uniform mediump vec2 uCenter;\n" + "uniform mediump vec2 uAnglePerUnit;\n" + "\n" + "void main()\n" + "{\n" + " vec4 world = uModelView * vec4(aPosition,1.0);\n" + " vec2 d = (world.xy - uCenter) * uAnglePerUnit;\n" + " float a = length(d);\n" + " float cs = cos(radians(a));\n" + " world.z += cs * uRadius;\n" + " gl_Position = uProjection * world;\n" + " \n" + " vTexCoord = aTexCoord;\n" + "}\n"); + + ShaderEffect shaderEffectCustom = ShaderEffect::New(vertexShader, + "", + GeometryType( GEOMETRY_TYPE_IMAGE | GEOMETRY_TYPE_TEXT ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_DEPTH_BUFFER )); + + // Pass ownership to CarouselEffect through overloaded constructor, So that it now has access to the + // Dali::ShaderEffect implementation + CarouselEffect handle( shaderEffectCustom ); + + handle.SetUniform( RADIUS_PROPERTY_NAME, 0.0f ); + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2( 0.0f, 0.0f ) ); + handle.SetUniform( ANGLE_PER_UNIT_PROPERTY_NAME, Vector2( 0.0f, 0.0f ) ); + + return handle; +} + +void CarouselEffect::SetRadius( float radius) +{ + SetUniform( RADIUS_PROPERTY_NAME, radius ); +} + +void CarouselEffect::SetCenter( const Vector2& center ) +{ + SetUniform( CENTER_PROPERTY_NAME, center ); +} + +void CarouselEffect::SetAnglePerUnit( const Vector2& angle ) +{ + SetUniform( ANGLE_PER_UNIT_PROPERTY_NAME, angle ); +} + +const std::string& CarouselEffect::GetRadiusPropertyName() const +{ + return RADIUS_PROPERTY_NAME; +} + +const std::string& CarouselEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& CarouselEffect::GetAnglePerUnitPropertyName() const +{ + return ANGLE_PER_UNIT_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/carousel-effect.h b/dali-toolkit/public-api/shader-effects/carousel-effect.h new file mode 100644 index 0000000..4190d69 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/carousel-effect.h @@ -0,0 +1,118 @@ +#ifndef __DALI_TOOLKIT_CAROUSEL_EFFECT_H__ +#define __DALI_TOOLKIT_CAROUSEL_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * CarouselEffect is a custom shader effect to achieve Carousel effects in actors + * + * A Carousel has a Radius property which can be +ve (appear as if viewing from the outside of + * a cylinder/sphere) + * or -ve (appear as if viewing from the inside of a cylinder/sphere). + * + * It can be a horizontal or vertical (cylindrical) or both (spherical). The AnglePerUnit + * property provides this functionality as a Vector2. + * + * Finally, the carousel's center position can be specified as a Screen coordinate (top-left being + * the origin). + */ +class CarouselEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized CarouselEffect; this can be initialized with CarouselEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + CarouselEffect(); + + /** + * Virtual destructor. + */ + virtual ~CarouselEffect(); + + /** + * Create an initialized CarouselEffect. + * @return A handle to a newly allocated Dali resource. + */ + static CarouselEffect New(); + + /** + * Set the radius of the Carousel effect. + * A positive Radius will bend toward the camera, + * while a negative Radius will bend away from the camera. + * @param[in] radius The new radius. + */ + void SetRadius( float radius); + + /** + * Sets the center point of the carousel (in screen coordinates) + * this is where the peek of the carousel should appear. + * this defaults to top-left corner (0.0f, 0.0f). + * + * @param[in] center The center point. + */ + void SetCenter( const Vector2& center ); + + /** + * Set the angle deviation of Carousel in degrees per + * geometric unit for each axis. For example if you + * wish for the horizontal angle deviation to vary from +/- 10 + * degrees, then a Value of 20.0f / stageWidth for the X + * component should be specified. + * + * @param[in] angle the Angle Spread in X and Y axes. + */ + void SetAnglePerUnit( const Vector2& angle ); + + /** + * Get the name for the radius property + * @return A std::string containing the property name + */ + const std::string& GetRadiusPropertyName() const; + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the angle spread property + * @return A std::string containing the property name + */ + const std::string& GetAnglePerUnitPropertyName() const; + + +private: // Not intended for application developers + CarouselEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_CAROUSEL_EFFECT_H__ diff --git a/dali-toolkit/public-api/shader-effects/displacement-effect.cpp b/dali-toolkit/public-api/shader-effects/displacement-effect.cpp new file mode 100644 index 0000000..6594657 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/displacement-effect.cpp @@ -0,0 +1,240 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string DISPLACEMENT_EFFECT_LIGHT_DIRECTION_PROPERTY_NAME( "uLightDirection" ); +const std::string DISPLACEMENT_EFFECT_AMBIENT_LIGHT_COLOR_PROPERTY_NAME( "uAmbientLightColor" ); +const std::string DISPLACEMENT_EFFECT_DIFFUSE_LIGHT_COLOR_PROPERTY_NAME( "uDiffuseLightColor" ); +const std::string DISPLACEMENT_EFFECT_LIGHT_MULTIPLIER_PROPERTY_NAME( "uLightMultiplier" ); +const std::string DISPLACEMENT_EFFECT_STATE_PROPERTY_NAME( "uState" ); +const std::string DISPLACEMENT_EFFECT_HEIGHT_SCALE_PROPERTY_NAME( "uHightScale" ); +const std::string DISPLACEMENT_EFFECT_FIXED_NORMAL_PROPERTY_NAME( "uFixedNormal" ); + +// factors that scale the look, defaults +const Vector3 DISPLACEMENT_EFFECT_LIGHT_DIRECTION_DEFAULT = Vector3(0.0, 0.7070168f, 0.7071068f); +const Vector3 DISPLACEMENT_EFFECT_AMBIENT_LIGHT_COLOR_DEFAULT = Vector3(0.15f, 0.15f, 0.15f); +const Vector3 DISPLACEMENT_EFFECT_DIFFUSE_LIGHT_COLOR_DEFAULT = Vector3(1.0f, 1.0f, 1.0f); +const float DISPLACEMENT_EFFECT_LIGHT_MULTIPLIER_DEFAULT = 1.0f; +const float DISPLACEMENT_EFFECT_STATE_DEFAULT = 0.0f; +const float DISPLACEMENT_EFFECT_HEIGHT_SCALE_DEFAULT = 0.1f; +const Vector3 DISPLACEMENT_EFFECT_FIXED_NORMAL_DEFAULT = Vector3(0.0f, 0.0f, 1.0f); + +} // namespace + + +//////////////////////////////////////////////////// +// +// Soft button shader / actor tweaking parameters +// + + +DisplacementEffect::DisplacementEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +DisplacementEffect::DisplacementEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +DisplacementEffect::~DisplacementEffect() +{ +} + +DisplacementEffect DisplacementEffect::New(Type type) +{ + + std::string fragmentSourceFixed; + fragmentSourceFixed = "precision mediump float;\n" + "uniform vec3 uLightDirection;\n" + "uniform vec3 uAmbientLightColor;\n" + "uniform vec3 uDiffuseLightColor;\n" + "uniform float uLightMultiplier;\n" + "uniform float uState;\n" + "uniform float uHightScale;\n" + "uniform vec3 uFixedNormal;\n" + + "void main()\n" + "{\n" + " vec4 col = texture2D(sTexture, vTexCoord);\n" + // calc lighting + " float intensity = dot(uLightDirection, uFixedNormal);" + " vec3 lighting = (intensity * uDiffuseLightColor) + uAmbientLightColor;\n" + " lighting *= uLightMultiplier;\n" + // output col = image * light + " gl_FragColor = vec4(col.rgb * lighting * uColor.rgb, col.a * uColor.a);\n" + "}\n"; + + + + std::string fragmentSourceDisplaced( + "precision mediump float;\n" + "uniform vec3 uLightDirection;\n" + "uniform vec3 uAmbientLightColor;\n" + "uniform vec3 uDiffuseLightColor;\n" + "uniform float uLightMultiplier;\n" + "uniform float uState;\n" + "uniform float uHightScale;\n" + "void main()\n" + "{\n" + " highp vec4 displacementMap1 = texture2D(sEffect, vec2(vTexCoord.s, vTexCoord.t/2.0));\n" + " highp vec4 displacementMap2 = texture2D(sEffect, vec2(vTexCoord.s, 0.5+vTexCoord.t/2.0));\n" + " highp vec4 displacementMap = mix(displacementMap1, displacementMap2, uState);\n" + + " vec3 normalAdjusted = normalize(displacementMap.rgb*2.0-1.0);\n" + " float height = uHightScale * (displacementMap.a*2.0 - 1.0);\n" + " vec2 displacement = vec2(0.0);\n" + " displacement += (vec2(0.5)-vTexCoord.st)*height;\n" + " vec2 newCoord = vTexCoord.st + displacement.xy;\n" + + " vec4 col = texture2D(sTexture, newCoord);\n" + // Y-Axis for the normal map is taken as in Y-Down format, So inverting it for GL + " float intensity = dot(uLightDirection, vec3(1.0,-1.0, 1.0) * normalAdjusted);" + " vec3 lighting = (intensity * uDiffuseLightColor) + uAmbientLightColor;\n" + " lighting *= uLightMultiplier;\n" + " vec3 color = col.rgb * lighting * uColor.rgb;\n" + " gl_FragColor = vec4(color, col.a * uColor.a);\n" + "}\n"); + + ////////////////////////////////////// + // Create shader effect + // + // + + ShaderEffect shader; + switch(type) + { + case DISPLACED: + shader = ShaderEffect::New( "", fragmentSourceDisplaced); + break; + + case FIXED: + default: + shader = ShaderEffect::New( "", fragmentSourceFixed); + break; + } + DisplacementEffect handle( shader ); + + + ////////////////////////////////////// + // Register uniform properties + // + // + // factors that scale the look, defaults + + handle.SetLightDirection(DISPLACEMENT_EFFECT_LIGHT_DIRECTION_DEFAULT); + handle.SetAmbientLightColorProperty(DISPLACEMENT_EFFECT_AMBIENT_LIGHT_COLOR_DEFAULT); + handle.SetDiffuseLightColorProperty(DISPLACEMENT_EFFECT_DIFFUSE_LIGHT_COLOR_DEFAULT); + handle.SetLightingMultiplierProperty(DISPLACEMENT_EFFECT_LIGHT_MULTIPLIER_DEFAULT); + handle.SetStateProperty(DISPLACEMENT_EFFECT_STATE_DEFAULT); + handle.SetHeightScaleProperty(DISPLACEMENT_EFFECT_HEIGHT_SCALE_DEFAULT); + + if(type == FIXED) + { + handle.SetFixedNormalProperty(DISPLACEMENT_EFFECT_FIXED_NORMAL_DEFAULT); + } + + + return handle; +} + +const std::string& DisplacementEffect::GetLightDirectionPropertyName() const +{ + return DISPLACEMENT_EFFECT_LIGHT_DIRECTION_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetAmbientLightColorPropertyName() const +{ + return DISPLACEMENT_EFFECT_AMBIENT_LIGHT_COLOR_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetDiffuseLightColorPropertyName() const +{ + return DISPLACEMENT_EFFECT_DIFFUSE_LIGHT_COLOR_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetLightingMultiplierPropertyName() const +{ + return DISPLACEMENT_EFFECT_LIGHT_MULTIPLIER_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetStatePropertyName() const +{ + return DISPLACEMENT_EFFECT_STATE_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetHeightScalePropertyName() const +{ + return DISPLACEMENT_EFFECT_HEIGHT_SCALE_PROPERTY_NAME; +} + +const std::string& DisplacementEffect::GetFixedNormalPropertyName() const +{ + return DISPLACEMENT_EFFECT_FIXED_NORMAL_PROPERTY_NAME; +} + +void DisplacementEffect::SetLightDirection(const Vector3 lightDirection) +{ + SetUniform( DISPLACEMENT_EFFECT_LIGHT_DIRECTION_PROPERTY_NAME, lightDirection); +} + +void DisplacementEffect::SetAmbientLightColorProperty(const Vector3 ambientLight) +{ + SetUniform( DISPLACEMENT_EFFECT_AMBIENT_LIGHT_COLOR_PROPERTY_NAME, ambientLight); +} + +void DisplacementEffect::SetDiffuseLightColorProperty(const Vector3 diffuseLight) +{ + SetUniform( DISPLACEMENT_EFFECT_DIFFUSE_LIGHT_COLOR_PROPERTY_NAME, diffuseLight); +} + +void DisplacementEffect::SetLightingMultiplierProperty(const float lightMultiplier) +{ + SetUniform( DISPLACEMENT_EFFECT_LIGHT_MULTIPLIER_PROPERTY_NAME, lightMultiplier); +} + +void DisplacementEffect::SetStateProperty(const float state) +{ + SetUniform( DISPLACEMENT_EFFECT_STATE_PROPERTY_NAME, state); +} + +void DisplacementEffect::SetHeightScaleProperty(const float heightScale) +{ + SetUniform( DISPLACEMENT_EFFECT_HEIGHT_SCALE_PROPERTY_NAME, heightScale); +} + +void DisplacementEffect::SetFixedNormalProperty(const Vector3 fixedNormal) +{ + Vector3 newFixedNormal(fixedNormal); + newFixedNormal.Normalize(); + + SetUniform( DISPLACEMENT_EFFECT_FIXED_NORMAL_PROPERTY_NAME, newFixedNormal); +} + +} + +} + diff --git a/dali-toolkit/public-api/shader-effects/displacement-effect.h b/dali-toolkit/public-api/shader-effects/displacement-effect.h new file mode 100644 index 0000000..ee163b3 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/displacement-effect.h @@ -0,0 +1,221 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_DISPLACEMENT_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_DISPLACEMENT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * + * Class for two state displacement effect shader that works on a per object basis. By passing a height-normal map as an effect image, the user can create + * various styles of buttons on an image actor. The shader requires two height-normal maps in one image, one for each state. + * + * The normals and height information for the two states of the button should be strictly specified in this format: + * ______________ + * | State 0 | + * | | + * | | --> Unpressed button normals in rgb and height in a + * | Map | + * |______________| + * | State 1 | + * | | + * | | --> Pressed button normals in rgb and height in a + * | Map | + * |______________| + * + * The RGB values should contain the surface normals and the alpha should contian the height map. For a better effect keep the highest point (alpha value) in + * the combined map as 1.0 and the lowest posint as 0.0 and 0.5 for any region which doesn't need displacement. + * + * For the supplied Normal map the Y-Axis should be down, Meaning (0,0) is in the top left. As the shader inverts the Y axis for lighting calculation. + * + * Limitations: Can be applied to ImageActor only, And doesn't provide support for specular color. + * + * Usage example:- + * + * // Create shader used for doing soft button\n + * DisplacementEffect buttonEffect = DisplacementEffect::New(); + * buttonEffect.SetEffectImage(Image::New( FANCY_BUTTON_HEIGHT_MAP_IMAGE_PATH );); + * + * // set shader to the soft button\n + * ImageActor fancyButton = ImageActor::New( ... );\n + * fancyButton.SetShaderEffect( buttonEffect ); + * + * // animate a button push, using e.g. AlphaFunctions::Bounce. With these values the button pushes in and out (animates to and fro between the two states) + * + * + * Animation animation = Animation::New( ... );\n + * animation.AnimateTo( Property(buttonEffect, buttonEffect.GetStatePropertyName()), 1.0f, AlphaFunctions::Bounce, ... );\n + * animation.Play();\n + * + */ +class DisplacementEffect : public ShaderEffect +{ + +public: + + typedef enum + { + DISPLACED = 0, /// Image gets displaced + FIXED /// Image does not displace. Useful for matching lighting between areas that do not displace and those that do, e.g for backgrounds which are visible between buttons. + }Type; + + /** + * Create an uninitialized DisplacementEffect; this can be initialized with DisplacementEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + DisplacementEffect(); + + /** + * Virtual destructor. + */ + virtual ~DisplacementEffect(); + + /** + * Create an initialized DisplacementEffect + * @param type The type of the effect, can be either DISPLACED, or FIXED. + * @return A handle to a newly allocated Dali resource. + */ + static DisplacementEffect New(Type type); + + /** + * Get the name for the light direction property (Vector3) + * The light direction is used in the lighting calculation. The angle of incidence directly affects the amount of light reflected. + * Default (0.0f, 0.7070168f, 0.7071068f), i.e angled at the surface from in front and above. + * @return A std::string containing the property name + */ + const std::string& GetLightDirectionPropertyName() const; + + /** + * Get the name for the ambient lighting color property (Vector3) + * The ambient light is used in the lighting calculation. Care must be taken to not saturate the image by setting this value too high, + * or the indentation will not look correct. Default 0.15. + * @return A std::string containing the property name + */ + const std::string& GetAmbientLightColorPropertyName() const; + + /** + * Get the name for the diffuse light color property (Vector3). + * The diffuse light is used in the lighting calculation. Default is (1.0f, 1.0f, 1.0f). + * @return A std::string containing the property name + */ + const std::string& GetDiffuseLightColorPropertyName() const; + + /** + * Get the name for the lighting multiplier property (float). + * The ambient and diffuse lighting is multiplied by this factor. Since a diffuse light at an angle will cause the whole image to darken, + * this property can be used to scale the image back up closer to the pixel values of the original diffuse texture. Care must be taken to not saturate the image, + * or the indentation will not look correct. Default 1.0 + * @return A std::string containing the property name + */ + const std::string& GetLightingMultiplierPropertyName() const; + + /** + * Get the name for the state property (float). + * The shader can have a maximum of two end states 0 or 1, Animate between these two values to do the transitions between states. + * Default 0.0 + * @return A std::string containing the property name. + */ + const std::string& GetStatePropertyName() const; + + /** + * Get the name for the height scale property (float). + * The height displacement is multiplied by this factor. Tweak this to get the required level of depth. + * Default 0.1 + * @return A std::string containing the property name. + */ + const std::string& GetHeightScalePropertyName() const; + + /** + * Get the name for the fixed normal property (Vector3). + * Only applicable for the FIXED type shader and not for DISPLACEMENT type. + * The Fixed normal will be used for the light calculation. Tweak this to get the required level of light. + * Default (0.0f, 0.0f, 1.0f) + * @return A std::string containing the property name. + */ + const std::string& GetFixedNormalPropertyName() const; + + /** + * Set the light direction property + * The light direction is used in the lighting calculation. The angle of incidence directly affects the amount of light reflected. + * Default (0.0f, 0.7070168f, 0.7071068f), i.e angled at the surface from in front and above. + * @param [in] lightDirection The new light direction. + */ + void SetLightDirection(Vector3 lightDirection); + + /** + * Set the ambient light color property + * The ambient light is used in the lighting calculation. Care must be taken to not saturate the image by setting this value too high, + * or the indentation will not look correct. Default (0.15f, 0.15f, 0.15f). + * @param [in] ambientLight The new ambient light value. + */ + void SetAmbientLightColorProperty(Vector3 ambientLight); + + /** + * Set the diffuse light color property. + * The diffuse light is used in the lighting calculation. Default is (1.0f, 1.0f, 1.0f), i.e. a white light so the natural image color is shown. + * @param [in] diffuseLight The new diffuse light value. + */ + void SetDiffuseLightColorProperty(Vector3 diffuseLight); + + /** + * Get the name for the lighting multiplier property. + * The ambient and diffuse lighting is multiplied by this factor. Since a diffuse light at an angle will cause the whole image to darken, + * this property can be used to scale the image back up closer to the pixel values of the original diffuse texture. Care must be taken to not saturate the image, + * or the indentation will not look correct. Default 1.0 + * @param [in] lightMultiplier The new light multiplier value. + */ + void SetLightingMultiplierProperty(float lightMultiplier); + + /** + * Get the name for the state property. + * The shader can only be in or in between two states 0 or 1, Animate between these two values to do the transitions between states. + * @param [in] state The new state value. + */ + void SetStateProperty(float state); + + /** + * Set the name for the height scale property. + * The height displacement is multiplied by this factor. Tweak this to get the required level of depth. Default 0.1 + * @param [in] heightScale The new height scale. + */ + void SetHeightScaleProperty(float heightScale); + + /** + * Set the name for fixed normal property, Only applicable for the FIXED type shader and not for DISPLACEMENT type. + * The Fixed normal will be used for the light calculation. Tweak this to get the required level of light. + * @param [in] fixedNormal The new normal for the fixed type shader effect. + */ + void SetFixedNormalProperty(Vector3 fixedNormal); + +private: + // Not intended for application developers + DisplacementEffect(ShaderEffect handle); +}; + +} + +} + +#endif //#ifndef __DALI_TOOLKIT_SHADER_EFFECT_DISPLACEMENT_H__ + diff --git a/dali-toolkit/public-api/shader-effects/dissolve-effect.cpp b/dali-toolkit/public-api/shader-effects/dissolve-effect.cpp new file mode 100644 index 0000000..c623f0c --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/dissolve-effect.cpp @@ -0,0 +1,198 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string DISTORTION_PROPERTY_NAME( "uPercentage" ); + +} // namespace + +DissolveEffect::DissolveEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +DissolveEffect::DissolveEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +DissolveEffect::~DissolveEffect() +{ +} + + +DissolveEffect DissolveEffect::New( bool useHighPrecision ) +{ + std::string prefixHighPrecision( "precision highp float;\n"); + std::string prefixMediumPrecision( "precision mediump float;\n" ); + std::string vertexShader( + "uniform float uPercentage;\n" + "uniform vec3 uSaddleParam;\n" + "uniform vec2 uTranslation;\n" + "uniform vec2 uRotation; \n" + "uniform float uToNext;\n" + "varying float vPercentage;\n" + "void main()\n" + "{\n" + "gl_Position = uProjection * uModelView * vec4(aPosition, 1.0);\n" + "vTexCoord = aTexCoord;\n" + //Calculate the distortion value given the dissolve central line + "vec2 texCoor = vec2( (aTexCoord.s - sTextureRect.s ) / (sTextureRect.p - sTextureRect.s), (aTexCoord.t- sTextureRect.t)/(sTextureRect.q - sTextureRect.t) ); \n" + "vec2 value = texCoor + uTranslation; \n" + "mat2 rotateMatrix = mat2( uRotation.s, uRotation.t, -uRotation.t, uRotation.s ); \n" + "value = rotateMatrix * value; \n" + "if(uToNext == 1.0) \n" + " value.s = uSaddleParam[2] + value.s; \n" + "float delay = value.t*value.t / uSaddleParam[0] - value.s*value.s/uSaddleParam[1];\n" + "vPercentage = clamp( uPercentage*2.0 - 0.5*sin(delay*1.571) - 0.5, 0.0, 1.0 ); \n" + "}\n"); + std::string fragmentShader( + "varying float vPercentage;\n" + "float rand(vec2 co) \n" + "{\n" + " return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n" + "}\n" + "void main()\n" + "{\n" + //Calculate the randomness + "float offsetS = rand( vTexCoord * vPercentage ) * (sTextureRect.p - sTextureRect.s) - vTexCoord.s + sTextureRect.s; \n" + "float offsetT = rand( vec2(vTexCoord.t*vPercentage, vTexCoord.s * vPercentage) ) * (sTextureRect.q - sTextureRect.t) - vTexCoord.t + sTextureRect.t; \n" + "vec2 lookupCoord = vTexCoord + vec2(offsetS, offsetT) * vPercentage; \n" + "gl_FragColor = texture2D( sTexture, lookupCoord ) * uColor; \n" + "gl_FragColor.a *= 1.0 - vPercentage; \n" + "}" ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom; + if( useHighPrecision ) + { + shaderEffectCustom = Dali::ShaderEffect::New( vertexShader, prefixHighPrecision + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ) ); + } + else + { + shaderEffectCustom = Dali::ShaderEffect::New( vertexShader, prefixMediumPrecision + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID | ShaderEffect::HINT_BLENDING ) ); + } + + /* Pass ownership to DissolveEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::DissolveEffect handle( shaderEffectCustom ); + + handle.SetUniform( DISTORTION_PROPERTY_NAME, 0.0f ); + handle.SetProperty( ShaderEffect::GRID_DENSITY, Property::Value(50.0f) ); + + handle.SetCentralLine( Vector2(1.0f,0.5f), Vector2(-1.0f, 0.0f) ); + + return handle; +} + +void DissolveEffect::SetCentralLine( const Vector2& position, const Vector2& displacement ) +{ + // the line passes through 'position' and has the direction of 'displacement' + float coefA, coefB, coefC; //line equation: Ax+By+C=0; + coefA = displacement.y; + coefB = -displacement.x; + coefC = -displacement.y*position.x + displacement.x*position.y; + + float inversedAABB = 1.f / (coefA*coefA+coefB*coefB); + float inversedSqrtAABB = sqrtf(inversedAABB); + float saddleA; + + //saddle surface(Hyperbolic paraboloid)function, used to calculate the dissolve starting time + //z = y*y/a/a - x*x/b/b + //with our selection of parameters(a and b), this value for any texture coordinate is between -1.0 and 1.0 + + Vector3 saddleParam; // [0]: a*a, [1]: b*b, [2] b + Vector2 translation; + Vector2 rotation; + float toNext = -1.f; + if( displacement.x > 0.f || (EqualsZero(displacement.x) && displacement.y > 0.f) ) + { + toNext = 1.f; + } + + if( (displacement.y * displacement.x < 0.0f) ) + { + //distance from (0,0) to the line + float distanceTopLeft = fabsf(coefC) * inversedSqrtAABB; + //distance from (1, 1 ) to the line + float distanceBottomRight = fabsf(coefA+coefB+coefC) * inversedSqrtAABB; + saddleA = std::max( distanceTopLeft, distanceBottomRight ); + + //foot of a perpendicular: (1,0) to the line + float footX1 = ( coefB*coefB - coefA*coefC) * inversedAABB; + float footY1 = (-coefA*coefB - coefB*coefC) * inversedAABB; + //foot of a perpendicular: (0,1) to the line + float footX2 = (-coefA*coefB - coefA*coefC) * inversedAABB; + float footY2 = ( coefA*coefA - coefB*coefC) * inversedAABB; + saddleParam[1] = (footX1-footX2)*(footX1-footX2) + (footY1-footY2)*(footY1-footY2); + translation = Vector2(-footX2,-footY2); + } + else + { + //distance from(1,0) to the line + float distanceTopRight = fabsf(coefA+coefC) * inversedSqrtAABB; + //distance from(0,1) to the line + float distanceBottomLeft = fabsf(coefB+coefC) * inversedSqrtAABB; + saddleA = std::max( distanceTopRight, distanceBottomLeft ); + //foot of a perpendicular: (0,0) to the line + float footX3 = (-coefA*coefC) * inversedAABB; + float footY3 = (-coefB*coefC) * inversedAABB; + //foot of a perpendicular: (1.0,1.0) to the line + float footX4 = ( coefB*coefB - coefA*coefB - coefA*coefC) * inversedAABB; + float footY4 = (-coefA*coefB + coefA*coefA- coefB*coefC) * inversedAABB; + saddleParam[1] = (footX3-footX4)*(footX3-footX4) + (footY3-footY4)*(footY3-footY4); + translation = Vector2(-footX3, -footY3); + } + + saddleParam[2] = sqrtf(saddleParam[1]); + saddleParam[0] = saddleA*saddleA; + rotation = Vector2(-displacement.x, displacement.y); + rotation.Normalize(); + + SetUniform( "uSaddleParam", saddleParam ); + SetUniform( "uTranslation", translation ); + SetUniform( "uRotation", rotation ); + SetUniform( "uToNext", toNext ); + +} + +void DissolveEffect::SetDistortion( float distortion ) +{ + SetUniform( DISTORTION_PROPERTY_NAME, distortion ); +} + +const std::string& DissolveEffect::GetDistortionPropertyName() const +{ + return DISTORTION_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/dissolve-local-effect.cpp b/dali-toolkit/public-api/shader-effects/dissolve-local-effect.cpp new file mode 100644 index 0000000..5646390 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/dissolve-local-effect.cpp @@ -0,0 +1,167 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#include + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + const std::string DISTORTION_PROPERTY_NAME( "uPercentage" ); + const std::string CENTER_PROPERTY_NAME( "uCenter" ); + const std::string RADIUS_PROPERTY_NAME( "uRadius" ); + const std::string TRANSPARENCY_PROPERTY_NAME( "uTransparency" ); +} + +DissolveLocalEffect::DissolveLocalEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +DissolveLocalEffect::DissolveLocalEffect( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +DissolveLocalEffect::~DissolveLocalEffect() +{ +} + +DissolveLocalEffect DissolveLocalEffect::New( unsigned int numberOfDimples ) +{ + std::ostringstream vertexShaderStringStream; + vertexShaderStringStream << "#define NUMBER_OF_DIMPLE "<< numberOfDimples << "\n"; + std::string vertexShader( + "uniform vec2 uCenter[ NUMBER_OF_DIMPLE ];\n" + "uniform float uRadius[ NUMBER_OF_DIMPLE ]; \n" + "uniform float uPercentage[ NUMBER_OF_DIMPLE ]; \n" + "varying float vPercentage;\n" + "void main()\n" + "{\n" + " vec4 position = uModelView * vec4( aPosition, 1.0 );\n" + " float percentage = 0.0;\n" + " for( int i=0; i + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * DissolveLocalEffect is a custom shader effect to achieve Dissolve effects in multiple small areas of Image actors + */ +class DissolveLocalEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized DissolveLocalEffect; this can be initialized with DissolveLocalEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + DissolveLocalEffect(); + + /** + * Virtual destructor. + */ + virtual ~DissolveLocalEffect(); + + /** + * Create an initialized DissolveLocalEffect. + * @param[in] numberOfDimples The number of dimples + * @return A handle to a newly allocated Dali resource. + */ + static DissolveLocalEffect New( unsigned int numberOfDimples ); + + /** + * Get the number of dimples the shader supports. + * @return The number of dimples in the shader. + */ + unsigned int GetNumberOfDimples() const; + + /** + * Set the transparency of the drifted pixels. + * @param[in] transparency The transparency of the drifted pixels. + */ + void SetTransparency( float transparency); + + /** + * Set the certer position of a dimple. + * @param[in] index The index of the dimple to change. + * @param[in] center The center position of the dimple. + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + void SetCenter( unsigned int index, const Vector2& center ); + + /** + * Set the propogation radius of a dimple. + * @param[in] index The index of the dimple to change. + * @param[in] radius The propagation radius of the dimple. + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + void SetRadius( unsigned int index, float radius ); + + /** + * Sets the distortion applied to the effect texture. + * This value is proportional to the distortion applied; a value of zero means no distortion. + * @param[in] index The index of the dimple to change. + * @param[in] distortion The distortion value. + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + void SetDistortion( unsigned int index, float distortion ); + + /** + * Get the name of the center property of a dimple. + * @param[in] index The index of the dimple. + * @return A std::string containing the property name. + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + std::string GetCenterPropertyName( unsigned int index ) const; + + /** + * Get the name of the radius property of a dimple. + * @param[in] index The index of the dimple. + * @return A std::string containing the property name + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + std::string GetRadiusPropertyName( unsigned int index ) const; + + /** + * Get the name for the distortion property of a dimple + * @param[in] index the index of a dimple. + * @return A std::string containing the property name + * @pre index has to be between 0 and GetNumberOfDimples() - 1 + */ + std::string GetDistortionPropertyName( unsigned int index ) const; + +private: // Not intended for application developers + + DissolveLocalEffect( ShaderEffect handle ); + +private: + + unsigned int mNumberOfDimples; ///< The number of dimples the shader supports + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_LOCAL_DISSOLVE_H__ diff --git a/dali-toolkit/public-api/shader-effects/distance-field-effect.cpp b/dali-toolkit/public-api/shader-effects/distance-field-effect.cpp new file mode 100644 index 0000000..7abddcd --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/distance-field-effect.cpp @@ -0,0 +1,312 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +#define STRINGIFY(...) #__VA_ARGS__ + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ +// generic uniforms +const std::string COLOR_PROPERTY_NAME( "uColor" ); +const std::string SMOOTHING_PROPERTY_NAME( "uSmoothing" ); + +// outline uniforms +const std::string OUTLINE_ENABLE_PROPERTY_NAME( "uDoOutline" ); +const std::string OUTLINECOLOR_PROPERTY_NAME( "uOutlineColor" ); +const std::string OUTLINE_SIZE_PROPERTY_NAME( "uOutlineParams" ); + +// glow related +const std::string GLOW_ENABLE_PROPERTY_NAME( "uDoGlow" ); +const std::string GLOW_COLOR_PROPERTY_NAME( "uGlowColor" ); +const std::string GLOW_BOUNDARY_PROPERTY_NAME( "uGlowBoundary" ); + +// shadow related +const std::string SHADOW_ENABLE_PROPERTY_NAME( "uDoShadow" ); +const std::string SHADOW_COLOR_PROPERTY_NAME( "uShadowColor" ); +const std::string SHADOW_OFFSET_PROPERTY_NAME( "uShadowOffset" ); +} // namespace + +DistanceFieldEffect::DistanceFieldEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +DistanceFieldEffect::DistanceFieldEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +DistanceFieldEffect::~DistanceFieldEffect() +{ +} + +DistanceFieldEffect DistanceFieldEffect::New() +{ + std::string fragmentShaderPrefix( + "#extension GL_OES_standard_derivatives : enable\n" + "\n" + ); + + std::string fragmentShader( + "uniform mediump float uSmoothing;\n" + "uniform mediump float uGlowBoundary;\n" + "uniform mediump vec2 uOutlineParams;\n" + "uniform lowp vec4 uOutlineColor;\n" + "uniform lowp vec4 uShadowColor;\n" + "uniform mediump vec2 uShadowOffset;\n" + "uniform lowp vec4 uGlowColor;\n" + "uniform lowp float uDoOutline;\n" + "uniform lowp float uDoShadow;\n" + "uniform lowp float uDoGlow;\n" + "\n" + "void main()\n" + "{\n" + " // sample distance field\n" + " mediump float distance = texture2D(sTexture, vTexCoord).a;\n" + " mediump float smoothWidth = fwidth(distance);\n" + " mediump float alphaFactor = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, distance);\n" + " lowp vec4 color;\n" + " if (uDoShadow == 0.0)\n" + " {\n" + " mediump float alpha = uColor.a * alphaFactor;\n" + " lowp vec4 rgb = uColor;\n" + "\n" + " if (uDoOutline > 0.0)\n" + " {\n" + " mediump float outlineWidth = uOutlineParams[1] + smoothWidth;\n" + " mediump float outlineBlend = smoothstep(uOutlineParams[0] - outlineWidth, uOutlineParams[0] + outlineWidth, distance);\n" + " alpha = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, distance);\n" + " rgb = mix(uOutlineColor, uColor, outlineBlend);\n" + " }\n" + "\n" + " if (uDoGlow > 0.0)\n" + " {\n" + " rgb = mix(uGlowColor, rgb, alphaFactor);\n" + " alpha = smoothstep(uGlowBoundary, uSmoothing, distance);\n" + " }\n" + "\n" + " // set fragment color\n" + " color = vec4(rgb.rgb, alpha);\n" + " }\n" + "\n" + " else // (uDoShadow > 0.0)\n" + " {\n" + " float shadowDistance = texture2D(sTexture, vTexCoord - uShadowOffset).a;\n" + " mediump float inText = alphaFactor;\n" + " mediump float inShadow = smoothstep(uSmoothing - smoothWidth, uSmoothing + smoothWidth, shadowDistance);\n" + "\n" + " // inside object, outside shadow\n" + " if (inText == 1.0)\n" + " {\n" + " color = uColor;\n" + " }\n" + " // inside object, outside shadow\n" + " else if ((inText != 0.0) && (inShadow == 0.0))\n" + " {\n" + " color = uColor;\n" + " color.a *= inText;\n" + " }\n" + " // outside object, completely inside shadow\n" + " else if ((inText == 0.0) && (inShadow == 1.0))\n" + " {\n" + " color = uShadowColor;\n" + " }\n" + " // inside object, completely inside shadow\n" + " else if ((inText != 0.0) && (inShadow == 1.0))\n" + " {\n" + " color = mix(uShadowColor, uColor, inText);\n" + " color.a = uShadowColor.a;\n" + " }\n" + " // inside object, inside shadow's border\n" + " else if ((inText != 0.0) && (inShadow != 0.0))\n" + " {\n" + " color = mix(uShadowColor, uColor, inText);\n" + " color.a *= max(inText, inShadow);\n" + " }\n" + " // inside shadow's border\n" + " else if (inShadow != 0.0)\n" + " {\n" + " color = uShadowColor;\n" + " color.a *= inShadow;\n" + " }\n" + " // outside shadow and object\n" + " else \n" + " {\n" + " color.a = 0.0;\n" + " }\n" + "\n" + " }\n" + "\n" + " gl_FragColor = color;\n" + "\n" + "}\n" + ); + + // Create the implementation, temporarily owned on stack + Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::NewWithPrefix("", "", + fragmentShaderPrefix, fragmentShader, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING)); + + /* Pass ownership to DistanceFieldEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + + // TODO: move default values to... Constants? + Dali::Toolkit::DistanceFieldEffect handle( shaderEffect ); + + handle.SetSmoothingEdge(0.5f); + handle.SetOutlineColor(Color::BLACK); + handle.SetOutlineParams(Vector2(0.51f, 0.0f)); + handle.SetGlowBoundary(0.4f); + handle.SetGlowColor(Color::GREEN); + handle.SetShadowColor(Vector4(0.0f, 0.0f, 0.0f, 0.4f)); + + // TODO: find a way to set the shadow offset in texel coordinates instead of UVs. + handle.SetShadowOffset(Vector2(0.05f, 0.05f)); + + // Default: + handle.SetOutline(false); + handle.SetGlow(false); + handle.SetShadow(false); + + return handle; + +} + +void DistanceFieldEffect::SetGlowColor(const Vector4& color) +{ + SetUniform(GLOW_COLOR_PROPERTY_NAME, color); +} + +void DistanceFieldEffect::SetGlow(bool glowEnable) +{ + const float a = glowEnable ? 1.0f : 0.0f; + SetUniform(GLOW_ENABLE_PROPERTY_NAME, a); +} + +void DistanceFieldEffect::SetGlowBoundary(float glowBoundary) +{ + SetUniform(GLOW_BOUNDARY_PROPERTY_NAME, glowBoundary); +} + +void DistanceFieldEffect::SetOutline(bool outlineEnable) +{ + const float a = outlineEnable ? 1.0f : 0.0f; + SetUniform(OUTLINE_ENABLE_PROPERTY_NAME, a); +} + +void DistanceFieldEffect::SetOutlineColor(const Vector4& color) +{ + SetUniform(OUTLINECOLOR_PROPERTY_NAME, color); +} + +void DistanceFieldEffect::SetOutlineParams(const Vector2& outlineParams) +{ + SetUniform(OUTLINE_SIZE_PROPERTY_NAME, outlineParams); +} + +void DistanceFieldEffect::SetShadow(bool shadowEnable) +{ + if (shadowEnable) + { + SetGlow(false); + SetOutline(false); + } + + const float a = shadowEnable ? 1.0f : 0.0f; + SetUniform(SHADOW_ENABLE_PROPERTY_NAME, a); +} + +void DistanceFieldEffect::SetShadowColor(const Vector4& color) +{ + SetUniform(SHADOW_COLOR_PROPERTY_NAME, color); +} + +void DistanceFieldEffect::SetShadowOffset(const Vector2& offset) +{ + SetUniform(SHADOW_OFFSET_PROPERTY_NAME, offset); +} + +void DistanceFieldEffect::SetSmoothingEdge(const float smoothing) +{ + SetUniform(SMOOTHING_PROPERTY_NAME, smoothing); +} + +const std::string& DistanceFieldEffect::GetColorPropertyName() const +{ + return COLOR_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetOutlineColorPropertyName() const +{ + return OUTLINECOLOR_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetShadowColorPropertyName() const +{ + return SHADOW_COLOR_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetShadowOffsetPropertyName() const +{ + return SHADOW_OFFSET_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetGlowColorPropertyName() const +{ + return GLOW_COLOR_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetGlowBoundaryPropertyName() const +{ + return GLOW_BOUNDARY_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetOutlineSizePropertyName() const +{ + return OUTLINE_SIZE_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetOutlineEnablePropertyName() const +{ + return OUTLINE_ENABLE_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetGlowEnablePropertyName() const +{ + return GLOW_ENABLE_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetShadowEnablePropertyName() const +{ + return SHADOW_ENABLE_PROPERTY_NAME; +} + +const std::string& DistanceFieldEffect::GetSmoothingPropertyName() const +{ + return SMOOTHING_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/distance-field-effect.h b/dali-toolkit/public-api/shader-effects/distance-field-effect.h new file mode 100644 index 0000000..cc3fb69 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/distance-field-effect.h @@ -0,0 +1,196 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_DISTANCEFIELD_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_DISTANCEFIELD_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * DistanceFieldEffect is a custom shader effect to achieve distance field on Image actors + */ +class DistanceFieldEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized DistanceFieldEffect; this can be initialized with DistanceFieldEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + DistanceFieldEffect(); + + /** + * Virtual destructor. + */ + virtual ~DistanceFieldEffect(); + + /** + * Create an initialized DistanceFieldEffect. + * @return A handle to a newly allocated Dali resource. + */ + static DistanceFieldEffect New(); + + /** + * Set the shadow state + * @param[in] shadowEnable value - true, enable shadow. + * @note Shadow cannot be used with glow/and or outline. + */ + void SetShadow(bool shadowEnable); + + /** + * Set the shadow color multiplier (e.g. output RGB) + * @param[in] color Shadow color value + */ + void SetShadowColor(const Vector4& color); + + /** + * Set the shadow offset + * @param[in] color shadow offset value + */ + void SetShadowOffset(const Vector2& color); + + /** + * Set the glow state + * @param[in] glowEnable value - true, enable glow. + */ + + void SetGlow(bool glowEnable); + + /** + * Set the glow color multiplier (e.g. output RGB) + * @param[in] color Glow color value + */ + void SetGlowColor(const Vector4& color); + + /** + * Set the glow boundary factor + * @param[in] glowBoundary glow boundary + */ + void SetGlowBoundary(float glowBoundary); + + /** + * Set the outline state + * @param[in] outlineEnable value - true, enable outline. + */ + + void SetOutline(bool outlineEnable); + + /** + * Set the outline color multiplier (e.g. output RGB) + * @param[in] color Outline color value + */ + void SetOutlineColor(const Vector4& color); + + /** + * Sets the outline parameters. + * @param[in] outlineParams Thickness of outline. The outline thickness is determined by two parameters. + * params[0] (0-1) Specifies the distance field value for the center of the outline. + * 0 <= params[0] <= 1 + * params[1] (0-1) Specifies the softness/width/anti-aliasing of the outlines inner edge. + * 0 <= params[0] <= 1 + */ + void SetOutlineParams(const Vector2& outlineParams); + + /** + * Set soft edge smoothing + * @param[in] smoothing Specify the distance field value for the center of the edge. + * 0 <= params <= 1 + */ + void SetSmoothingEdge(float smoothing); + + /** + * Get the name for the outline-enable property + * @return A std::string containing the property name + */ + const std::string& GetOutlineEnablePropertyName() const; + + /** + * Get the name for the glow-enable property + * @return A std::string containing the property name + */ + const std::string& GetGlowEnablePropertyName() const; + + /** + * Get the name for the shadow-enable property + * @return A std::string containing the property name + */ + const std::string& GetShadowEnablePropertyName() const; + + /** + * Get the name for the radius property + * @return A std::string containing the property name + */ + const std::string& GetColorPropertyName() const; + + /** + * Get the name for the smoothing property + * @return A std::string containing the property name + */ + const std::string& GetSmoothingPropertyName() const; + + /** + * Get the name for the outline color property + * @return A std::string containing the property name + */ + const std::string& GetOutlineColorPropertyName() const; + + /** + * Get the name for the outline size property + * @return A std::string containing the property name + */ + const std::string& GetOutlineSizePropertyName() const; + + /** + * Get the name for the shadow color property + * @return A std::string containing the property name + */ + const std::string& GetShadowColorPropertyName() const; + + /** + * Get the name for the shadow offset property + * @return A std::string containing the property name + */ + const std::string& GetShadowOffsetPropertyName() const; + + /** + * Get the name for the glow color property + * @return A std::string containing the property name + */ + const std::string& GetGlowColorPropertyName() const; + + /** + * Get the name for the glow boundary property + * @return A std::string containing the property name + */ + const std::string& GetGlowBoundaryPropertyName() const; + +private: + DistanceFieldEffect(ShaderEffect handle); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_SPOT_H__ diff --git a/dali-toolkit/public-api/shader-effects/image-region-effect.cpp b/dali-toolkit/public-api/shader-effects/image-region-effect.cpp new file mode 100644 index 0000000..18bb1c3 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/image-region-effect.cpp @@ -0,0 +1,100 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string TOP_LEFT_PROPERTY_NAME( "uTopLeft" ); +const std::string BOTTOM_RIGHT_PROPERTY_NAME( "uBottomRight" ); + +} // namespace + +ImageRegionEffect::ImageRegionEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +ImageRegionEffect::ImageRegionEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +ImageRegionEffect::~ImageRegionEffect() +{ +} + + +ImageRegionEffect ImageRegionEffect::New() +{ + std::string vertexShader( + "uniform vec2 uTopLeft;\n" + "uniform vec2 uBottomRight;\n" + "void main()\n" + "{\n" + " vec4 position = vec4(aPosition,1.0);\n" + " gl_Position = uMvpMatrix * position;\n" + // The line below is doing the same as the following commented lines: + //" vec2 imageSize = sTextureRect.zw - sTextureRect.xy;\n" + //" vec2 topLeft = sTextureRect.xy + uTopLeft * imageSize;\n" + //" vec2 bottomRight = sTextureRect.xy + uBottomRight * imageSize;\n" + //" vec2 texCoord = (aTexCoord - sTextureRect.xy) / imageSize;\n" + //" vTexCoord = topLeft + texCoord * ( bottomRight - topLeft );\n" + " vTexCoord = sTextureRect.xy + uTopLeft * ( sTextureRect.zw - sTextureRect.xy ) + ( aTexCoord - sTextureRect.xy ) * ( uBottomRight - uTopLeft );\n" + "}\n" + ); + + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( vertexShader, "" ); + + Dali::Toolkit::ImageRegionEffect handle( shaderEffectCustom ); + + handle.SetUniform( TOP_LEFT_PROPERTY_NAME, Vector2( 0.f, 0.f ) ); + handle.SetUniform( BOTTOM_RIGHT_PROPERTY_NAME, Vector2( 1.f, 1.f ) ); + + return handle; +} + +void ImageRegionEffect::SetTopLeft(const Vector2& point) +{ + SetUniform( TOP_LEFT_PROPERTY_NAME, point ); +} + +void ImageRegionEffect::SetBottomRight(const Vector2& point) +{ + SetUniform( BOTTOM_RIGHT_PROPERTY_NAME, point ); +} + +const std::string& ImageRegionEffect::GetTopLeftPropertyName() const +{ + return TOP_LEFT_PROPERTY_NAME; +} + +const std::string& ImageRegionEffect::GetBottomRightPropertyName() const +{ + return BOTTOM_RIGHT_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali + diff --git a/dali-toolkit/public-api/shader-effects/iris-effect.cpp b/dali-toolkit/public-api/shader-effects/iris-effect.cpp new file mode 100644 index 0000000..7c49ab9 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/iris-effect.cpp @@ -0,0 +1,120 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ +const std::string RADIUS_PROPERTY_NAME( "uRadius" ); +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string BLEND_FACTOR_PROPERTY_NAME( "uBlendFactor" ); +} // namespace + +IrisEffect::IrisEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +IrisEffect::IrisEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +IrisEffect::~IrisEffect() +{ +} + +IrisEffect IrisEffect::New() +{ + // append the default version + std::string vertexShader( + "uniform mediump vec2 uCenter;\n" + "varying vec2 vRelativePosition;\n" + "\n" + "void main()\n" + "{\n" + " vec4 world = uModelView * vec4(aPosition,1.0);\n" + " gl_Position = uProjection * world;\n" + " \n" + " vTexCoord = aTexCoord;\n" + " vRelativePosition = aTexCoord - uCenter;\n" + "}\n"); + + std::string fragmentShader( + "uniform float uRadius; \n" + "uniform float uBlendFactor; \n" + "varying vec2 vRelativePosition; \n" + "void main() \n" + "{ \n" + " float delta = (length(vRelativePosition) - uRadius); \n" + " delta = clamp(0.0 - delta * uBlendFactor, 0.0, 1.0); \n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * uColor; \n" + " gl_FragColor.a *= delta; \n" + "} \n" + ); + + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + vertexShader, + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING )); + + Dali::Toolkit::IrisEffect handle( shaderEffectCustom ); + handle.SetUniform( RADIUS_PROPERTY_NAME, 0.0f ); + handle.SetUniform( BLEND_FACTOR_PROPERTY_NAME, 100.0f ); + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.5f, 0.5f) ); + return handle; +} + +void IrisEffect::SetRadius( float radius ) +{ + SetUniform( RADIUS_PROPERTY_NAME, radius ); +} + +void IrisEffect::SetBlendFactor(float value ) +{ + SetUniform( BLEND_FACTOR_PROPERTY_NAME, value ); +} + +void IrisEffect::SetCenter( const Vector2& center ) +{ + SetUniform( CENTER_PROPERTY_NAME, center ); +} + +const std::string& IrisEffect::GetRadiusPropertyName() const +{ + return RADIUS_PROPERTY_NAME; +} + +const std::string& IrisEffect::GetBlendFactorPropertyName() const +{ + return BLEND_FACTOR_PROPERTY_NAME; +} + +const std::string& IrisEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + + +} +} + diff --git a/dali-toolkit/public-api/shader-effects/mask-effect.cpp b/dali-toolkit/public-api/shader-effects/mask-effect.cpp new file mode 100644 index 0000000..918b365 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/mask-effect.cpp @@ -0,0 +1,61 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +MaskEffect::MaskEffect() +{ +} + +MaskEffect::~MaskEffect() +{ +} + +MaskEffect MaskEffect::New( Image maskImage ) +{ + const char* ALPHA_MASK_FRAGMENT_SHADER_SOURCE = + "void main() \n" + "{ \n" + " highp vec4 mask = texture2D(sEffect, vTexCoord); \n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * uColor * vec4(1,1,1,mask.a); \n" + "} \n"; + + ShaderEffect shader = ShaderEffect::New( "", // Use default + ALPHA_MASK_FRAGMENT_SHADER_SOURCE, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) ); + + shader.SetEffectImage( maskImage ); + + return MaskEffect( shader ); +} + +//Call the Parent copy constructor to add reference to the implementation for this object +MaskEffect::MaskEffect( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/mirror-effect.cpp b/dali-toolkit/public-api/shader-effects/mirror-effect.cpp new file mode 100644 index 0000000..4b72f0f --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/mirror-effect.cpp @@ -0,0 +1,122 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string DEPTH_PROPERTY_NAME( "uDepth" ); +const std::string ALPHA_PROPERTY_NAME( "uAlpha" ); + +} // namespace + +MirrorEffect::MirrorEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +MirrorEffect::MirrorEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +MirrorEffect::~MirrorEffect() +{ +} + +MirrorEffect MirrorEffect::New() +{ + + std::string vertextShader( + "precision mediump float; \n" + "void main() \n" + "{ \n" + " vec3 pos = aPosition; \n" + " pos.y = pos.y * 3.0; \n" + " vec4 world = uModelView * vec4(pos,1.0); \n" + " gl_Position = uProjection * world; \n" + " vTexCoord = aTexCoord; \n" + "} \n" ); + + std::string fragmentShader( + "uniform float uDepth; \n" + "uniform float uAlpha; \n" + "void main() \n" + "{ \n" + " if(vTexCoord.y < 1.0 / 3.0) \n" + " { \n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n" + " } \n" + " else if(vTexCoord.y < 2.0 / 3.0) \n" + " { \n" + " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, vTexCoord.y * 3.0 - 1.0)) * uColor; \n" + " gl_FragColor.a *= uAlpha; \n" + " } \n" + " else \n" + " { \n" + " float darkness = 3.0 - vTexCoord.y * 3.0; \n" + " darkness = (1.0 - 1.0 / uDepth + darkness * 1.0/ uDepth) * 0.65; \n" + " vec4 color = texture2D(sTexture, vec2(vTexCoord.x, -vTexCoord.y *3.0 + 3.0)) * uColor; \n" + " color.a *= uAlpha; \n" + " gl_FragColor = color * vec4(darkness, darkness, darkness, darkness); \n" + " } \n" + "} \n" ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + vertextShader, + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING )); + + /* Pass ownership to MirrorEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::MirrorEffect handle( shaderEffectCustom ); + handle.SetUniform(ALPHA_PROPERTY_NAME, 1.0f); + handle.SetUniform(DEPTH_PROPERTY_NAME, 0.5f); + return handle; +} + +void MirrorEffect::SetDepth( float depth ) +{ + SetUniform( DEPTH_PROPERTY_NAME, depth ); +} + +void MirrorEffect::SetAlpha( float alpha ) +{ + SetUniform( ALPHA_PROPERTY_NAME, alpha ); +} + +const std::string& MirrorEffect::GetDepthPropertyName() const +{ + return DEPTH_PROPERTY_NAME; +} + +const std::string& MirrorEffect::GetAlphaPropertyName() const +{ + return ALPHA_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/mirror-effect.h b/dali-toolkit/public-api/shader-effects/mirror-effect.h new file mode 100644 index 0000000..42af6af --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/mirror-effect.h @@ -0,0 +1,88 @@ +#ifndef __DALI_TOOLKIT_MIRROR_EFFECT_H__ +#define __DALI_TOOLKIT_MIRROR_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * MirrorEffect is a custom shader effect to achieve square effects in Image actors + */ +class MirrorEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized MirrorEffect; this can be initialized with MirrorEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + MirrorEffect(); + + /** + * Virtual destructor. + */ + virtual ~MirrorEffect(); + + /** + * Create an initialized MirrorEffect. + * @return A handle to a newly allocated Dali resource. + */ + static MirrorEffect New(); + + /** + * Set the depth of the mirror effect. + * @param [in] depth The new mirror depth value. + */ + void SetDepth(float depth); + + /** + * Set the alpha of the mirror effect. + * @param [in] alpha The new mirror alpha value. + */ + void SetAlpha(float alpha); + + /** + * Get the name for the depth property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetDepthPropertyName() const; + + /** + * Get the name for the alpha property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetAlphaPropertyName() const; + +private: // Not intended for application developers + MirrorEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_MIRROR_EFFECT_H__ + diff --git a/dali-toolkit/public-api/shader-effects/motion-blur-effect.cpp b/dali-toolkit/public-api/shader-effects/motion-blur-effect.cpp new file mode 100644 index 0000000..c28eaa9 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/motion-blur-effect.cpp @@ -0,0 +1,328 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string MOTION_BLUR_TEXCOORD_SCALE_PROPERTY_NAME( "uBlurTexCoordScale" ); +const std::string MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR_PROPERTY_NAME( "uGeometryStretchFactor" ); +const std::string MOTION_BLUR_SPEED_SCALING_FACTOR_PROPERTY_NAME( "uSpeedScalingFactor" ); +const std::string MOTION_BLUR_OBJECT_FADE_START_PROPERTY_NAME( "uObjectFadeStart" ); +const std::string MOTION_BLUR_OBJECT_FADE_END_PROPERTY_NAME( "uObjectFadeEnd" ); +const std::string MOTION_BLUR_ALPHA_SCALE_PROPERTY_NAME( "uAlphaScale" ); +const std::string MOTION_BLUR_NUM_SAMPLES_NAME( "uNumSamples" ); +const std::string MOTION_BLUR_RECIP_NUM_SAMPLES_NAME( "uRecipNumSamples" ); +const std::string MOTION_BLUR_RECIP_NUM_SAMPLES_MINUS_ONE_NAME( "uRecipNumSamplesMinusOne" ); +const std::string MOTION_BLUR_MODEL_LASTFRAME( "uModelLastFrame" ); ///< Matrix + +//////////////////////////////////////////////////// +// +// Motion blur shader / actor tweaking parameters +// + +const unsigned int MOTION_BLUR_NUM_SAMPLES = 8; + +// half width and half height respectively of actor, corresponding to values in vertex attribute stream +// TODO: Note that these values work for normal image actor (verts +/- 0.5) but a grid or a nine square seems to have verts in pixel space (e.g. 256,256). Need to fix this somehow, +// either in Dali or by passing uniforms which we can use to 'normalise' the verts in the vertex shader +const Vector2 MOTION_BLUR_ACTOR_VERTEX( 0.5f, 0.5f ); + +const float MOTION_BLUR_TEXCOORD_SCALE = 0.125f; // stretch texture reads along velocity vector, larger number means reads spaced further apart +const float MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR = 0.05f; // scaling factor for how much to stretch actor geom as it moves +const float MOTION_BLUR_SPEED_SCALING_FACTOR = 0.5f; // scales the speed, producing a number affecting how much the actor blurs & fades at the edges + +const Vector2 MOTION_BLUR_OBJECT_FADE_END( MOTION_BLUR_ACTOR_VERTEX ); // distance from center at which actor fully fades to zero alpha +const Vector2 MOTION_BLUR_OBJECT_FADE_START( MOTION_BLUR_OBJECT_FADE_END * 0.5f ); // distance from center at which actor start to fade from full alpha + +const float MOTION_BLUR_ALPHA_SCALE = 0.75f; // global scaler applied to actor alpha as it is blurred + moving + +} // namespace + + +MotionBlurEffect::MotionBlurEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +MotionBlurEffect::MotionBlurEffect( ShaderEffect handle ) +:ShaderEffect( handle ) +{ +} + +MotionBlurEffect::~MotionBlurEffect() +{ +} + +MotionBlurEffect MotionBlurEffect::Apply( Actor handle ) +{ + MotionBlurEffect newEffect = New( MOTION_BLUR_NUM_SAMPLES ); + handle.SetShaderEffect( newEffect ); + + Property::Index uModelProperty = newEffect.GetPropertyIndex( MOTION_BLUR_MODEL_LASTFRAME ); + + Constraint constraint = Constraint::New( uModelProperty, + Source( handle, Actor::WORLD_MATRIX ), + EqualToConstraint() ); + + // and set up constraint. + newEffect.ApplyConstraint( constraint ); + return newEffect; +} + +MotionBlurEffect MotionBlurEffect::New() +{ + return New( MOTION_BLUR_NUM_SAMPLES ); +} + +MotionBlurEffect MotionBlurEffect::New( const unsigned int numBlurSamples ) +{ + // Dali vertexSource prefix for reference: + // precision highp float; + // attribute vec3 aPosition; + // attribute vec2 aTexCoord; + // uniform mat4 uMvpMatrix; + // uniform mat4 uModelView; + // uniform mat3 uNormalMatrix; + // uniform mat4 uProjection; + // uniform vec4 uColor; + // varying vec2 vTexCoord; + std::string vertexSource; + vertexSource = + "uniform mat4 uModelLastFrame;\n" + "uniform float uTimeDelta;\n" + + "uniform float uGeometryStretchFactor;\n" + "uniform float uSpeedScalingFactor;\n" + + // outputs + "varying vec2 vModelSpaceCenterToPos;\n" + "varying vec2 vScreenSpaceVelocityVector;\n" + "varying float vSpeed;\n" + + "void main()\n" + "{\n" + // get view space position of vertex this frame and last frame + " vec4 vertex = vec4(aPosition, 1.0);\n" + " vec4 viewSpaceVertex = uModelView * vertex;\n" + " vec4 viewSpaceVertexLastFrame = (uViewMatrix * uModelLastFrame) * vertex;\n" + " float reciprocalTimeDelta = 1.0 / ((uTimeDelta > 0.0) ? uTimeDelta : 0.01);\n" + + // work out vertex's last movement in view space + " vec3 viewSpacePosDelta = viewSpaceVertex.xyz - viewSpaceVertexLastFrame.xyz;\n" + + // get clip space position of vertex this frame and last frame + " vec4 clipSpaceVertex = uMvpMatrix * vertex;\n" + " vec4 clipSpaceVertexLastFrame = uProjection * viewSpaceVertexLastFrame;\n" + + // decide how much this vertex is 'trailing', i.e. at the back of the object relative to its direction of motion. We do this + // by assuming the objects model space origin is at its center and taking the dot product of the vector from center to vertex with the motion direction + " float t = 0.0;\n" + " float posDeltaLength = length(viewSpacePosDelta);\n" + " if(posDeltaLength > 0.001)\n" // avoid div by 0 if object has barely moved + " {\n" + " vec4 viewSpaceCenterToPos = uModelView * vec4(aPosition, 0.0);\n" + " float centerToVertexDist = length(viewSpaceCenterToPos);\n" + " if(centerToVertexDist > 0.001)\n" // avoid div by 0 if object has vertex at model space origin + " {\n" + " vec3 viewSpacePosDeltaNormalised = viewSpacePosDelta / posDeltaLength;\n" + " vec3 viewSpaceCenterToPosNormalised = viewSpaceCenterToPos.xyz / centerToVertexDist;\n" + " t = (dot(viewSpacePosDeltaNormalised, viewSpaceCenterToPosNormalised) * 0.5 ) + 0.5;\n" // scale and bias from [-1..1] to [0..1] + " }\n" + " }\n" + // output vertex position lerped with its last position, based on how much it is trailing, + // this stretches the geom back along where it has just been, giving a warping effect + // Note: we must take account of time delta to convert position delta into a velocity, so changes are smooth (take into account frame time correctly) + " gl_Position = mix(clipSpaceVertexLastFrame, clipSpaceVertex, t * uGeometryStretchFactor * reciprocalTimeDelta);\n" + + // work out vertex's last movement in normalised device coordinates [-1..1] space, i.e. perspective divide + " vec2 ndcVertex = clipSpaceVertex.xy / clipSpaceVertex.w;\n" + " vec2 ndcVertexLastFrame = clipSpaceVertexLastFrame.xy / clipSpaceVertexLastFrame.w;\n" + // scale and bias so that a value of 1.0 corresponds to screen size (NDC is [-1..1] = 2) + " vScreenSpaceVelocityVector = ((ndcVertex - ndcVertexLastFrame) * 0.5 * reciprocalTimeDelta);\n" + " vScreenSpaceVelocityVector.y = -vScreenSpaceVelocityVector.y;\n" // TODO negated due to y being inverted in our coordinate system? + // calculate a scaling factor proportional to velocity, which we can use to tweak how things look + " vSpeed = length(vScreenSpaceVelocityVector) * uSpeedScalingFactor;\n" + " vSpeed = clamp(vSpeed, 0.0, 1.0);\n" + + // provide fragment shader with vector from center of object to pixel (assumes the objects model space origin is at its center and verts have same z) + " vModelSpaceCenterToPos = aPosition.xy;\n" + + " vTexCoord = aTexCoord;\n" + "}\n"; + + + // Dali fragmentSource prefix for reference: + // precision highp float; + // uniform sampler2D sTexture; + // uniform sampler2D sEffect; + // uniform vec4 uColor; + // varying vec2 vTexCoord; + std::string fragmentSource; + fragmentSource = + "precision mediump float;\n" + + "uniform vec2 uObjectFadeStart;\n" + "uniform vec2 uObjectFadeEnd;\n" + "uniform float uAlphaScale;\n" + "uniform float uBlurTexCoordScale;\n" + "uniform float uNumSamples;\n" + "uniform float uRecipNumSamples;\n" + "uniform float uRecipNumSamplesMinusOne;\n" + // inputs + "varying vec2 vModelSpaceCenterToPos;\n" + "varying vec2 vScreenSpaceVelocityVector;\n" + "varying float vSpeed;\n" + + "void main()\n" + "{\n" + // calculate an alpha value that will fade the object towards its extremities, we need this to avoid an unsightly hard edge between color values of + // the blurred object and the unblurred background. Use smoothstep also to hide any hard edges (discontinuities) in rate of change of this alpha gradient + " vec2 centerToPixel = abs(vModelSpaceCenterToPos);\n" + " vec2 fadeToEdges = smoothstep(0.0, 1.0, 1.0 - ((centerToPixel - uObjectFadeStart) / (uObjectFadeEnd - uObjectFadeStart)));\n" + " float fadeToEdgesScale = fadeToEdges.x * fadeToEdges.y * uAlphaScale;\n" // apply global scaler + " fadeToEdgesScale = mix(1.0, fadeToEdgesScale, vSpeed);\n" // fade proportional to speed, so opaque when at rest + + // scale velocity vector by user requirements + " vec2 velocity = vScreenSpaceVelocityVector * uBlurTexCoordScale;\n" + + // standard actor texel + " vec4 colActor = texture2D(sTexture, vTexCoord);\n" + + // blurred actor - gather texture samples from the actor texture in the direction of motion + " vec4 col = colActor * uRecipNumSamples;\n" + " for(float i = 1.0; i < uNumSamples; i += 1.0)\n" + " {\n" + " float t = i * uRecipNumSamplesMinusOne;\n" + " col += texture2D(sTexture, vTexCoord + (velocity * t)) * uRecipNumSamples;\n" + " }\n" + " gl_FragColor = mix(colActor, col, vSpeed);\n" // lerp blurred and non-blurred actor based on speed of motion + " gl_FragColor.a = colActor.a * fadeToEdgesScale;\n" // fade blurred actor to its edges based on speed of motion + " gl_FragColor *= uColor;\n" + "}\n"; + + // NOTE: we must turn on alpha blending for the actor (HINT_BLENDING) + ShaderEffect shader = ShaderEffect::New( vertexSource, + fragmentSource, + GEOMETRY_TYPE_IMAGE, + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID) ); + + MotionBlurEffect handle( shader ); + + + ////////////////////////////////////// + // Register uniform properties + // + // + + // factors that scale the look, defaults + handle.SetUniform( MOTION_BLUR_TEXCOORD_SCALE_PROPERTY_NAME, MOTION_BLUR_TEXCOORD_SCALE ); + handle.SetUniform( MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR_PROPERTY_NAME, MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR ); + handle.SetUniform( MOTION_BLUR_SPEED_SCALING_FACTOR_PROPERTY_NAME, MOTION_BLUR_SPEED_SCALING_FACTOR ); + handle.SetUniform( MOTION_BLUR_OBJECT_FADE_START_PROPERTY_NAME, MOTION_BLUR_OBJECT_FADE_START ); + handle.SetUniform( MOTION_BLUR_OBJECT_FADE_END_PROPERTY_NAME, MOTION_BLUR_OBJECT_FADE_END ); + handle.SetUniform( MOTION_BLUR_ALPHA_SCALE_PROPERTY_NAME, MOTION_BLUR_ALPHA_SCALE ); + handle.SetUniform( MOTION_BLUR_NUM_SAMPLES_NAME, static_cast( MOTION_BLUR_NUM_SAMPLES ) ); + handle.SetUniform( MOTION_BLUR_RECIP_NUM_SAMPLES_NAME, 1.0f / static_cast( MOTION_BLUR_NUM_SAMPLES ) ); + handle.SetUniform( MOTION_BLUR_RECIP_NUM_SAMPLES_MINUS_ONE_NAME, 1.0f / static_cast( MOTION_BLUR_NUM_SAMPLES - 1.0f ) ); + handle.SetUniform( MOTION_BLUR_MODEL_LASTFRAME, Matrix::IDENTITY ); + + return handle; +} + +MotionBlurEffect MotionBlurEffect::DownCast( ShaderEffect shaderEffect ) +{ + MotionBlurEffect handle = shaderEffect; + return handle; +} + +void MotionBlurEffect::SetNumSamples( int numSamples ) +{ + SetUniform( MOTION_BLUR_NUM_SAMPLES_NAME, static_cast( numSamples ) ); + SetUniform( MOTION_BLUR_RECIP_NUM_SAMPLES_NAME, 1.0f / static_cast( numSamples ) ); + SetUniform( MOTION_BLUR_RECIP_NUM_SAMPLES_MINUS_ONE_NAME, 1.0f / static_cast( numSamples - 1.0f ) ); +} + +void MotionBlurEffect::SetTexcoordScale( float texcoordScale ) +{ + SetUniform( MOTION_BLUR_TEXCOORD_SCALE_PROPERTY_NAME, texcoordScale ); +} + +void MotionBlurEffect::SetGeometryStretchFactor( float scalingFactor ) +{ + SetUniform( MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR_PROPERTY_NAME, scalingFactor ); +} + +void MotionBlurEffect::SetSpeedScalingFactor( float scalingFactor ) +{ + SetUniform( MOTION_BLUR_SPEED_SCALING_FACTOR_PROPERTY_NAME, scalingFactor ); +} + +void MotionBlurEffect::SetObjectFadeStart( Vector2 displacement ) +{ + SetUniform( MOTION_BLUR_OBJECT_FADE_START_PROPERTY_NAME, displacement ); +} + +void MotionBlurEffect::SetObjectFadeEnd( Vector2 displacement ) +{ + SetUniform( MOTION_BLUR_OBJECT_FADE_END_PROPERTY_NAME, displacement ); +} + +void MotionBlurEffect::SetAlphaScale( float alphaScale ) +{ + SetUniform( MOTION_BLUR_ALPHA_SCALE_PROPERTY_NAME, alphaScale ); +} + +const std::string& MotionBlurEffect::GetTexcoordScalePropertyName() const +{ + return MOTION_BLUR_TEXCOORD_SCALE_PROPERTY_NAME; +} + +const std::string& MotionBlurEffect::GetGeometryStretchFactorPropertyName() const +{ + return MOTION_BLUR_GEOM_STRETCH_SCALING_FACTOR_PROPERTY_NAME; +} + +const std::string& MotionBlurEffect::GetSpeedScalingFactorPropertyName() const +{ + return MOTION_BLUR_SPEED_SCALING_FACTOR_PROPERTY_NAME; +} + +const std::string& MotionBlurEffect::GetObjectFadeStartPropertyName() const +{ + return MOTION_BLUR_OBJECT_FADE_START_PROPERTY_NAME; +} + +const std::string& MotionBlurEffect::GetObjectFadeEndPropertyName() const +{ + return MOTION_BLUR_OBJECT_FADE_END_PROPERTY_NAME; +} + +const std::string& MotionBlurEffect::GetAlphaScalePropertyName() const +{ + return MOTION_BLUR_ALPHA_SCALE_PROPERTY_NAME; +} + +} + +} + diff --git a/dali-toolkit/public-api/shader-effects/motion-blur-effect.h b/dali-toolkit/public-api/shader-effects/motion-blur-effect.h new file mode 100644 index 0000000..3099d48 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/motion-blur-effect.h @@ -0,0 +1,213 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_MOTION_BLUR_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_MOTION_BLUR_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * + * Class for motion blur shader that works on a per object basis. Objects will + * blur when they move, or if the camera moves. Can be applied to ImageActor or + * TextActor only. + * + * Usage example:- + * + * // Create shader used for doing motion blur\n + * MotionBlurEffect MotionBlurEffect = MotionBlurEffect::New(); + * + * // set actor shader to the blur one\n + * Actor Actor = Actor::New( ... );\n + * Actor.SetShaderEffect( MotionBlurEffect ); + * + */ +class MotionBlurEffect : public ShaderEffect +{ + +public: + + /** + * Create an uninitialized MotionBlurEffect; this can be initialized with MotionBlurEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + MotionBlurEffect(); + + /** + * Virtual destructor. + */ + virtual ~MotionBlurEffect(); + + /** + * Create an initialized MotionBlurEffect + * The number of texture samples taken along the motion velocity vector of the + * actor, producing the blur, is set to a default of 8. + * @return A handle to a newly allocated Dali resource. + */ + static MotionBlurEffect New(); + + /** + * Create a MotionBlurEffect and attach it to the specified actor + * The number of texture samples taken along the motion velocity vector of the + * actor, producing the blur, is set to a default of 8. + * @return A handle to a newly allocated Dali resource. + */ + static MotionBlurEffect Apply( Actor handle ); + + /** + * Create an initialized MotionBlurEffect + * @param numBlurSamples The number of texture samples taken along the motion + * velocity vector of the actor, producing the blur. A higher number gives a + * smoother blur but costs more in terms of performance. + * @return A handle to a newly allocated Dali resource. + */ + static MotionBlurEffect New( const unsigned int numBlurSamples ); + + /** + * Set texcoord scale property. This scales the offset for texture samples + * along the motion velocity vector. A smaller value means the samples will + * be spaced closer, larger value further apart. User should use this to get + * the blur to look contiguous, i.e. the blur texels should not be too widely + * spread, with gaps in between. Default 0.125. + * @param texcoordScale The scaling factor that multiplies the motion velocity vector for texture lookups. + */ + void SetTexcoordScale( float texcoordScale ); + + /** + * Set geometry stretch factor property. This scales the amount the + * geometry stretches backwards along the motion velocity vector. A smaller + * value means the geometry stretches less, larger it stretches more. User + * should use this to get the blur to 'bleed' into areas outside the physical + * bounds of the actor. We need this as the blur is only applied inside the + * bounds of the actor, but you would expect motion blur trails where the + * actor was previously but is there no longer. Default 0.05. + * @param scalingFactor The scaling factor that extrudes the geometry backwards along the motion velocity vector. + */ + void SetGeometryStretchFactor( float scalingFactor ); + + /** + * Set speed scaling factor property. This takes the magnitude of the motion + * velocity vector and scales it to produce a value which is used to fade the + * blur in / out with the speed that the actor is moving. As the blur fades + * in, more of the blur is visible and less of the original actor, and vice + * versa. + * This value is also used to control how much to fade the actor near the + * edges, based on the speed the actor is moving. When the actor is at rest + * this is not applied. Default 0.5. + * @param scalingFactor The scaling factor that controls the edge fade / blur fade of the actor. + */ + void SetSpeedScalingFactor( float scalingFactor ); + + /** + * Set the displacement from the centre of the actor that the actor will start + * to fade towards its edges. This is used to prevent an unsightly hard edge + * between the blurred actor and the scene. Depends on the values of the + * vertices in the vertex stream. When the actor is at rest this is not applied. + * Default 0.25, which is half way towards the edge for an ImageRenderer::QUAD. + * @param displacement The displacement from the centre of the actor that the actor will start to edge fade. + */ + void SetObjectFadeStart( Vector2 displacement ); + + /** + * Set the displacement from the centre of the actor that the actor will + * finish fading towards its edges. This is used to prevent an unsightly hard + * edge between the blurred actor and the scene. Depends on the values of the + * vertices in the vertex stream. When the actor is at rest this is not applied. + * Default 0.5, which is all the way towards the edge for an ImageRenderer::QUAD. + * @param displacement The displacement from the centre of the actor that the actor will finish edge fading. + */ + void SetObjectFadeEnd( Vector2 displacement ); + + /** + * Set a global scaler applied to the alpha of the actor. Used to make the + * blurred actor a bit more subtle (helps to hide discontinuities due to + * limited number of texture samples) and reveal a bit of the background + * behind it as it moves. When the actor is at rest this is not applied. Default 0.75. + * @param alphaScale The scaling factor which multiplies the alpha of each pixel of the actor. + */ + void SetAlphaScale( float alphaScale ); + + /** + * Set the number of texture samples to be taken. Increasing the number of + * samples provides better quality at the cost of performance. + * @param numSamples The number of texture samples to be taken. Default is 8. + */ + void SetNumSamples( int numSamples ); + + /** + * Get the name for the texcoord scale property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetTexcoordScalePropertyName() const; + + /** + * Get the name for the geometry stretching property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetGeometryStretchFactorPropertyName() const; + + /** + * Get the name for the speed scaling property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetSpeedScalingFactorPropertyName() const; + + /** + * Get the name for the fade start property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetObjectFadeStartPropertyName() const; + + /** + * Get the name for the fade end property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetObjectFadeEndPropertyName() const; + + /** + * Get the name for the alpha scale property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetAlphaScalePropertyName() const; + + /** + * Downcast an ShaderEffect handle to MotionBlurEffect handle. If handle points to a MotionBlurEffect object the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle to a ShaderEffect + * @return handle to a MotionBlurEffect object or an uninitialized handle + */ + static MotionBlurEffect DownCast( ShaderEffect handle ); + +private: + // Not intended for application developers + MotionBlurEffect( ShaderEffect handle ); +}; + +} + +} + +#endif //#ifndef __DALI_TOOLKIT_SHADER_EFFECT_MOTION_BLUR_H__ + diff --git a/dali-toolkit/public-api/shader-effects/motion-stretch-effect.cpp b/dali-toolkit/public-api/shader-effects/motion-stretch-effect.cpp new file mode 100644 index 0000000..cc2a9ca --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/motion-stretch-effect.cpp @@ -0,0 +1,299 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +struct MatrixFromPropertiesConstraint +{ + MatrixFromPropertiesConstraint() + { + } + + Matrix operator()( const Matrix& current, + const PropertyInput& propertyPosition, + const PropertyInput& propertyOrientation, + const PropertyInput& propertyScale ) + { + Matrix mat4( false ); + mat4.SetTransformComponents( propertyScale.GetVector3(), + propertyOrientation.GetQuaternion(), + propertyPosition.GetVector3() ); + + return mat4; + } +}; + +const std::string MOTION_STRETCH_GEOMETRY_STRETCH_SCALING_FACTOR_PROPERTY_NAME( "uGeometryStretchFactor" ); +const std::string MOTION_STRETCH_SPEED_SCALING_FACTOR_PROPERTY_NAME( "uSpeedScalingFactor" ); +const std::string MOTION_STRETCH_OBJECT_FADE_START_PROPERTY_NAME( "uObjectFadeStart" ); +const std::string MOTION_STRETCH_OBJECT_FADE_END_PROPERTY_NAME( "uObjectFadeEnd" ); +const std::string MOTION_STRETCH_ALPHA_SCALE_PROPERTY_NAME( "uAlphaScale" ); +const std::string MOTION_STRETCH_MODELVIEW_LASTFRAME( "uModelLastFrame" ); ///< Matrix + +//////////////////////////////////////////////////// +// +// Motion stretch shader / actor tweaking parameters +// + +// half width and half height respectively of actor, corresponding to values in vertex attribute stream +// Note that these values work for normal image actor (verts +/- 0.5) but a grid or a nine square seemsi +// to have verts in pixel space (e.g. 256,256). Need to fix this somehow, +// either in Dali or by passing uniforms which we can use to 'normalise' the verts in the vertex shader +const Vector2 MOTION_STRETCH_ACTOR_VERTEX( 0.5f, 0.5f ); + +const float MOTION_STRETCH_GEOM_STRETCH_SCALING_FACTOR = 0.5f; // scaling factor for how much to stretch actor geom as it moves +const float MOTION_STRETCH_SPEED_SCALING_FACTOR = 0.5f; // scales the speed, producing a number affecting how much the actor stretches & fades at the edges + +const Vector2 MOTION_STRETCH_OBJECT_FADE_END( MOTION_STRETCH_ACTOR_VERTEX ); // displacement from center at which actor fully fades to zero alpha +const Vector2 MOTION_STRETCH_OBJECT_FADE_START( MOTION_STRETCH_OBJECT_FADE_END * 0.5f ); // displacement from center at which actor start to fade from full alpha + +const float MOTION_STRETCH_ALPHA_SCALE = 0.75f; // global scaler applied to actor alpha as it is stretched + moving + +} // namespace + + +MotionStretchEffect::MotionStretchEffect() +{ +} + +// Call the Parent copy constructor to add reference to the implementation for this object +MotionStretchEffect::MotionStretchEffect( ShaderEffect handle ) +:ShaderEffect( handle ) +{ +} + +MotionStretchEffect::~MotionStretchEffect() +{ +} + +MotionStretchEffect MotionStretchEffect::Apply( Actor handle ) +{ + MotionStretchEffect newEffect = New(); + handle.SetShaderEffect( newEffect ); + + Property::Index uModelProperty = newEffect.GetPropertyIndex( MOTION_STRETCH_MODELVIEW_LASTFRAME ); + + Constraint constraint = Constraint::New( uModelProperty, + Source( handle, Actor::WORLD_MATRIX ), + EqualToConstraint() ); + + // and set up constraint. + newEffect.ApplyConstraint(constraint); + return newEffect; +} + +MotionStretchEffect MotionStretchEffect::New() +{ + // Dali vertexSource prefix for reference: + // precision highp float; + // attribute vec3 aPosition; + // attribute vec2 aTexCoord; + // uniform mat4 uMvpMatrix; + // uniform mat4 uModelView; + // uniform mat3 uNormalMatrix; + // uniform mat4 uProjection; + // uniform vec4 uColor; + // varying vec2 vTexCoord; + std::string vertexSource; + vertexSource = + "uniform mat4 uModelLastFrame;\n" + "uniform float uTimeDelta;\n" + + "uniform float uGeometryStretchFactor;\n" + "uniform float uSpeedScalingFactor;\n" + + // outputs + "varying vec2 vModelSpaceCenterToPos;\n" + "varying vec2 vScreenSpaceVelocityVector;\n" + "varying float vSpeed;\n" + + "void main()\n" + "{\n" + // get view space position of vertex this frame and last frame + " vec4 vertex = vec4(aPosition, 1.0);\n" + " vec4 viewSpaceVertex = uModelView * vertex;\n" + " vec4 viewSpaceVertexLastFrame = uViewMatrix * uModelLastFrame * vertex;\n" + + // work out vertex's last movement in view space + " vec3 viewSpacePosDelta = viewSpaceVertex.xyz - viewSpaceVertexLastFrame.xyz;\n" + " float reciprocalTimeDelta = 1.0 / ((uTimeDelta > 0.0) ? uTimeDelta : 0.01);\n" + + // get clip space position of vertex this frame and last frame + " vec4 clipSpaceVertex = uMvpMatrix * vertex;\n" + " vec4 clipSpaceVertexLastFrame = uProjection * viewSpaceVertexLastFrame;\n" + + // decide how much this vertex is 'trailing', i.e. at the back of the object relative to its direction of motion. We do this + // by assuming the objects model space origin is at its center and taking the dot product of the vector from center to vertex with the motion direction + " float t = 0.0;\n" + " float posDeltaLength = length(viewSpacePosDelta);\n" + " if(posDeltaLength > 0.001)\n" // avoid div by 0 if object has barely moved + " {\n" + " vec4 viewSpaceCenterToPos = uModelView * vec4(aPosition, 0.0);\n" + " float centerToVertexDist = length(viewSpaceCenterToPos);\n" + " if(centerToVertexDist > 0.001)\n" // avoid div by 0 if object has vertex at model space origin + " {\n" + " vec3 viewSpacePosDeltaNormalised = viewSpacePosDelta / posDeltaLength;\n" + " vec3 viewSpaceCenterToPosNormalised = viewSpaceCenterToPos.xyz / centerToVertexDist;\n" + " t = (dot(viewSpacePosDeltaNormalised, viewSpaceCenterToPosNormalised) * 0.5 ) + 0.5;\n" // scale and bias from [-1..1] to [0..1] + " }\n" + " }\n" + // output vertex position lerped with its last position, based on how much it is trailing, + // this stretches the geom back along where it has just been, giving a warping effect + // We raise t to a power in order that non-trailing vertices are effected much more than trailing ones + // Note: we must take account of time delta to convert position delta into a velocity, so changes are smooth (take into account frame time correctly) + " gl_Position = mix(clipSpaceVertexLastFrame, clipSpaceVertex, t * t * t * uGeometryStretchFactor * reciprocalTimeDelta);\n" + + // work out vertex's last movement in normalised device coordinates [-1..1] space, i.e. perspective divide + " vec2 ndcVertex = clipSpaceVertex.xy / clipSpaceVertex.w;\n" + " vec2 ndcVertexLastFrame = clipSpaceVertexLastFrame.xy / clipSpaceVertexLastFrame.w;\n" + // scale and bias so that a value of 1.0 corresponds to screen size (NDC is [-1..1] = 2) + " vScreenSpaceVelocityVector = ((ndcVertex - ndcVertexLastFrame) * 0.5 * reciprocalTimeDelta);\n" + " vScreenSpaceVelocityVector.y = -vScreenSpaceVelocityVector.y;\n" // TODO negated due to y being inverted in our coordinate system? + // calculate a scaling factor proportional to velocity, which we can use to tweak how things look + " vSpeed = length(vScreenSpaceVelocityVector) * uSpeedScalingFactor;\n" + " vSpeed = clamp(vSpeed, 0.0, 1.0);\n" + + // provide fragment shader with vector from center of object to pixel (assumes the objects model space origin is at its center and verts have same z) + " vModelSpaceCenterToPos = aPosition.xy;\n" + + " vTexCoord = aTexCoord;\n" + "}\n"; + + + // Dali fragmentSource prefix for reference: + // precision highp float; + // uniform sampler2D sTexture; + // uniform sampler2D sEffect; + // uniform vec4 uColor; + // varying vec2 vTexCoord; + std::string fragmentSource; + fragmentSource = + "precision mediump float;\n" + + "uniform vec2 uObjectFadeStart;\n" + "uniform vec2 uObjectFadeEnd;\n" + "uniform float uAlphaScale;\n" + + // inputs + "varying vec2 vModelSpaceCenterToPos;\n" + "varying vec2 vScreenSpaceVelocityVector;\n" + "varying float vSpeed;\n" + + "void main()\n" + "{\n" + // calculate an alpha value that will fade the object towards its extremities, we need this to avoid an unsightly hard edge between color values of + // the stretched object and the background. Use smoothstep also to hide any hard edges (discontinuities) in rate of change of this alpha gradient + " vec2 centerToPixel = abs( vModelSpaceCenterToPos );\n" + " vec2 fadeToEdges = smoothstep(0.0, 1.0, 1.0 - ((centerToPixel - uObjectFadeStart) / (uObjectFadeEnd - uObjectFadeStart)));\n" + " float fadeToEdgesScale = fadeToEdges.x * fadeToEdges.y * uAlphaScale;\n" // apply global scaler + " fadeToEdgesScale = mix(1.0, fadeToEdgesScale, vSpeed);\n" // fade proportional to speed, so opaque when at rest + + // standard actor texel + " vec4 colActor = texture2D(sTexture, vTexCoord);\n" + " gl_FragColor = colActor;\n" + " gl_FragColor.a *= fadeToEdgesScale;\n" // fade actor to its edges based on speed of motion + " gl_FragColor *= uColor;\n" + "}"; + + // NOTE: we must turn on alpha blending for the actor (HINT_BLENDING) + ShaderEffect shader = ShaderEffect::New( vertexSource, + fragmentSource, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID ) ); + + + + MotionStretchEffect handle( shader ); + + + ////////////////////////////////////// + // Register uniform properties + // + // + + // factors that scale the look, defaults + handle.SetUniform( MOTION_STRETCH_GEOMETRY_STRETCH_SCALING_FACTOR_PROPERTY_NAME, MOTION_STRETCH_GEOM_STRETCH_SCALING_FACTOR ); + handle.SetUniform( MOTION_STRETCH_SPEED_SCALING_FACTOR_PROPERTY_NAME, MOTION_STRETCH_SPEED_SCALING_FACTOR ); + handle.SetUniform( MOTION_STRETCH_OBJECT_FADE_START_PROPERTY_NAME, MOTION_STRETCH_OBJECT_FADE_START ); + handle.SetUniform( MOTION_STRETCH_OBJECT_FADE_END_PROPERTY_NAME, MOTION_STRETCH_OBJECT_FADE_END ); + handle.SetUniform( MOTION_STRETCH_ALPHA_SCALE_PROPERTY_NAME, MOTION_STRETCH_ALPHA_SCALE ); + handle.SetUniform( MOTION_STRETCH_MODELVIEW_LASTFRAME, Matrix::IDENTITY ); + + return handle; +} + +void MotionStretchEffect::SetGeometryStretchFactor( float scalingFactor ) +{ + SetUniform( MOTION_STRETCH_GEOMETRY_STRETCH_SCALING_FACTOR_PROPERTY_NAME, scalingFactor ); +} + +void MotionStretchEffect::SetSpeedScalingFactor( float scalingFactor ) +{ + SetUniform( MOTION_STRETCH_SPEED_SCALING_FACTOR_PROPERTY_NAME, scalingFactor ); +} + +void MotionStretchEffect::SetObjectFadeStart( Vector2 displacement ) +{ + SetUniform( MOTION_STRETCH_OBJECT_FADE_START_PROPERTY_NAME, displacement ); +} + +void MotionStretchEffect::SetObjectFadeEnd( Vector2 displacement ) +{ + SetUniform( MOTION_STRETCH_OBJECT_FADE_END_PROPERTY_NAME, displacement ); +} + +void MotionStretchEffect::SetAlphaScale( float alphaScale ) +{ + SetUniform( MOTION_STRETCH_ALPHA_SCALE_PROPERTY_NAME, alphaScale ); +} + +const std::string& MotionStretchEffect::GetGeometryStretchFactorPropertyName() const +{ + return MOTION_STRETCH_GEOMETRY_STRETCH_SCALING_FACTOR_PROPERTY_NAME; +} + +const std::string& MotionStretchEffect::GetSpeedScalingFactorPropertyName() const +{ + return MOTION_STRETCH_SPEED_SCALING_FACTOR_PROPERTY_NAME; +} + +const std::string& MotionStretchEffect::GetObjectFadeStartPropertyName() const +{ + return MOTION_STRETCH_OBJECT_FADE_START_PROPERTY_NAME; +} + +const std::string& MotionStretchEffect::GetObjectFadeEndPropertyName() const +{ + return MOTION_STRETCH_OBJECT_FADE_END_PROPERTY_NAME; +} + +const std::string& MotionStretchEffect::GetAlphaScalePropertyName() const +{ + return MOTION_STRETCH_ALPHA_SCALE_PROPERTY_NAME; +} + +} + +} + diff --git a/dali-toolkit/public-api/shader-effects/motion-stretch-effect.h b/dali-toolkit/public-api/shader-effects/motion-stretch-effect.h new file mode 100644 index 0000000..af32b52 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/motion-stretch-effect.h @@ -0,0 +1,165 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_MOTION_STRETCH_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_MOTION_STRETCH_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * + * Class for motion stretch shader that works on a per object basis. Objects will stretch in the direction of motion when they move, or if the camera moves. Can be applied + * to ImageActor or TextActor only. + * + * Usage example:- + * + * // Create shader used for doing motion stretch\n + * MotionStretchEffect MotionStretchEffect = MotionStretchEffect::New(); + * + * // set actor shader to the stretch one\n + * Actor Actor = Actor::New( ... );\n + * Actor.SetShaderEffect( MotionStretchEffect ); + * + */ +class MotionStretchEffect : public ShaderEffect +{ + +public: + + /** + * Create an uninitialized MotionStretchEffect; this can be initialized with MotionStretchEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + MotionStretchEffect(); + + /** + * Virtual destructor. + */ + virtual ~MotionStretchEffect(); + + /** + * Create an initialized MotionStretchEffect + * @return A handle to a newly allocated Dali resource. + */ + static MotionStretchEffect New(); + + /** + * Create a MotionStretchEffect and attach it to the specified actor + * @return A handle to a newly allocated Dali resource. + */ + static MotionStretchEffect Apply( Actor handle ); + + /** + * Set geometry stretch factor property. This scales the amount the geometry + * stretches along the motion velocity vector. A smaller value means the geometry + * stretches less, larger it stretches more. Default 0.5. + * @param scalingFactor The scaling factor that extrudes the geometry forwards along the motion velocity vector. + */ + void SetGeometryStretchFactor( float scalingFactor ); + + /** + * Set speed scaling factor property. This value is used to control how much + * to fade the actor near the edges, based on the speed the actor is moving. + * When the actor is at rest this is not applied. Default 0.5. + * @param scalingFactor The scaling factor that controls the edge fade of the actor. + */ + void SetSpeedScalingFactor( float scalingFactor ); + + /** + * Set the displacement from the centre of the actor that the actor will start to + * fade towards its edges. This is used to prevent an unsightly hard edge + * between the stretched actor and the scene. Depends on the values of the + * vertices in the vertex stream. When the actor is at rest this is not applied. + * Default Vector2(0.25, 0.25), which is half way towards the edge for an ImageRenderer::QUAD. + * @param displacement The displacement from the centre of the actor that the actor will start to edge fade. + */ + void SetObjectFadeStart( Vector2 displacement ); + + /** + * Set the displacement from the centre of the actor that the actor will + * finish fading towards its edges. This is used to prevent an unsightly hard + * edge between the stretched actor and the scene. Depends on the values of + * the vertices in the vertex stream. When the actor is at rest this is not applied. + * Default 0.5, which is all the way towards the edge for an ImageRenderer::QUAD. + * @param displacement The displacement from the centre of the actor that the actor will finish edge fading. + */ + void SetObjectFadeEnd( Vector2 displacement ); + + /** + * Set a global scaler applied to the alpha of the actor. Used to make the + * stretched actor a bit more subtle and reveal a bit of the background behind + * it as it moves. When the actor is at rest this is not applied. Default 0.75. + * @param alphaScale The scaling factor which multiplies the alpha of each pixel of the actor. + */ + void SetAlphaScale( float alphaScale ); + + + /** + * Get the name for the texcoord scale property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetTexcoordScalePropertyName() const; + + /** + * Get the name for the geometry stretching property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetGeometryStretchFactorPropertyName() const; + + /** + * Get the name for the speed scaling property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetSpeedScalingFactorPropertyName() const; + + /** + * Get the name for the fade start X property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetObjectFadeStartPropertyName() const; + + /** + * Get the name for the fade end X property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetObjectFadeEndPropertyName() const; + + /** + * Get the name for the alpha scale property. Useful for animation. + * @return A std::string containing the property name + */ + const std::string& GetAlphaScalePropertyName() const; + +private: + // Not intended for application developers + MotionStretchEffect( ShaderEffect handle ); +}; + +} + +} + +#endif //#ifndef __DALI_TOOLKIT_SHADER_EFFECT_MOTION_STRETCH_H__ + diff --git a/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.cpp b/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.cpp new file mode 100644 index 0000000..fc1740b --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/nine-patch-mask-effect.cpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +namespace NinePatchMaskEffect +{ + +struct NinePatchMaskEffectSizeConstraint +{ + Vector2 operator()( const Vector2& current, const PropertyInput& property ) + { + const Vector3& actorSize = property.GetVector3(); + return Vector2( actorSize.x, actorSize.y ); + } +}; + +static void DoApply( ImageActor actor, const std::string& maskImage, const Vector2& maskSize, Vector4 maskBorder ) +{ + const char* ALPHA_MASK_VERTEX_SHADER_SOURCE = + "uniform vec2 uImageSize; \n" + "uniform vec2 uMaskSize; \n" + "varying vec2 vMaskTexCoord; \n" + " \n" + "void main() \n" + "{ \n" + " gl_Position = uMvpMatrix * vec4(aPosition, 1.0); \n" + " \n" + " // Ignore mask UVs for image \n" + " \n" + " highp vec2 halfImageSize = uImageSize * 0.5; \n" + " vTexCoord = (aPosition.xy + halfImageSize) / uImageSize; \n" + " \n" + " // UVs were calculated for image size, so convert for mask size \n" + " \n" + " highp vec2 halfMaskSize = uMaskSize * 0.5; \n" + " highp vec2 halfSizeDelta = halfImageSize - halfMaskSize; \n" + " \n" + " highp vec2 maskPosition = aPosition.xy; \n" + " maskPosition.x -= halfSizeDelta.x * sign(aPosition.x); \n" + " maskPosition.y -= halfSizeDelta.y * sign(aPosition.y); \n" + " \n" + " vMaskTexCoord = (maskPosition + halfMaskSize) / uMaskSize; \n" + "} \n"; + + const char* ALPHA_MASK_FRAGMENT_SHADER_SOURCE = + "varying vec2 vMaskTexCoord; \n" + " \n" + "void main() \n" + "{ \n" + " highp vec4 mask = texture2D(sEffect, vMaskTexCoord); \n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * uColor * vec4(1,1,1,mask.a); \n" + "} \n"; + + ShaderEffect maskEffect = ShaderEffect::New( ALPHA_MASK_VERTEX_SHADER_SOURCE, + ALPHA_MASK_FRAGMENT_SHADER_SOURCE, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) ); + + maskEffect.SetEffectImage( Image::New( maskImage ) ); + + maskEffect.SetUniform( "uImageSize", Vector2(0,0) /*Constrained to actor size*/ ); + maskEffect.ApplyConstraint( Constraint::New( maskEffect.GetPropertyIndex("uImageSize"), + Source(actor, Actor::SIZE), + NinePatchMaskEffectSizeConstraint() ) ); + + maskEffect.SetUniform( "uMaskSize", maskSize ); + + // Actor must provide nine-patch style geometry for this effect to work + actor.SetStyle( ImageActor::STYLE_NINE_PATCH ); + actor.SetNinePatchBorder( maskBorder ); + + actor.SetShaderEffect( maskEffect ); +} + +void Apply( ImageActor actor, const std::string& maskImage ) +{ + Vector2 maskSize = Image::GetImageSize( maskImage ); + + const float leftRight = (maskSize.width - 1.0f) * 0.5f; + const float topBottom = (maskSize.height - 1.0f) * 0.5f; + + DoApply( actor, maskImage, maskSize, Vector4( leftRight, topBottom, leftRight, topBottom ) ); +} + +void Apply( ImageActor actor, const std::string& maskImage, const Vector4& maskBorder ) +{ + Vector2 maskSize = Image::GetImageSize( maskImage ); + + DoApply( actor, maskImage, maskSize, maskBorder ); +} + +} // namespace NinePatchMaskEffect + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/overlay-effect.cpp b/dali-toolkit/public-api/shader-effects/overlay-effect.cpp new file mode 100644 index 0000000..301a096 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/overlay-effect.cpp @@ -0,0 +1,70 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +namespace Dali +{ + +namespace Toolkit +{ + +OverlayEffect::OverlayEffect() +{ +} + +OverlayEffect::~OverlayEffect() +{ +} + +OverlayEffect OverlayEffect::New( Image overlayImage ) +{ + // (Target > 0.5) * (1 - (1-2*(Target-0.5)) * (1-Blend)) + (Target <= 0.5) * ((2*Target) * Blend) + const char* OVERLAY_FRAGMENT_SHADER_SOURCE = + "void main()\n" + "{\n" + " lowp vec4 target = texture2D(sTexture, vTexCoord);\n" + " lowp vec4 overlay = texture2D(sEffect, vTexCoord);\n" + " if ( length( target.rgb ) > 0.5 )\n" + " {\n" + " gl_FragColor = vec4( mix( target.rgb, 1.0 - ( 1.0 - 2.0 * ( target.rgb - 0.5 ) ) * ( 1.0 - overlay.rgb ), overlay.a ), min( 1.0, target.a + overlay.a ) );\n" + " }\n" + " else\n" + " {\n" + " gl_FragColor = vec4( mix( target.rgb, 2.0 * target.rgb * overlay.rgb, overlay.a ), target.a + overlay.a );\n" + " }\n" + "}\n"; + + ShaderEffect shader = ShaderEffect::New( "", // Use default + OVERLAY_FRAGMENT_SHADER_SOURCE, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) ); + + shader.SetEffectImage( overlayImage ); + + return OverlayEffect( shader ); +} + +//Call the Parent copy constructor to add reference to the implementation for this object +OverlayEffect::OverlayEffect( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/overlay-effect.h b/dali-toolkit/public-api/shader-effects/overlay-effect.h new file mode 100644 index 0000000..8346d67 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/overlay-effect.h @@ -0,0 +1,68 @@ +#ifndef __DALI_TOOLKIT_OVERLAY_EFFECT_H__ +#define __DALI_TOOLKIT_OVERLAY_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * OverlayEffect is used to apply an overlay image to the actor. + * Typically overlay images should be the same size as the main image being viewed, but this isn't essential. + * + * Usage example: + * + * ImageActor actor = ImageActor::New( Image( EXAMPLE_IMAGE_PATH ) ); + * OverlayEffect overlayEffect = OverlayEffect::New( Image::New( OVERLAY_IMAGE_PATH ) ); + * actor.SetShaderEffect( overlayEffect ); + */ +class DALI_IMPORT_API OverlayEffect : public ShaderEffect +{ +public: + + /** + * Create an empty OverlayEffect handle. + */ + OverlayEffect(); + + /** + * Virtual destructor. + */ + virtual ~OverlayEffect(); + + /** + * Create a OverlayEffect. + * @return A handle to a newly allocated OverlayEffect. + */ + static OverlayEffect New( Image overlayImage ); + +private: // Not intended for application developers + + DALI_INTERNAL OverlayEffect( ShaderEffect handle ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_OVERLAY_EFFECT_H__ diff --git a/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.cpp b/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.cpp new file mode 100644 index 0000000..9ea742c --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/page-turn-book-spine-effect.cpp @@ -0,0 +1,130 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string SHADOW_WIDTH_PROPERTY_NAME("uShadowWidth"); +const std::string SPINE_SHADOW_PARAMETER_PROPERTY_NAME("uSpineShadowParameter"); +const std::string IS_BACK_IMAGE_VISIBLE_PROPERTY_NAME( "uIsBackImageVisible" ); +const std::string PAGE_WIDTH_PROPERTY_NAME( "uPageWidth" ); + +// fake shadow is used to enhance the effect, with its default maximum width to be pageSize * 0.15 +const float DEFAULT_SHADOW_WIDTH(0.15f); + +// the major&minor radius (in pixels) to form an ellipse shape +// the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow +const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f); + +} + +PageTurnBookSpineEffect::PageTurnBookSpineEffect() +{ +} + +PageTurnBookSpineEffect::PageTurnBookSpineEffect( ShaderEffect handle ) +: ShaderEffect( handle ) +{ +} + +PageTurnBookSpineEffect::~PageTurnBookSpineEffect() +{ +} + +PageTurnBookSpineEffect PageTurnBookSpineEffect::New() +{ + std::string vertexSource( + "uniform float uShadowWidth;\n" + " void main()\n" + " {\n" + " gl_Position = uProjection * uModelView * vec4(aPosition, 1.0);\n" + " vTexCoord.x = (aTexCoord.x-sTextureRect.s) /( 1.0 - uShadowWidth ) + sTextureRect.s;\n" + " vTexCoord.y = ( aTexCoord.y-sTextureRect.t-0.5*uShadowWidth*(sTextureRect.q-sTextureRect.t) )/( 1.0 - uShadowWidth ) + sTextureRect.t;\n" + " }"); + + // the simplified version of the fragment shader of page turn effect + std::string fragmentSource( + "uniform float uIsBackImageVisible;\n" + "uniform float uPageWidth;\n" + "uniform vec2 uSpineShadowParameter;\n" + " void main()\n" + " {\n" + // leave the border for display shadow, not visible( out of the screen ) when the page is static + " if( vTexCoord.y > sTextureRect.q || vTexCoord.y < sTextureRect.t || vTexCoord.x > sTextureRect.p )\n" + " {\n" + " gl_FragColor = vec4( 0.0 );\n" + " }\n" + " else \n" + " { \n" + // flip the image horizontally by changing the x component of the texture coordinate + " if( uIsBackImageVisible == 1.0 ) gl_FragColor = texture2D( sTexture, vec2( sTextureRect.p+sTextureRect.s-vTexCoord.x, vTexCoord.y ) ) * uColor; \n" + " else gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n" + " \n" + // display book spine, a stripe of shadowed texture + " float pixelPos = (vTexCoord.x-sTextureRect.s)*uPageWidth; \n" + " if(pixelPos < uSpineShadowParameter.x) \n" + " {\n" + " float x = pixelPos - uSpineShadowParameter.x;\n" + " float y = sqrt( uSpineShadowParameter.x*uSpineShadowParameter.x - x*x );\n" + " vec2 spineNormal = normalize(vec2(uSpineShadowParameter.y*x/uSpineShadowParameter.x, y));\n" + " gl_FragColor.rgb *= spineNormal.y; \n" + " }" + " }\n" + " }" ); + + ShaderEffect shader; + shader = ShaderEffect::New( vertexSource,fragmentSource ); + PageTurnBookSpineEffect handle( shader ); + handle.SetUniform( IS_BACK_IMAGE_VISIBLE_PROPERTY_NAME, -1.f ); + handle.SetUniform( SHADOW_WIDTH_PROPERTY_NAME, DEFAULT_SHADOW_WIDTH ); + handle.SetUniform( SPINE_SHADOW_PARAMETER_PROPERTY_NAME, DEFAULT_SPINE_SHADOW_PARAMETER ); + float defaultPageWidth = Dali::Stage::GetCurrent().GetSize().x; + handle.SetUniform( PAGE_WIDTH_PROPERTY_NAME, defaultPageWidth/(1.f-DEFAULT_SHADOW_WIDTH) ); + return handle; +} + +void PageTurnBookSpineEffect::SetIsBackImageVisible( bool isBackVisible ) +{ + float direction = isBackVisible ? 1.0f : -1.0f; + SetUniform( IS_BACK_IMAGE_VISIBLE_PROPERTY_NAME, direction ); +} + +void PageTurnBookSpineEffect::SetPageWidth( float pageWidth ) +{ + SetUniform( PAGE_WIDTH_PROPERTY_NAME, pageWidth ); +} + +void PageTurnBookSpineEffect::SetShadowWidth( float shadowWidth ) +{ + SetUniform( SHADOW_WIDTH_PROPERTY_NAME, shadowWidth ); +} + +void PageTurnBookSpineEffect::SetSpineShadowParameter( const Vector2& spineShadowParameter ) +{ + SetUniform( SPINE_SHADOW_PARAMETER_PROPERTY_NAME, spineShadowParameter); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/page-turn-effect.cpp b/dali-toolkit/public-api/shader-effects/page-turn-effect.cpp new file mode 100644 index 0000000..38e6449 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/page-turn-effect.cpp @@ -0,0 +1,96 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL HEADERS +#include + +namespace Dali +{ + +namespace Toolkit +{ + +PageTurnEffect::PageTurnEffect() +{ +} + +// Call the Parent copy constructor to add reference to the implementation for this object +PageTurnEffect::PageTurnEffect( ShaderEffect handle, Internal::PageTurnEffect* shaderExtension ) +: ShaderEffect( handle ) +{ + AttachExtension( shaderExtension ); +} + +PageTurnEffect::~PageTurnEffect() +{ +} + +PageTurnEffect PageTurnEffect::New( bool enableBlending ) +{ + return Internal::PageTurnEffect::CreateShaderEffect( enableBlending ); +} + +void PageTurnEffect::SetPageSize(const Vector2& pageSize) +{ + GetImpl( *this ).SetPageSize( pageSize ); +} + +void PageTurnEffect::SetOriginalCenter(const Vector2& originalCenter) +{ + GetImpl( *this ).SetOriginalCenter( originalCenter ); +} + +void PageTurnEffect::SetCurrentCenter(const Vector2& currentCenter) +{ + GetImpl( *this ).SetCurrentCenter( currentCenter ); +} + +void PageTurnEffect::SetIsTurningBack(bool isTurningBack) +{ + GetImpl( *this ).SetIsTurningBack( isTurningBack ); +} + +void PageTurnEffect::SetShadowWidth(float shadowWidth) +{ + GetImpl( *this ).SetShadowWidth( shadowWidth ); +} + +void PageTurnEffect::SetSpineShadowParameter(const Vector2& spineShadowParameter) +{ + GetImpl( *this ).SetSpineShadowParameter( spineShadowParameter); +} + +const std::string& PageTurnEffect::GetPageSizePropertyName() const +{ + return GetImpl( *this ).GetPageSizePropertyName(); +} + +const std::string& PageTurnEffect::GetOriginalCenterPropertyName() const +{ + return GetImpl( *this ).GetOriginalCenterPropertyName(); +} + +const std::string& PageTurnEffect::GetCurrentCenterPropertyName() const +{ + return GetImpl( *this ).GetCurrentCenterPropertyName(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/ripple-effect.cpp b/dali-toolkit/public-api/shader-effects/ripple-effect.cpp new file mode 100644 index 0000000..a685e0c --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/ripple-effect.cpp @@ -0,0 +1,141 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string AMPLITUDE_PROPERTY_NAME( "uAmplitude" ); +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string TIME_PROPERTY_NAME( "uTime" ); + +} // namespace + +RippleEffect::RippleEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +RippleEffect::RippleEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +RippleEffect::~RippleEffect() +{ +} + + +RippleEffect RippleEffect::New() +{ + + std::string vertexShader( + "precision mediump float;\n" + "uniform mediump vec2 uCenter;\n" + "uniform mediump float uTime;\n" + "uniform mediump float uAmplitude;\n" + "uniform mediump float uLighting;\n" + "uniform mediump float uWaveLength;\n" + "varying mediump float vLight;\n" + "varying mediump float vShade;\n" + "void main()\n" + "{\n" + "float lighting = uAmplitude * 0.02;\n" + "float waveLength = uAmplitude * 0.0016;\n" + "vec4 world = uModelView * vec4(aPosition,1.0);\n" + "vec2 d = vec2(world.x - uCenter.x, world.y - uCenter.y);\n" + "float dist = length(d);\n" + "float amplitude = cos(uTime - dist*waveLength);\n" + "float slope = sin(uTime - dist*waveLength);\n" + "world.z += amplitude * uAmplitude;\n" + "gl_Position = uProjection * world;\n" + "vec2 lightDirection = vec2(-0.707,0.707);\n" + "float dot = 0.0;\n" + "if(dist > 0.0)\n" + "{\n" + " dot = dot(normalize(d),lightDirection) * lighting;\n" + "}\n" + "vShade = 1.0 - (dot * slope);\n" + "vLight = max(0.0, dot * -slope);\n" + "vTexCoord = aTexCoord;\n" + "}" ); + + // append the default version + std::string imageFragmentShader( + "precision mediump float;\n" + "varying mediump float vLight;\n" + "varying mediump float vShade;\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * uColor * vec4(vShade,vShade,vShade,1.0) + vec4(vLight, vLight, vLight,0.0);\n" + "}" ); + + // Create the implementation, temporarily owned on stack + Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::New( + vertexShader, imageFragmentShader, vertexShader, "", GeometryHints(HINT_GRID) ); + + /* Pass ownership to RippleEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::RippleEffect handle( shaderEffect ); + + handle.SetUniform( AMPLITUDE_PROPERTY_NAME, 0.0f ); + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.0f, 0.0f)); + handle.SetUniform( TIME_PROPERTY_NAME, 0.0f ); + + return handle; +} + +void RippleEffect::SetAmplitude(float amplitude) +{ + SetUniform( AMPLITUDE_PROPERTY_NAME, amplitude ); +} + +void RippleEffect::SetCenter(const Vector2& center) +{ + SetUniform( CENTER_PROPERTY_NAME, center ); +} + +void RippleEffect::SetTime(float time) +{ + SetUniform( TIME_PROPERTY_NAME, time ); +} + +const std::string& RippleEffect::GetAmplitudePropertyName() const +{ + return AMPLITUDE_PROPERTY_NAME; +} + +const std::string& RippleEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& RippleEffect::GetTimePropertyName() const +{ + return TIME_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/ripple2d-effect.cpp b/dali-toolkit/public-api/shader-effects/ripple2d-effect.cpp new file mode 100644 index 0000000..d2d0633 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/ripple2d-effect.cpp @@ -0,0 +1,106 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string TEXTURE_SIZE_PROPERTY_NAME( "uTextureSize" ); +const std::string AMPLITUDE_PROPERTY_NAME( "uAmplitude" ); +const std::string TIME_PROPERTY_NAME( "uTime" ); + +} // namespace + +Ripple2DEffect::Ripple2DEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +Ripple2DEffect::Ripple2DEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +Ripple2DEffect::~Ripple2DEffect() +{ +} + + +Ripple2DEffect Ripple2DEffect::New() +{ + // append the default version + std::string fragmentShader( + "uniform float uAmplitude;\n" + "uniform float uTime;\n" + "void main()\n" + "{\n" + " highp vec2 textureSize = sTextureRect.zw - sTextureRect.xy;\n" + " highp vec2 pos = -1.0 + 2.0 * vTexCoord.st/textureSize;\n" + " highp float len = length(pos);\n" + " highp vec2 texCoord = vTexCoord.st/textureSize + pos/len * sin( len * 12.0 - uTime * 4.0 ) * uAmplitude;\n" + " gl_FragColor = texture2D(sTexture, texCoord) * uColor;\n" + "}" ); + + // Create the implementation, temporarily owned on stack + Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::New( + "", + fragmentShader, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID )); + + /* Pass ownership to Ripple2DEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::Ripple2DEffect handle( shaderEffect ); + + handle.SetUniform( TEXTURE_SIZE_PROPERTY_NAME, Vector2(0.0f, 0.0f) ); + handle.SetUniform( AMPLITUDE_PROPERTY_NAME, 0.0f ); + handle.SetUniform( TIME_PROPERTY_NAME, 0.0f ); + + return handle; + +} + +void Ripple2DEffect::SetAmplitude(float amplitude) +{ + SetUniform( AMPLITUDE_PROPERTY_NAME, amplitude ); +} + +void Ripple2DEffect::SetTime(float time) +{ + SetUniform( TIME_PROPERTY_NAME, time ); +} + +const std::string& Ripple2DEffect::GetAmplitudePropertyName() const +{ + return AMPLITUDE_PROPERTY_NAME; +} + +const std::string& Ripple2DEffect::GetTimePropertyName() const +{ + return TIME_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/shear-effect.cpp b/dali-toolkit/public-api/shader-effects/shear-effect.cpp new file mode 100644 index 0000000..98c67c2 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/shear-effect.cpp @@ -0,0 +1,119 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string ANGLE_X_AXIS_PROPERTY_NAME( "uAngleXAxis" ); +const std::string ANGLE_Y_AXIS_PROPERTY_NAME( "uAngleYAxis" ); + +} // namespace + +ShearEffect::ShearEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +ShearEffect::ShearEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +ShearEffect::~ShearEffect() +{ +} + + +ShearEffect ShearEffect::New() +{ + // append the default version + std::string vertexShader( + "uniform mediump vec2 uCenter;\n" + "uniform mediump float uAngleXAxis;\n" + "uniform mediump float uAngleYAxis;\n" + "\n" + "void main()\n" + "{\n" + "mediump vec4 world = uModelView * vec4(aPosition,1.0);\n" + "\n" + "world.x = world.x + tan(radians(uAngleXAxis)) * (world.y - uCenter.y * world.w);\n" + "world.y = world.y + tan(radians(uAngleYAxis)) * (world.x - uCenter.x * world.w);\n" + "\n" + "gl_Position = uProjection * world;\n" + "\n" + "vTexCoord = aTexCoord;\n" + "}" ); + + // Create the implementation, temporarily owned on stack, + ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + vertexShader, + "", + GeometryType( GEOMETRY_TYPE_IMAGE | GEOMETRY_TYPE_TEXT ), + GeometryHints( HINT_GRID )); + + // Pass ownership to ShearEffect through overloaded constructor, So that it now has access to the + // Dali::ShaderEffect implementation + Dali::Toolkit::ShearEffect handle( shaderEffectCustom ); + + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.0f, 0.0f), COORDINATE_TYPE_VIEWPORT_POSITION ); + handle.SetUniform( ANGLE_X_AXIS_PROPERTY_NAME, 0.0f); + handle.SetUniform( ANGLE_Y_AXIS_PROPERTY_NAME, 0.0f); + + return handle; +} + +void ShearEffect::SetCenter( const Vector2& center ) +{ + SetUniform( CENTER_PROPERTY_NAME, center, COORDINATE_TYPE_VIEWPORT_POSITION ); +} + +void ShearEffect::SetAngleXAxis( float angle ) +{ + SetUniform( ANGLE_X_AXIS_PROPERTY_NAME, angle ); +}; + +void ShearEffect::SetAngleYAxis( float angle ) +{ + SetUniform( ANGLE_Y_AXIS_PROPERTY_NAME, angle ); +}; + +const std::string& ShearEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& ShearEffect::GetAngleXAxisPropertyName() const +{ + return ANGLE_X_AXIS_PROPERTY_NAME; +} + +const std::string& ShearEffect::GetAngleYAxisPropertyName() const +{ + return ANGLE_Y_AXIS_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/shear-effect.h b/dali-toolkit/public-api/shader-effects/shear-effect.h new file mode 100644 index 0000000..87a1e3f --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/shear-effect.h @@ -0,0 +1,98 @@ +#ifndef __DALI_TOOLKIT_SHEAR_EFFECT_H__ +#define __DALI_TOOLKIT_SHEAR_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * ShearEffect is a custom shader effect to achieve shear effects in Image actors + */ +class ShearEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized ShearEffect; this can be initialized with ShearEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + ShearEffect(); + + /** + * Virtual destructor. + */ + virtual ~ShearEffect(); + + /** + * Create an initialized ShearEffect. + * @return A handle to a newly allocated Dali resource. + */ + static ShearEffect New(); + + /** + * Set the center point of the shear effect in screen coordinates. + * @param [in] center The new center point. + */ + void SetCenter(const Vector2& center); + + /** + * Set the angle of the shear effect in the X axis. + * @param [in] angle The new angle. + */ + void SetAngleXAxis(float angle); + + /** + * Set the angle of the shear effect in the Y axis. + * @param [in] angle The new angle. + */ + void SetAngleYAxis(float angle); + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the X axis property + * @return A std::string containing the property name + */ + const std::string& GetAngleXAxisPropertyName() const; + + /** + * Get the name for the Y axis property + * @return A std::string containing the property name + */ + const std::string& GetAngleYAxisPropertyName() const; + + +private: // Not intended for application developers + ShearEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHEAR_EFFECT_H__ diff --git a/dali-toolkit/public-api/shader-effects/soft-button-effect.cpp b/dali-toolkit/public-api/shader-effects/soft-button-effect.cpp new file mode 100644 index 0000000..b9f2a67 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/soft-button-effect.cpp @@ -0,0 +1,423 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_PROPERTY_NAME( "uLightingIndentationAmount" ); +const std::string SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_PROPERTY_NAME( "uTextureDistortAmount" ); +const std::string SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_PROPERTY_NAME( "uAmbientLight" ); +const std::string SOFT_BUTTON_DIFFUSE_LIGHT_PROPERTY_NAME( "uDiffuseLight" ); +const std::string SOFT_BUTTON_LIGHTING_MULTIPLIER_PROPERTY_NAME( "uLightMultiplier" ); +const std::string SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME( "uInsideCircleSizeScale" ); +const std::string SOFT_BUTTON_RECIP_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME( "uRecipInsideCircleSizeScale" ); +const std::string SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_PROPERTY_NAME( "uOutsideCircleDepth" ); +const std::string SOFT_BUTTON_EFFECT_PIXEL_AREA_PROPERTY_NAME( "uEffectRegion" ); +const std::string SOFT_BUTTON_RECTANGLE_SIZE_SCALE_PROPERTY_NAME( "uRectangleSizeScale" ); + + +// factors that scale the look, defaults +const float SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_DEFAULT = 0.0f; +const float SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_DEFAULT = 0.0f; +const float SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_DEFAULT = 0.15f; +const Vector3 SOFT_BUTTON_DIFFUSE_LIGHT_DEFAULT = Vector3(0.0f, 0.7070168f, 0.7071068f); +const float SOFT_BUTTON_LIGHTING_MULTIPLIER_DEFAULT = 1.2f; +const float SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT = 0.75f; +const float SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_DEFAULT = Math::PI * 0.05f; +const Vector4 SOFT_BUTTON_EFFECT_PIXEL_AREA_DEFAULT = Vector4(0.0f, 0.0f, 1.0f, 1.0f); +const float SOFT_BUTTON_RECTANGLE_SIZE_SCALE_DEFAULT = 0.5f; + +} // namespace + +/** + * ReciprocalConstraint + * + * f(current, property) = 1.0 / property + */ +struct ReciprocalConstraint +{ + ReciprocalConstraint(){} + + float operator()(const float current, const PropertyInput& property) + { + return 1.0f / property.GetFloat(); + } +}; + + +//////////////////////////////////////////////////// +// +// Soft button shader / actor tweaking parameters +// + + +SoftButtonEffect::SoftButtonEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +SoftButtonEffect::SoftButtonEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +SoftButtonEffect::~SoftButtonEffect() +{ +} + +SoftButtonEffect SoftButtonEffect::New(Type type) +{ + std::string vertexSource; + vertexSource = "precision mediump float;\n" + "uniform vec3 uDiffuseLight;\n" + "uniform float uAmbientLight;\n" + "uniform float uLightMultiplier;\n" + "uniform vec4 uEffectRegion;\n" + "varying vec2 vCentredCoord;\n" + + "const vec3 norm = vec3(0.0, 0.0, 1.0);\n" + + "void main()\n" + "{\n" + " vTexCoord = aTexCoord;\n" + // Get the rect coords of the effect region in -1..1 range, i.e. circle centred around the center of the rect + // Done in the vertex shader itself to make use of gl interpolation for varying. + " vCentredCoord = vec2( ( (vTexCoord.x - uEffectRegion.x)/(uEffectRegion.z - uEffectRegion.x) * 2.0 - 1.0 ), ( (vTexCoord.y - uEffectRegion.y)/(uEffectRegion.w - uEffectRegion.y) * 2.0 - 1.0 ) );\n" + " gl_Position = uMvpMatrix * vec4(aPosition, 1.0);\n" + "}\n"; + + std::string fragmentSourceFixed; + fragmentSourceFixed = "precision mediump float;\n" + + "uniform vec3 uDiffuseLight;\n" + "uniform float uAmbientLight;\n" + "uniform float uLightMultiplier;\n" + "varying vec2 vCentredCoord;\n" + + "const vec3 norm = vec3(0.0, 0.0, 1.0);\n" + + "void main()\n" + "{\n" + " vec4 col = texture2D(sTexture, vTexCoord);\n" + // calc lighting + " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n" + // output col = image * light + // use the lighting value for colors only + " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n" + "}\n"; + + std::string fragmentSourceElliptical; + fragmentSourceElliptical = "precision mediump float;\n" + + "uniform float uLightingIndentationAmount;\n" + "uniform float uTextureDistortAmount;\n" + "uniform vec3 uDiffuseLight;\n" + "uniform float uAmbientLight;\n" + "uniform float uLightMultiplier;\n" + "uniform float uInsideCircleSizeScale;\n" + "uniform float uRecipInsideCircleSizeScale;\n" + "uniform float uOutsideCircleDepth;\n" + "uniform vec4 uEffectRegion;\n" + "varying vec2 vCentredCoord;\n" + + "const float PI = 3.1415927;\n" + + "void main()\n" + "{\n" + // Apply distortion only if the pixel is within the rect specified + "if( (vTexCoord.x > uEffectRegion.x) && (vTexCoord.x < uEffectRegion.z) && (vTexCoord.y > uEffectRegion.y) && (vTexCoord.y < uEffectRegion.w) )\n" + "{\n" + " vec2 coord = vCentredCoord;\n" + + // find a coordinate representing distance from circle centre, such that we split into inside / outside circles that can have different gradients / normals + " float realDistFromCentre = length(coord);\n" + " realDistFromCentre = min(1.0, realDistFromCentre);\n" // clamp corners of square to vertical normal + " float distFromCentre;\n" + " if(realDistFromCentre <= uInsideCircleSizeScale)\n" + " {\n" + " distFromCentre = realDistFromCentre * uRecipInsideCircleSizeScale * (1.0 - uOutsideCircleDepth);\n" // inside circle indent, up to outline depth + " }\n" + " else \n" + " {\n" + " distFromCentre = mix(1.0 - uOutsideCircleDepth, 1.0, (realDistFromCentre - ( uInsideCircleSizeScale)) / (1.0 - uInsideCircleSizeScale));\n" // outside circle + " }\n" + + // get coords in -PI..PI range, i.e. scale the circle for use by trig functions + " coord *= PI;\n" + + // get a z value for the distorted surface in 0..1 range, using cos for a smooth curve (note, we ignore inside / outside circles since the difference isn't noticeable visually) + " vec2 cosThetaCoord = (cos(coord) * 0.5) + 0.5;\n" + " float z = cosThetaCoord.x * cosThetaCoord.y;\n" + + // get the normal for the distorted surface, using the fact that the derivative of cos is -sin, finding tangent vector from slope and then normal by cross product... + " float sinThetaCoord = sin(distFromCentre*PI) * uLightingIndentationAmount;\n" // slope, so tangent vec is (1.0, -sin) + // ...2D normal vector along distFromCentre vec is (sin, 1.0), convert to components in 3D. + " vec3 norm = normalize(vec3(coord.x * sinThetaCoord, coord.y * sinThetaCoord, 1.0));\n" + + // form surface z and project texture onto it. + " float indentAmount = 1.0 / (1.0 - (z * uTextureDistortAmount));\n" + " vec2 distortedCoord = vCentredCoord * indentAmount;\n" + + // Convert the rect coordinates in -1 to 1 range back to the original coordinates + " vec2 texCoord = vec2( ( (distortedCoord.x + 1.0)*(0.5) * (uEffectRegion.z - uEffectRegion.x) + uEffectRegion.x ), ( (distortedCoord.y + 1.0)*(0.5) * (uEffectRegion.w - uEffectRegion.y) + uEffectRegion.y ) ); \n" + " vec4 col = texture2D(sTexture, texCoord);\n" + + // calc lighting + " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n" + " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n" + "}\n" + "else\n" + "{\n" + " vec4 col = texture2D(sTexture, vTexCoord);\n" + " float lighting = (dot(uDiffuseLight, vec3(0.0, 0.0, 1.0)) + uAmbientLight) * uLightMultiplier;\n" + " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n" + "}\n" + "}\n"; + + std::string fragmentSourceRectangular; + fragmentSourceRectangular = "precision mediump float;\n" + + "uniform float uLightingIndentationAmount;\n" + "uniform float uTextureDistortAmount;\n" + "uniform vec3 uDiffuseLight;\n" + "uniform float uAmbientLight;\n" + "uniform float uLightMultiplier;\n" + "uniform float uInsideCircleSizeScale;\n" + "uniform float uRecipInsideCircleSizeScale;\n" + "uniform float uOutsideCircleDepth;\n" + "uniform float uRectangleSizeScale;\n" + "uniform vec4 uEffectRegion;\n" + "varying vec2 vCentredCoord;\n" + + "const float PI = 3.1415927;\n" + + "void main()\n" + "{\n" + // Apply distortion only if the pixel is within the rect specified + "if( (vTexCoord.x > uEffectRegion.x) && (vTexCoord.x < uEffectRegion.z) && (vTexCoord.y > uEffectRegion.y) && (vTexCoord.y < uEffectRegion.w) )\n" + "{ \n" + // get the rect coords to -1..1 range, i.e. circle centred around the center of the rect + " vec2 centredCoord = vCentredCoord;\n" + // clamp coords such that the circle is split into 4 pieces that lie in the corners of the actor. uRectangleScale is the distance along each axis from the centre + // of the actor, e.g. 0.5 is half way along an axis from centre to actor edge. + " vec2 clampedCoord;\n" + " if(centredCoord.x > 0.0)\n" + " {\n" + " if(centredCoord.x < uRectangleSizeScale)\n" + " {\n" + // we are in a rectangular region along this axis, clamp coord to be same as centre pixel + " clampedCoord.x = 0.0;\n" + " }\n" + " else\n" + " {\n" + // we are outside rectangular region along this axis, so we want curvature. + " clampedCoord.x = smoothstep(0.0, 1.0, (centredCoord.x - uRectangleSizeScale) / (1.0 - uRectangleSizeScale));\n" + " }\n" + " }\n" + " else\n" + " {\n" + " if(centredCoord.x > -uRectangleSizeScale)\n" + " {\n" + // we are in a rectangular region along this axis, clamp coord to be same as centre pixel + " clampedCoord.x = 0.0;\n" + " }\n" + " else\n" + " {\n" + // we are outside rectangular region along this axis, so we want curvature. + " clampedCoord.x = -smoothstep(0.0, 1.0, (centredCoord.x + uRectangleSizeScale) / (uRectangleSizeScale - 1.0));\n" + " }\n" + " }\n" + " if(centredCoord.y > 0.0)\n" + " {\n" + " if(centredCoord.y < uRectangleSizeScale)\n" + " {\n" + // we are in a rectangular region along this axis, clamp coord to be same as centre pixel + " clampedCoord.y = 0.0;\n" + " }\n" + " else\n" + " {\n" + // we are outside rectangular region along this axis, so we want curvature. + " clampedCoord.y = smoothstep(0.0, 1.0, (centredCoord.y - uRectangleSizeScale) / (1.0 - uRectangleSizeScale));\n" + " }\n" + " }\n" + " else\n" + " {\n" + " if(centredCoord.y > -uRectangleSizeScale)\n" + " {\n" + // we are in a rectangular region along this axis, clamp coord to be same as centre pixel + " clampedCoord.y = 0.0;\n" + " }\n" + " else\n" + " {\n" + // we are outside rectangular region along this axis, so we want curvature. + " clampedCoord.y = -smoothstep(0.0, 1.0, (centredCoord.y + uRectangleSizeScale) / (uRectangleSizeScale - 1.0));\n" + " }\n" + " }\n" + // get coords in -PI..PI range, i.e. scale above circle for use by trig functions + " vec2 thetaCoord = clampedCoord * PI;\n" + // get a z value for the distorted surface in 0..1 range, using cos for a smooth curve (note, we ignore inside / outside circles since the difference isn't noticeable visually) + " vec2 cosThetaCoord = (cos(thetaCoord) * 0.5) + 0.5;\n" + " float z = cosThetaCoord.x * cosThetaCoord.y;\n" + // find a coordinate representing distance from circle centre, such that we split into inside / outside circles that can have different gradients / normals + " float realDistFromCentre = length(thetaCoord);\n" + " realDistFromCentre = min(PI, realDistFromCentre);\n" // clamp corners of square to vertical normal + " float distFromCentre;\n" + " if(realDistFromCentre <= PI * uInsideCircleSizeScale)\n" + " {\n" + " distFromCentre = realDistFromCentre * uRecipInsideCircleSizeScale * (PI - (uOutsideCircleDepth * PI)) / PI;\n" // inside circle indent, up to outline depth + " }\n" + " else\n" + " {\n" + " distFromCentre = mix(PI - (uOutsideCircleDepth * PI), PI, (realDistFromCentre - ( PI * uInsideCircleSizeScale)) / (PI - (PI * uInsideCircleSizeScale)));\n" // outside circle + " }\n" + // get the normal for the distorted surface, using the fact that the derivative of cos is -sin, finding tangent vector from slope and then normal by cross product... + " float sinThetaCoord = sin(distFromCentre) * uLightingIndentationAmount;\n" // slope, so tangent vec is (1.0, -sin) + // ...2D normal vector along distFromCentre vec is (sin, 1.0), convert to components in 3D. + " vec3 norm = normalize(vec3(thetaCoord.x * sinThetaCoord, thetaCoord.y * sinThetaCoord, 1.0));\n" + // form surface z and project texture onto it. + " float indentAmount = 1.0 / (1.0 - (z * uTextureDistortAmount));\n" + " vec2 distortedCoord = centredCoord * indentAmount;\n" + // Convert the rect coordinates in -1 to 1 range back to the original coordinates + " vec2 texCoord = vec2( ( (distortedCoord.x + 1.0)/(2.0) * (uEffectRegion.z - uEffectRegion.x) + uEffectRegion.x ), ( (distortedCoord.y + 1.0)/(2.0) * (uEffectRegion.w - uEffectRegion.y) + uEffectRegion.y ) );\n" + " vec4 col = texture2D(sTexture, texCoord);\n" + // calc lighting + " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n" + // output col = image * light + // use the lighting value for colors only + " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n" + + "}\n" + "else\n" + "{\n" + " vec4 col = texture2D(sTexture, vTexCoord);\n" + " float lighting = (dot(uDiffuseLight, vec3(0.0, 0.0, 1.0)) + uAmbientLight) * uLightMultiplier;\n" + " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n" + "} \n" + "}\n"; + + + ////////////////////////////////////// + // Create shader effect + // + // + + ShaderEffect shader; + switch(type) + { + case RECTANGULAR: + shader = ShaderEffect::New( vertexSource, fragmentSourceRectangular, GeometryType( GEOMETRY_TYPE_IMAGE ), ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE )); + break; + + case ELLIPTICAL: + shader = ShaderEffect::New( vertexSource, fragmentSourceElliptical, GeometryType( GEOMETRY_TYPE_IMAGE ), ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE )); + break; + + case FIXED: + default: + shader = ShaderEffect::New( vertexSource, fragmentSourceFixed, GeometryType( GEOMETRY_TYPE_IMAGE ), ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE )); + break; + } + SoftButtonEffect handle( shader ); + + + ////////////////////////////////////// + // Register uniform properties + // + // + + // factors that scale the look, defaults + handle.SetUniform(SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_PROPERTY_NAME, SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_DEFAULT); + handle.SetUniform(SOFT_BUTTON_DIFFUSE_LIGHT_PROPERTY_NAME, SOFT_BUTTON_DIFFUSE_LIGHT_DEFAULT); + handle.SetUniform(SOFT_BUTTON_LIGHTING_MULTIPLIER_PROPERTY_NAME, SOFT_BUTTON_LIGHTING_MULTIPLIER_DEFAULT); + if(FIXED != type) + { + handle.SetUniform(SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_PROPERTY_NAME, SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_DEFAULT); + handle.SetUniform(SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_PROPERTY_NAME, SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_DEFAULT); + handle.SetUniform(SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME, SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT); + handle.SetUniform(SOFT_BUTTON_RECIP_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME, 1.0f / SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT); + handle.SetUniform(SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_PROPERTY_NAME, SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_DEFAULT); + handle.SetUniform(SOFT_BUTTON_EFFECT_PIXEL_AREA_PROPERTY_NAME, SOFT_BUTTON_EFFECT_PIXEL_AREA_DEFAULT); + if(RECTANGULAR == type) + { + handle.SetUniform(SOFT_BUTTON_RECTANGLE_SIZE_SCALE_PROPERTY_NAME, SOFT_BUTTON_RECTANGLE_SIZE_SCALE_DEFAULT); + } + + // precalc 1.0 / uInsideCircleSizeScale on CPU to save shader insns, using constraint to tie to the normal property + Property::Index insideCircleSizeScalePropertyIndex = handle.GetPropertyIndex(SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME); + Property::Index recipInsideCircleSizeScalePropertyIndex = handle.GetPropertyIndex(SOFT_BUTTON_RECIP_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME); + Constraint constraint = Constraint::New( recipInsideCircleSizeScalePropertyIndex, LocalSource(insideCircleSizeScalePropertyIndex), ReciprocalConstraint()); + handle.ApplyConstraint(constraint); + } + + return handle; +} + +const std::string& SoftButtonEffect::GetLightingIndentationAmountPropertyName() const +{ + return SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetTextureDistortionAmountPropertyName() const +{ + return SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetAmbientLightAmountPropertyName() const +{ + return SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetDiffuseLightPropertyName() const +{ + return SOFT_BUTTON_DIFFUSE_LIGHT_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetLightingMultiplierPropertyName() const +{ + return SOFT_BUTTON_LIGHTING_MULTIPLIER_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetInsideShapeSizeScalePropertyName() const +{ + return SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetOutsideShapeDepthPropertyName() const +{ + return SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetEffectPixelAreaPropertyName() const +{ + return SOFT_BUTTON_EFFECT_PIXEL_AREA_PROPERTY_NAME; +} + +const std::string& SoftButtonEffect::GetRectangleSizeScalePropertyName() const +{ + return SOFT_BUTTON_RECTANGLE_SIZE_SCALE_PROPERTY_NAME; +} + +} + +} + diff --git a/dali-toolkit/public-api/shader-effects/soft-button-effect.h b/dali-toolkit/public-api/shader-effects/soft-button-effect.h new file mode 100644 index 0000000..3b783f6 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/soft-button-effect.h @@ -0,0 +1,175 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * + * Class for soft button shader that works on a per object basis. Using animatable parameters user can create effect of button pushing in / out. Can be applied to ImageActor only. + * + * Usage example:- + * + * // Create shader used for doing soft button\n + * SoftButtonEffect softButtonEffect = SoftButtonEffect::New(); + * + * // set image actor shader to the soft button one\n + * ImageActor imageActor = ImageActor::New( ... );\n + * imageActor.SetShaderEffect( softButtonEffect ); + * + * // animate a button push, using e.g. AlphaFunctions::Bounce. With these values the button pushes in and pops out slightly at the end\n + * Animation animation = Animation::New( ... );\n + * animation.AnimateTo( Property(softButtonEffect, softButtonEffect.GetLightingIndentationAmountPropertyName()), 0.25f, AlphaFunctions::Bounce, ... );\n + * animation.AnimateTo( Property(softButtonEffect, softButtonEffect.GetLightingIndentationAmountPropertyName()), -0.05f, AlphaFunctions::Bounce, ... );\n + * animation.AnimateTo( Property(softButtonEffect, softButtonEffect.GetTextureDistortionAmountPropertyName()), 0.25f, AlphaFunctions::Bounce, ... );\n + * animation.AnimateTo( Property(softButtonEffect, softButtonEffect.GetTextureDistortionAmountPropertyName()), -0.05f, AlphaFunctions::Bounce, ... );\n + * animation.Play();\n + * + */ +class SoftButtonEffect : public ShaderEffect +{ + +public: + + /** + * Create an uninitialized SoftButtonEffect; this can be initialized with SoftButtonEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + SoftButtonEffect(); + + /** + * Virtual destructor. + */ + virtual ~SoftButtonEffect(); + + typedef enum + { + ELLIPTICAL = 0, /// Button is elliptical + RECTANGULAR, /// Button is rectangular + FIXED /// Button does not indent (move). Useful for matching lighting between areas that do not indent (which can thus use a cheaper shader) and those that do indent. + }Type; + + /** + * Create an initialized SoftButtonEffect + * @param type The type of the soft button, can be either ELLIPTICAL, RECTANGULAR, or FIXED. + * @return A handle to a newly allocated Dali resource. + */ + static SoftButtonEffect New(Type type); + + /** + * Get the name for the lighting indentation amount property (float). Useful for animation. + * This property changes the lighting, to make it look like the button is pushed in. User should animate this in conjunction + * with texture distortion. Allowable values range from [-1..1], higher values give more change in lighting. Default 0.0 (no lighting change). + * See also GetTextureDistortionAmountPropertyName(). + * @return A std::string containing the property name + */ + const std::string& GetLightingIndentationAmountPropertyName() const; + + /** + * Get the name for the texture distortion amount property (float). Useful for animation. + * This property changes the distortion, to make it look like the button is pushed in. User should animate this is conjunction + * with lighting indentation. Allowable values range from [-1..1) - note 1.0 is NOT allowed - higher values give more distortion. Default 0.0 (no distortion). + * See also GetLightingIndentationAmountPropertyName(). + * @return A std::string containing the property name + */ + const std::string& GetTextureDistortionAmountPropertyName() const; + + /** + * Get the name for the ambient lighting amount property (float) + * The ambient light is used in the lighting calculation. Care must be taken to not saturate the image by setting this value too high, + * or the indentation will not look correct. Default 0.15. + * @return A std::string containing the property name + */ + const std::string& GetAmbientLightAmountPropertyName() const; + + /** + * Get the name for the diffuse light property (Vector3). + * The diffuse light is used in the lighting calculation. Default is (0.0, 0.7070168, 0.7070168), i.e. a light angled at the surface from in front and above. Note that + * you need to Normalize() the Vector3 that you set with this property. + * @return A std::string containing the property name + */ + const std::string& GetDiffuseLightPropertyName() const; + + /** + * Get the name for the lighting multiplier property (float). + * The ambient and diffuse lighting is multiplied by this factor. Since a diffuse light at an angle will cause the whole image to darken, even outside the soft button + * indentation, this property can be used to scale the image back up closer to the pixel values of the original diffuse texture. Care must be taken to not saturate the image, + * or the indentation will not look correct. Default 1.2. + * @return A std::string containing the property name + */ + const std::string& GetLightingMultiplierPropertyName() const; + + /** + * Get the name for the inside shape size scale property (float). + * The SoftButtonEffect consists of two shapes, one inside the other. The outside shape fits exactly to the actor, touching its edges but completely contained. The inside + * shape size is given by a multiplier of the outside shape size. For example a value of 0.5 means that the inside shape is half the size of the outside one. Allowable + * values are in the range (0.0 - 1.0), note that 0.0 and 1.0 themselves are not allowed. Default 0.75. + * See also GetOutsideShapeDepthPropertyName(). + * @return A std::string containing the property name + */ + const std::string& GetInsideShapeSizeScalePropertyName() const; + + /** + * Get the name for the outside shape depth property (float). + * The SoftButtonEffect consists of two shapes, one inside the other. The depth of the indentation at the transition between the inside and outside shapes is controlled by + * this property. The values lies in the range [0.0 - 1.0]. A value of 0.0 means the outside shape has no depth (and is thus invisible), value of 1.0 means the outside shape + * has maximum depth (and the inside shape is thus invisible). Default 0.05. + * See also GetInsideShapeSizeScalePropertyName(). + * @return A std::string containing the property name + */ + const std::string& GetOutsideShapeDepthPropertyName() const; + + /** + * Get the name for the effect pixel area property (Vector4). + * The soft button effect is applied within the supplied rect region of the texture. Default values for this is (0.0, 0.0, 1.0, 1.0) which is the entire image with + * 0,0 being the top left and 1.0, 1.0 being the bottom right. If the image texture is split between multiple ImageActors then the developer should specify the + * pixel area of the texture the effect should be applied with. Example, If the Image is split among two ImageActors side by side, with the left one using left half of the + * texture and right one using the right half of the texture then the pixel area value for the left ImageActor will be (0.0, 0.0, 0.5, 1.0) and the pixel area for the right + * will be (0.5, 0.0, 1.0, 1.0). + * @return A std::string containing the property name + */ + const std::string& GetEffectPixelAreaPropertyName() const; + + /** + * Get the name for the rectangle size scale property (float). Only applicable having created this SoftButtonEffect via a call to New() with RECTANGULAR as the type. + * This property can be used to set the mix between proportion of rectangle and proportion of ellipse - the result is a rectangle with rounded corners. If the value is 0.0, + * the shape is an ellipse. If the value is close to 1.0, the shape is close to a rectangle. The value lies in the range [0.0 - 1.0). Note that a value of 1.0 is NOT allowed. + * Default 0.5. + * @return A std::string containing the property name + */ + const std::string& GetRectangleSizeScalePropertyName() const; + + +private: + // Not intended for application developers + SoftButtonEffect(ShaderEffect handle); +}; + +} + +} + +#endif //#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__ + diff --git a/dali-toolkit/public-api/shader-effects/spot-effect.cpp b/dali-toolkit/public-api/shader-effects/spot-effect.cpp new file mode 100644 index 0000000..eed6958 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/spot-effect.cpp @@ -0,0 +1,116 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const float DEFAULT_RADIUS = 0.0f; +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string RADIUS_PROPERTY_NAME( "uRadius" ); + +} // namespace + +SpotEffect::SpotEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +SpotEffect::SpotEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +SpotEffect::~SpotEffect() +{ +} + +SpotEffect SpotEffect::New() +{ + // append the default version + std::string vertexShader( + "uniform mediump vec2 uCenter;\n" + "uniform mediump float uRadius;\n" + "varying mediump float vRange;\n" + "\n" + "void main()\n" + "{\n" + " vec4 world = vec4(aPosition, 1.0);\n" + " \n" + " vec2 d = vec2(world.xy - uCenter);\n" + " float dist = length(d);\n" + " \n" + " float range = (uRadius - dist) / (uRadius);\n" + " vRange = max(0.1, range);\n" + " \n" + " gl_Position = uMvpMatrix * world;\n" + " vTexCoord = aTexCoord;\n" + "}"); + + std::string fragmentShader( + "varying mediump float vRange;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(sTexture, vTexCoord) * vec4(vRange, vRange, vRange, 1.0) * uColor;\n" + "}" ); + + // Create the implementation, temporarily owned on stack + Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::New(vertexShader, fragmentShader, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_GRID )); + + /* Pass ownership to SpotEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::SpotEffect handle( shaderEffect ); + + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.0f, 0.0f) ); + handle.SetUniform( RADIUS_PROPERTY_NAME, DEFAULT_RADIUS ); + + return handle; + +} + +void SpotEffect::SetCenter(const Vector2& center) +{ + SetUniform( CENTER_PROPERTY_NAME, center ); +} + +void SpotEffect::SetRadius(float radius) +{ + SetUniform( RADIUS_PROPERTY_NAME, radius ); +} + +const std::string& SpotEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& SpotEffect::GetRadiusPropertyName() const +{ + return RADIUS_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/spot-effect.h b/dali-toolkit/public-api/shader-effects/spot-effect.h new file mode 100644 index 0000000..3b5efd7 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/spot-effect.h @@ -0,0 +1,86 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SPOT_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_SPOT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * SpotEffect2D is a custom shader effect to achieve spot effects on Image actors + */ +class SpotEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized SpotEffect; this can be initialized with SpotEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + SpotEffect(); + + /** + * Virtual destructor. + */ + virtual ~SpotEffect(); + + /** + * Create an initialized SpotEffect. + * @return A handle to a newly allocated Dali resource. + */ + static SpotEffect New(); + + /** + * Set the center of the spot. + * @param[in] center The center in Vector2. + */ + void SetCenter(const Vector2& center); + + /** + * Set the radius of the spot. + * @param[in] radius The radius in float. + */ + void SetRadius(float radius); + + /** + * Get the name for the center property + * @return A std::string containing the property name + */ + const std::string& GetCenterPropertyName() const; + + /** + * Get the name for the radius property + * @return A std::string containing the property name + */ + const std::string& GetRadiusPropertyName() const; + +private: + SpotEffect(ShaderEffect handle); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_SPOT_H__ diff --git a/dali-toolkit/public-api/shader-effects/square-dissolve-effect.cpp b/dali-toolkit/public-api/shader-effects/square-dissolve-effect.cpp new file mode 100644 index 0000000..72875cc --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/square-dissolve-effect.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string STEP_PROPERTY_NAME( "uStep" ); +const std::string ROWS_PROPERTY_NAME( "uRows" ); +const std::string COLUMNS_PROPERTY_NAME( "uColumns" ); +const std::string TEXSIZE_PROPERTY_NAME( "texSize" ); + +} // namespace + +SquareDissolveEffect::SquareDissolveEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +SquareDissolveEffect::SquareDissolveEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +SquareDissolveEffect::~SquareDissolveEffect() +{ +} + +SquareDissolveEffect SquareDissolveEffect::New() +{ + // variable "uStep" range scope : [0.0, 1.0] + std::string fragmentShader( + "uniform vec2 texSize; \n" + "uniform float uStep; \n" + "uniform float uRows; \n" + "uniform float uColumns; \n" + "void main() \n" + "{ \n" + " vec2 mosaicSize = vec2(1.0 / uRows, 1.0 / uColumns); \n" + " vec2 intXY = vec2(vTexCoord.x * texSize.x, vTexCoord.y * texSize.y); \n" + " vec2 XYMosaic = vec2(floor(intXY.x / mosaicSize.x) * mosaicSize.x, floor(intXY.y / mosaicSize.y) * mosaicSize.y); \n" + " vec2 UVMosaic = vec2(XYMosaic.x /texSize.x, XYMosaic.y / texSize.y); \n" + " vec4 noiseVec = texture2D(sEffect, UVMosaic); \n" + " float intensity = (noiseVec[0] + noiseVec[1] + noiseVec[2] + noiseVec[3]) / 4.0; \n" + " if(intensity < uStep) \n" + " gl_FragColor = vec4(0.1, 0.1, 0.1, 1.0); \n" + " else \n" + " gl_FragColor = texture2D(sTexture, vTexCoord); \n" + " gl_FragColor *= uColor; \n" + "} \n" ); + + // Create the implementation, temporarily owned on stack + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + "", + fragmentShader, + GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID )); + + /* Pass ownership to SquareDissolveEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::SquareDissolveEffect handle( shaderEffectCustom ); + + handle.SetUniform( TEXSIZE_PROPERTY_NAME, Vector2(1.0f, 1.0f) );//COORDINATE_TYPE_DEFAULT + handle.SetUniform( STEP_PROPERTY_NAME, 0.1f); + handle.SetUniform( ROWS_PROPERTY_NAME, 25.0f); + handle.SetUniform( COLUMNS_PROPERTY_NAME, 25.0f); + return handle; +} + +void SquareDissolveEffect::SetStep( float step ) +{ + SetUniform( STEP_PROPERTY_NAME, step ); +} + +void SquareDissolveEffect::SetRows(float rows) +{ + SetUniform(ROWS_PROPERTY_NAME, rows); +} + +void SquareDissolveEffect::SetColumns(float columns) +{ + SetUniform(COLUMNS_PROPERTY_NAME, columns); +} + +void SquareDissolveEffect::SetTextureSize(const Vector2& textureSize) +{ + SetUniform(TEXSIZE_PROPERTY_NAME, textureSize); +} + +const std::string& SquareDissolveEffect::GetStepPropertyName() const +{ + return STEP_PROPERTY_NAME; +} + +const std::string& SquareDissolveEffect::GetRowsPropertyName() const +{ + return ROWS_PROPERTY_NAME; +} + +const std::string& SquareDissolveEffect::GetColumnsPropertyName() const +{ + return COLUMNS_PROPERTY_NAME; +} + +const std::string& SquareDissolveEffect::GetTexSizePropertyName() const +{ + return TEXSIZE_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/square-dissolve-effect.h b/dali-toolkit/public-api/shader-effects/square-dissolve-effect.h new file mode 100644 index 0000000..a3ff470 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/square-dissolve-effect.h @@ -0,0 +1,113 @@ +#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SQUARE_H__ +#define __DALI_TOOLKIT_SHADER_EFFECT_SQUARE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +/** + * SquareDissolveEffect is a custom shader effect to achieve square effects in Image actors + */ +class SquareDissolveEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized SquareDissolveEffect; this can be initialized with BendyEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + SquareDissolveEffect(); + + /** + * Virtual destructor. + */ + virtual ~SquareDissolveEffect(); + + /** + * Create an initialized SquareDissolveEffect. + * @return A handle to a newly allocated Dali resource. + */ + static SquareDissolveEffect New(); + + /** + * Set the step of the square effect. + * @param [in] step the new step. + */ + void SetStep(float step); + + /** + * Set the rows of the square dissolve effect. + * @param [in] rows the new rows value. + */ + void SetRows(float rows); + + /** + * Set the columns of the square dissolve effect. + * @param [in] columns the new columns value. + */ + void SetColumns(float columns); + + /** + * Set the texture size of the square dissolve. + * @param[in] textureSize The texture size in Vector2. + */ + void SetTextureSize(const Vector2& textureSize); + + /** + * Get the name for the step property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetStepPropertyName() const; + + /** + * Get the name for the rows property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetRowsPropertyName() const; + + /** + * Get the name for the columns property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetColumnsPropertyName() const; + + /** + * Get the name for the texSize property + * which can be used in Animation API's + * @return A std::string containing the property name + */ + const std::string& GetTexSizePropertyName() const; + +private: // Not intended for application developers + SquareDissolveEffect(ShaderEffect handle); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_SHADER_EFFECT_SQUARE_H__ diff --git a/dali-toolkit/public-api/shader-effects/swirl-effect.cpp b/dali-toolkit/public-api/shader-effects/swirl-effect.cpp new file mode 100644 index 0000000..13b2333 --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/swirl-effect.cpp @@ -0,0 +1,136 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace +{ + +const std::string ANGLE_PROPERTY_NAME( "uAngle" ); +const std::string CENTER_PROPERTY_NAME( "uCenter" ); +const std::string RADIUS_PROPERTY_NAME( "uRadius" ); + +} // namespace + +SwirlEffect::SwirlEffect() +{ +} + +//Call the Parent copy constructor to add reference to the implementation for this object +SwirlEffect::SwirlEffect(ShaderEffect handle) +:ShaderEffect(handle) +{ +} + +SwirlEffect::~SwirlEffect() +{ +} + + +SwirlEffect SwirlEffect::New(bool warp) +{ + // append the default version + std::string fragmentShader( + "uniform vec2 uTextureSize;\n" + "uniform float uRadius;\n" + "uniform float uAngle;\n" + "uniform vec2 uCenter;\n" + "void main()\n" + "{\n" + " vec2 textureCenter = (sTextureRect.xy + sTextureRect.zw) * 0.5;\n" + " textureCenter = vTexCoord.st - textureCenter;\n" + " float distance = length(textureCenter);\n" + " if (distance >= uRadius)\n" + " discard;\n" + " float percent = (uRadius - distance) / uRadius;\n" + " float theta = percent * percent * uAngle * 4.0;\n" + " float sinTheta = sin(theta);\n" + " float cosTheta = cos(theta);\n" ); + // if warp, loose the sign from sin + if( warp ) + { + fragmentShader.append( + " textureCenter = vec2( dot( textureCenter, vec2(cosTheta, sinTheta) ), " + " dot( textureCenter, vec2(sinTheta, cosTheta) ) );\n" ); + } + else + { + fragmentShader.append( + " textureCenter = vec2( dot( textureCenter, vec2(cosTheta, -sinTheta) ), " + " dot( textureCenter, vec2(sinTheta, cosTheta) ) );\n" ); + } + fragmentShader.append( + " textureCenter += uCenter;\n" + " gl_FragColor = texture2D( sTexture, textureCenter ) * uColor;\n" + "}" ); + + // Create the implementation, temporarily owned on stack, + Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( + "", + fragmentShader, + Dali::GeometryType( GEOMETRY_TYPE_IMAGE ), + ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING | ShaderEffect::HINT_GRID )); + + /* Pass ownership to SwirlEffect through overloaded constructor, So that it now has access to the + Dali::ShaderEffect implementation */ + Dali::Toolkit::SwirlEffect handle( shaderEffectCustom ); + + handle.SetUniform( ANGLE_PROPERTY_NAME, 0.0f ); + handle.SetUniform( CENTER_PROPERTY_NAME, Vector2(0.5f, 0.5f) ); + handle.SetUniform( RADIUS_PROPERTY_NAME, 1.0f ); + + return handle; +} + +void SwirlEffect::SetAngle(float angle) +{ + SetUniform( ANGLE_PROPERTY_NAME, angle ); +} + +void SwirlEffect::SetCenter(const Vector2& center) +{ + SetUniform( CENTER_PROPERTY_NAME, center ); +} + +void SwirlEffect::SetRadius(float radius) +{ + SetUniform( RADIUS_PROPERTY_NAME, radius ); +} + +const std::string& SwirlEffect::GetAnglePropertyName() const +{ + return ANGLE_PROPERTY_NAME; +} + +const std::string& SwirlEffect::GetCenterPropertyName() const +{ + return CENTER_PROPERTY_NAME; +} + +const std::string& SwirlEffect::GetRadiusPropertyName() const +{ + return RADIUS_PROPERTY_NAME; +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/water-effect.cpp b/dali-toolkit/public-api/shader-effects/water-effect.cpp new file mode 100644 index 0000000..2b2ac1e --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/water-effect.cpp @@ -0,0 +1,101 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +WaterEffect::WaterEffect() +{ +} + +// Call the Parent copy constructor to add reference to the implementation for this object +WaterEffect::WaterEffect( ShaderEffect handle, Internal::WaterEffect* shaderExtension ) +: ShaderEffect( handle ) +{ + AttachExtension( shaderExtension ); +} + +WaterEffect::~WaterEffect() +{ +} + +WaterEffect WaterEffect::New( unsigned int numberOfWaves ) +{ + return Internal::WaterEffect::CreateShaderEffect( numberOfWaves ); +} + +unsigned int WaterEffect::GetNumberOfWaves() const +{ + return GetImpl(*this).GetNumberOfWaves(); +} + +void WaterEffect::SetAmplitude( unsigned int index, float amplitude ) +{ + GetImpl(*this).SetAmplitude( index, amplitude ); +} + +void WaterEffect::SetCenter( unsigned int index, const Vector2& center ) +{ + GetImpl(*this).SetCenter( index, center ); +} + +void WaterEffect::SetPropagation( unsigned int index, float radius ) +{ + GetImpl(*this).SetPropagation( index, radius ); +} + +float WaterEffect::GetAmplitude( unsigned int index ) const +{ + return GetImpl(*this).GetAmplitude( index ); +} + +Vector2 WaterEffect::GetCenter( unsigned int index ) const +{ + return GetImpl(*this).GetCenter( index ); +} + +float WaterEffect::GetPropagation( unsigned int index ) const +{ + return GetImpl(*this).GetPropagation( index ); +} + +std::string WaterEffect::GetAmplitudePropertyName( unsigned int index ) const +{ + return GetImpl(*this).GetAmplitudePropertyName( index ); +} + +std::string WaterEffect::GetCenterPropertyName( unsigned int index ) const +{ + return GetImpl(*this).GetCenterPropertyName( index ); +} + +std::string WaterEffect::GetPropagationPropertyName( unsigned int index ) const +{ + return GetImpl(*this).GetPropagationPropertyName( index ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/shader-effects/water-effect.h b/dali-toolkit/public-api/shader-effects/water-effect.h new file mode 100644 index 0000000..9ab412c --- /dev/null +++ b/dali-toolkit/public-api/shader-effects/water-effect.h @@ -0,0 +1,165 @@ +#ifndef __DALI_TOOLKIT_WATER_EFFECT_H__ +#define __DALI_TOOLKIT_WATER_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * WaterEffect implementation class + */ +class WaterEffect; + +} // namespace Internal + +/** + * WaterEffect is a custom shader effect to achieve water like effects on Image actors + * + * Usage example: + * + * WaterEffect waterEffect = WaterEffect::New( numberOfDrops ); + * + * actor.SetShaderEffect( waterEffect ); + * + * // Set initial values + * waterEffect.SetCenter( 1, centerPosition ); + * waterEffect.SetPropagation( 1, INITIAL_RADIUS ); + * + * // Animate the wave propagation + * std::string propertyName = waterEffect.GetPropagationPropertyName( 1 ); + * animation.AnimateTo( Property(waterEffect, propertyName), FINAL_RADIUS ); + */ +class WaterEffect : public ShaderEffect +{ +public: + + /** + * Create an uninitialized WaterEffect; this can be initialized with WaterEffect::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + WaterEffect(); + + /** + * Virtual destructor. + */ + virtual ~WaterEffect(); + + /** + * Create an initialized WaterEffect. + * @param [in] numberOfWaves The number of waves. + * @return A handle to a newly allocated Dali resource + */ + static WaterEffect New( unsigned int numberOfWaves ); + + /** + * Get the number of waves the shader supports. + * @return The number of waves in the shader. + */ + unsigned int GetNumberOfWaves() const; + + /** + * Set the amplitude of a wave. + * @param [in] index The index of the wave to change + * @param [in] amplitude The new amplitude of the wave + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + void SetAmplitude( unsigned int index, float amplitude ); + + /** + * Set the center point of a wave in texture coordinates. + * @param [in] index The index of the wave to change + * @param [in] center The center point of the wave + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + void SetCenter( unsigned int index, const Vector2& center ); + + /** + * Set the propagation radius of a wave. + * @param [in] index The index of the wave to change + * @param [in] radius The propagation radius + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + void SetPropagation( unsigned int index, float radius ); + + /** + * Get the amplitude of a wave. + * @param [in] index The index of the wave + * @return The new amplitude of the wave + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + float GetAmplitude( unsigned int index ) const; + + /** + * Get the center point of a wave in texture coordinates. + * @param [in] index The index of the wave + * @return The center point of the wave + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + Vector2 GetCenter( unsigned int index ) const; + + /** + * Get the propagation radius of a wave. + * @param [in] index The index of the wave + * @return The propagation radius + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + float GetPropagation( unsigned int index ) const; + + /** + * Get the name for the amplitude property of a wave. + * @param [in] index The index of the wave + * @return A std::string containing the property name + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + std::string GetAmplitudePropertyName( unsigned int index ) const; + + /** + * Get the name for the center property of a wave. + * @param [in] index The index of the wave + * @return A std::string containing the property name + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + std::string GetCenterPropertyName( unsigned int index ) const; + + /** + * Get the name for the propagation property. + * @param [in] index The index of the wave + * @return A std::string containing the property name + * @pre index has to be between 0 and GetNumberOfWaves() - 1 + */ + std::string GetPropagationPropertyName( unsigned int index ) const; + +public: // Not intended for developer use + + WaterEffect( ShaderEffect handle, Internal::WaterEffect* shaderExtension ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_WATER_EFFECT_H__ diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.cpp b/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.cpp new file mode 100644 index 0000000..fe74abe --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-cross-effect.h" + +//INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +CubeTransitionCrossEffect::CubeTransitionCrossEffect(Internal::CubeTransitionCrossEffect* impl) +: CubeTransitionEffect(impl) +{ +} + +CubeTransitionCrossEffect CubeTransitionCrossEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + return Internal::CubeTransitionCrossEffect::New( numRows, numColumns, viewAreaSize ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.h b/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.h new file mode 100644 index 0000000..79828b8 --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-cross-effect.h @@ -0,0 +1,70 @@ +#ifndef __DALI_TOOLKIT_CUBE_TRANSITION_CROSS_EFFECT_H__ +#define __DALI_TOOLKIT_CUBE_TRANSITION_CROSS_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + /** + * CubeTransitionCrossEffect implementation class + */ + class CubeTransitionCrossEffect; + +} // namespace Internal + +/** + * SubClass of CubeTransitionEffect + * Rotate the neighboring cubes in perpendicular directions to transite from one image to another + */ +class CubeTransitionCrossEffect : public CubeTransitionEffect +{ + +public: + + /** + * Create an initialized CubeTransitionCrossEffect + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + * @return The initialized CubeTransitionCrossEffect object + */ + static CubeTransitionCrossEffect New( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + +public: // Not intended for developer use + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + CubeTransitionCrossEffect( Internal::CubeTransitionCrossEffect* impl ); + +}; // class CubeTransitionCrossEffect + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_CUBE_TRANSITION_CROSS_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-effect.cpp b/dali-toolkit/public-api/transition-effects/cube-transition-effect.cpp new file mode 100644 index 0000000..24cbf81 --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-effect.cpp @@ -0,0 +1,116 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-effect.h" + +//INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +const char* const CubeTransitionEffect::SIGNAL_TRANSITION_COMPLETED = "transition-completed"; + +CubeTransitionEffect::CubeTransitionEffect() +{ +} + +CubeTransitionEffect::~CubeTransitionEffect() +{ +} + +CubeTransitionEffect::CubeTransitionEffect(Internal::CubeTransitionEffect* impl) +: BaseHandle(impl) +{ +} + +void CubeTransitionEffect::SetTransitionDuration( float duration ) +{ + GetImpl(*this).SetTransitionDuration( duration ); +} + +float CubeTransitionEffect::GetTransitionDuration() const +{ + return GetImpl(*this).GetTransitionDuration(); +} + +void CubeTransitionEffect::SetCubeDisplacement( float displacement ) +{ + GetImpl(*this).SetCubeDisplacement( displacement ); +} + +float CubeTransitionEffect::GetCubeDisplacement() const +{ + return GetImpl(*this).GetCubeDisplacement(); +} + +Actor CubeTransitionEffect::GetRoot() +{ + return GetImpl(*this).GetRoot(); +} + +bool CubeTransitionEffect::IsTransiting() +{ + return GetImpl(*this).IsTransiting(); +} + +void CubeTransitionEffect::SetCurrentImage(ImageActor imageActor) +{ + GetImpl(*this).SetCurrentImage( imageActor ); +} + +void CubeTransitionEffect::SetTargetImage(ImageActor imageActor) +{ + GetImpl(*this).SetTargetImage( imageActor ); +} + +void CubeTransitionEffect::StartTransition( bool toNextImage ) +{ + GetImpl(*this).StartTransition( toNextImage ); +} + +void CubeTransitionEffect::StartTransition( Vector2 panPosition, Vector2 panDisplacement ) +{ + GetImpl(*this).StartTransition( panPosition, panDisplacement ); +} + +void CubeTransitionEffect::PauseTransition() +{ + GetImpl(*this).PauseTransition(); +} + +void CubeTransitionEffect::ResumeTransition() +{ + GetImpl(*this).ResumeTransition(); +} + +void CubeTransitionEffect::StopTransition() +{ + GetImpl(*this).StopTransition(); +} + +CubeTransitionEffect::TransitionCompletedSignalV2& CubeTransitionEffect::TransitionCompletedSignal() +{ + return GetImpl( *this ).TransitionCompletedSignal(); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-effect.h b/dali-toolkit/public-api/transition-effects/cube-transition-effect.h new file mode 100644 index 0000000..6692cff --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-effect.h @@ -0,0 +1,204 @@ +#ifndef __DALI_TOOLKIT_CUBE_TRANSITION_EFFECT_H__ +#define __DALI_TOOLKIT_CUBE_TRANSITION_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * CubeTransitionEffect implementation class + */ +class CubeTransitionEffect; + +} // namespace Internal + +/** + * CubeTransitionEffect is a base class of custom transition effect on Image actors + * The two images are partitioned into tiles and serves as two perpendicular faces of cubes + * By rotating these cubes to transit from one image to another + * + * Usage example: + * + * @code + * + * //create a new CubeTransitionEffect + * //use the New funtion of subclass ( CubeTransitionWaveEffect or CubeTransitionCrossEffect ) + * CubeTransitionEffect cubeEffect = CubeTransitionWaveEffect::New(numRows, numColumns, viewAreaSize); + * + * //set the duration of transition animation + * cubeEffect.SetTransitionDuration( animationDuration ); + * + * //set the displacement of bouncing movement during cube's rotation + * cubeEffect.SetCubeDisplacement( cubeDisplacement ); + * + * // Add to stage + * stage.Add( cubeEffect.GetRoot() ); + * + * // Set the current image, + * // only need to set at beginning or when the current image was transited to with no effect or other effect + * cubeEffect.SetCurrentImage( firstImageActor ); + * + * // Set target image, paired with startTransition. These two steps would be repeated as needed + * cubeEffect.SetTargetimage( secondImageActor ); + * // Activate the effect + * // no param / param ture: default horizontally left panGesture + * // or param false: default horizontally right panGesture + * // or params position & displacement: specified the panGesture + * cubeEffect.StartTransition( ); + * + * @endcode + */ +class CubeTransitionEffect : public BaseHandle +{ +public: + + /** + * Create an uninitialized CubeTransitionEffect; + * this can be initialized by New function of its subclass + */ + CubeTransitionEffect(); + + /** + * Destructor + */ + ~CubeTransitionEffect(); + + /** + * Set the duration of transition animation + * @param[in] duration The duration of transition animation + */ + void SetTransitionDuration( float duration ); + + /** + * Get the duration of transition animation + * @return duration The duration of transition animation + */ + float GetTransitionDuration() const; + + /** + * Set the displacement of bouncing animation during cube's rotation + * @param[in] displacement The displacement of bouncing animation + */ + void SetCubeDisplacement( float displacement ); + + /** + * Getet the displacement of bouncing animation during cube's rotation + * @return displacement The displacement of bouncing animation + */ + float GetCubeDisplacement() const; + + /** + * Return the transition effect root actor, should then be added to stage + * @return The transition effect root actor + */ + Actor GetRoot(); + + /** + * Return the transition status + * @return True if the transition is under processing; false if finished + */ + bool IsTransiting(); + + /** + * Set the current image to transite from + * if using this same effect continuely, only need to set once + * @param[in] imageActor The current imageActor + */ + void SetCurrentImage(ImageActor imageActor); + + /** + * Set the target image to transit to + * @param[in] imageActor The new imageActor showing on stage + */ + void SetTargetImage(ImageActor imageActor); + + /** + * Activate the transition animation with horizontally left/right panGesture + * @pre target image is set + * @param[in] toNextImage Horizontally left panGesture if ture, horizontally right if false + */ + void StartTransition( bool toNextImage = true ); + + /** + * Activate the transition animation with specified panGesture + * @pre target image is set + * @param[in] panPosition The press down position of panGesture + * @param[in] panDisplacement The displacement vector of panGesture + */ + void StartTransition( Vector2 panPosition, Vector2 panDisplacement ); + + /** + * Pause the transition animation. + * It does nothing if the animation is not running. + */ + void PauseTransition(); + + /** + * Re-Activate the transition animation after it is paused by calling PauseTransition(). + * It does nothing in other cases. + */ + void ResumeTransition(); + + /** + * Inactivate the transition animation if it is running. + * Also set the rotation and position of cubes, colors of tile to the same as the final state when the animation if finished completely + * It does nothing if the animation is not running. + */ + void StopTransition(); + +public: //Signal + + //signal name + static const char* const SIGNAL_TRANSITION_COMPLETED; + + //Transition animation completed signal + typedef SignalV2< void ( CubeTransitionEffect, ImageActor ) > TransitionCompletedSignalV2; + + /** + * Signal emitted when the transition has completed animation + * A callback of the following type may be connected + * @code + * void YourCallbackName( CubeTransitionEffect cubeEffect, ImageActor currentImage ); + * @endcode + * @return The Signal to connect to. + */ + TransitionCompletedSignalV2& TransitionCompletedSignal(); + +public: // Not intended for developer use + + CubeTransitionEffect( Internal::CubeTransitionEffect* impl ); + +}; //class CubeTransitionEffect + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_CUBE_TRANSITION_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.cpp b/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.cpp new file mode 100644 index 0000000..3489df2 --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-fold-effect.h" + +//INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +CubeTransitionFoldEffect::CubeTransitionFoldEffect(Internal::CubeTransitionFoldEffect* impl) +: CubeTransitionEffect(impl) +{ +} + +CubeTransitionFoldEffect CubeTransitionFoldEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + return Internal::CubeTransitionFoldEffect::New( numRows, numColumns, viewAreaSize ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.h b/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.h new file mode 100644 index 0000000..b2ca86f --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-fold-effect.h @@ -0,0 +1,70 @@ +#ifndef __DALI_TOOLKIT_CUBE_TRANSITION_FOLD_EFFECT_H__ +#define __DALI_TOOLKIT_CUBE_TRANSITION_FOLD_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + /** + * CubeTransitionFoldEffectimplementation class + */ + class CubeTransitionFoldEffect; + +} // namespace Internal + +/** + * SubClass of CubeTransitionEffect + * Rotate the neighboring cubes in opposite directions to transite from one image to another + */ +class CubeTransitionFoldEffect : public CubeTransitionEffect +{ + +public: + + /** + * Create an initialized CubeTransitionFoldEffect + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + * @return The initialized CubeTransitionFoldEffect object + */ + static CubeTransitionFoldEffect New( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + +public: // Not intended for developer use + + /** + * This constructor is used by Dali New() methods. + * @param [in] impl A pointer to a newly allocated Dali resource + */ + CubeTransitionFoldEffect( Internal::CubeTransitionFoldEffect* impl ); + +}; // class CubeTransitionFoldEffect + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_CUBE_TRANSITION_FOLD_EFFECT_H__ */ diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.cpp b/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.cpp new file mode 100644 index 0000000..330fa48 --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.cpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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 "cube-transition-wave-effect.h" + +//INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +CubeTransitionWaveEffect::CubeTransitionWaveEffect(Internal::CubeTransitionWaveEffect* impl) +: CubeTransitionEffect(impl) +{ +} + +CubeTransitionWaveEffect CubeTransitionWaveEffect::New(unsigned int numRows, unsigned int numColumns, Size viewAreaSize) +{ + return Internal::CubeTransitionWaveEffect::New( numRows, numColumns, viewAreaSize ); +} + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.h b/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.h new file mode 100644 index 0000000..a37c32f --- /dev/null +++ b/dali-toolkit/public-api/transition-effects/cube-transition-wave-effect.h @@ -0,0 +1,69 @@ +#ifndef __DALI_TOOLKIT_CUBE_TRANSITION_WAVE_EFFECT_H__ +#define __DALI_TOOLKIT_CUBE_TRANSITION_WAVE_EFFECT_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.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://floralicense.org/license/ +// +// 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. +// +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + /** + * CubeTransitionWaveEffect implementation class + */ + class CubeTransitionWaveEffect; + +} // namespace Internal + +/** + * SubClass of CubeTransitionEffect + * Rotate the cubes successively according to the finger movement to achieve wave-like transition effect + */ +class CubeTransitionWaveEffect : public CubeTransitionEffect +{ + +public: + + /** + * Create an initialized CubeTransitionWaveEffect + * @param[in] numRows How many rows of cubes + * @param[in] numColumns How many columns of cubes + * @param[in] viewAreaSize The size of view area for this transition effect + * @return The initialized CubeTransitionWaveEffect object + */ + static CubeTransitionWaveEffect New( unsigned int numRows, unsigned int numColumns, Size viewAreaSize ); + + +public: // Not intended for developer use + + /** + * This constructor is used by Dali New() methods. + * @param[in] impl A pointer to a newly allocated Dali resource + */ + CubeTransitionWaveEffect( Internal::CubeTransitionWaveEffect* impl ); + +}; // class CubeTransitionWaveEffect + +} // namespace Toolkit + +} // namespace Dali +#endif /* __DALI_TOOLKIT_CUBE_TRANSITION_WAVE_EFFECT_H__ */ diff --git a/packaging/dali-toolkit.spec b/packaging/dali-toolkit.spec new file mode 100644 index 0000000..f29f52b --- /dev/null +++ b/packaging/dali-toolkit.spec @@ -0,0 +1,110 @@ +Name: dali-toolkit +Summary: The OpenGLES Canvas Core Library Toolkit +Version: 0.9.6 +Release: 1 +Group: System/Libraries +License: Flora +URL: TO_BE_FILLED +Source0: %{name}-%{version}.tar.gz + +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +Requires: boost +Requires: dali-adaptor-dali-feedback-plugin +Requires: dali +# Do NOT put an adaptor here - it is an application choice which adaptor to use +BuildRequires: pkgconfig +BuildRequires: pkgconfig(dlog) +BuildRequires: boost-devel +BuildRequires: dali-devel +BuildRequires: dali-adaptor-devel +BuildRequires: fribidi-devel +BuildRequires: pkgconfig(utilX) +BuildRequires: sec-product-features + +%description +The OpenGLES Canvas Core Library Toolkit + +############################## +# devel +############################## +%package devel +Summary: Application development package for the OpenGLES Canvas toolkit +Group: Development/Libs +Requires: %{name} = %{version}-%{release} +Requires: boost-devel + +%description devel +Application development package for the OpenGLES Canvas toolkit + +############################## +# Preparation +############################## +%prep +%setup -q +%define dali_data_rw_dir /opt/usr/share/dali/ +%define dali_data_ro_dir /usr/share/dali/ +%define dali_toolkit_image_files %{dali_data_ro_dir}/toolkit/images/ +#%define dev_include_path %{_includedir}/dali/internal +%define dev_include_path %{_includedir} + +############################## +# Build +############################## +%build +PREFIX="/usr" +CXXFLAGS+=" -Wall -g -fPIC -fvisibility-inlines-hidden -fdata-sections -ffunction-sections " +LDFLAGS+=" -Wl,--rpath=$PREFIX/lib -Wl,--as-needed -Wl,--gc-sections " + +%if 0%{?tizen_build_binary_release_type_eng} +export CFLAGS="$CFLAGS -DTIZEN_ENGINEER_MODE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_ENGINEER_MODE" +export FFLAGS="$FFLAGS -DTIZEN_ENGINEER_MODE" +%endif + +libtoolize --force +cd %{_builddir}/dali-toolkit-%{version}/build/slp && autoreconf --install +cd %{_builddir}/dali-toolkit-%{version}/build/slp && CXXFLAGS=$CXXFLAGS LDFLAGS=$LDFLAGS DALI_DATA_RW_DIR="%{dali_data_rw_dir}" DALI_DATA_RO_DIR="%{dali_data_ro_dir}" ./configure --prefix=$PREFIX + +make %{?jobs:-j%jobs} + +############################## +# Installation +############################## +%install +rm -rf %{buildroot} +cd build/slp +%make_install DALI_DATA_RW_DIR="%{dali_data_rw_dir}" DALI_DATA_RO_DIR="%{dali_data_ro_dir}" + +# LICENSE +mkdir -p %{buildroot}/usr/share/license +cp -af %{_builddir}/%{name}-%{version}/LICENSE %{buildroot}/usr/share/license/%{name} + +############################## +# Post Install +############################## +%post +/sbin/ldconfig +exit 0 + +############################## +# Post Uninstall +############################## +%postun +/sbin/ldconfig +exit 0 + +############################## +# Files in Binary Packages +############################## +%files +%manifest dali-toolkit.manifest +%defattr(-,root,root,-) +%{_libdir}/lib%{name}.so* +%{dali_toolkit_image_files}/* +%{_datadir}/license/%{name} + +%files devel +%defattr(-,root,root,-) +%{dev_include_path}/%{name}/* +%{_libdir}/pkgconfig/*.pc -- 2.7.4