2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "dali-table-view.h"
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/images/distance-field.h>
24 #include <dali-toolkit/devel-api/controls/control-devel.h>
25 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
26 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
27 #include <dali-toolkit/dali-toolkit.h>
28 #include <dali-toolkit/devel-api/accessibility-manager/accessibility-manager.h>
29 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
30 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
31 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
32 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
33 #include <dali/devel-api/actors/actor-devel.h>
34 #include <dali/devel-api/images/distance-field.h>
38 #include "shared/execute-process.h"
39 #include "shared/utility.h"
40 #include "shared/view.h"
43 using namespace Dali::Toolkit;
45 ///////////////////////////////////////////////////////////////////////////////
49 const std::string LOGO_PATH(DEMO_IMAGE_DIR "Logo-for-demo.png");
51 // Keyboard focus effect constants.
52 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f; ///< The total duration of the keyboard focus animation
53 const int32_t KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT = 5; ///< The number of loops for the keyboard focus animation
54 const float KEYBOARD_FOCUS_FINAL_SCALE_FLOAT = 1.05f; ///< The final scale of the focus highlight
55 const float KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT = 1.18f; ///< The scale of the focus highlight during the animation
56 const float KEYBOARD_FOCUS_FINAL_ALPHA = 0.7f; ///< The final alpha of the focus highlight
57 const float KEYBOARD_FOCUS_ANIMATING_ALPHA = 0.0f; ///< The alpha of the focus highlight during the animation
58 const float KEYBOARD_FOCUS_FADE_PERCENTAGE = 0.16f; ///< The duration of the fade (from translucent to the final-alpha) as a percentage of the overall animation duration
59 const Vector3 KEYBOARD_FOCUS_FINAL_SCALE(KEYBOARD_FOCUS_FINAL_SCALE_FLOAT, KEYBOARD_FOCUS_FINAL_SCALE_FLOAT, KEYBOARD_FOCUS_FINAL_SCALE_FLOAT); ///< @see KEYBOARD_FOCUS_START_SCALE_FLOAT
60 const Vector3 FOCUS_INDICATOR_ANIMATING_SCALE(KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT, KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT, KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT); ///< @see KEYBOARD_FOCUS_END_SCALE_FLOAT
61 const float KEYBOARD_FOCUS_MID_KEY_FRAME_TIME = KEYBOARD_FOCUS_ANIMATION_DURATION - (KEYBOARD_FOCUS_ANIMATION_DURATION * KEYBOARD_FOCUS_FADE_PERCENTAGE); ///< Time of the mid key-frame
63 const float TILE_LABEL_PADDING = 8.0f; ///< Border between edge of tile and the example text
64 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f; ///< Time to perform button scale effect.
65 const float ROTATE_ANIMATION_TIME = 0.5f; ///< Time to perform rotate effect.
66 const int MAX_PAGES = 256; ///< Maximum pages (arbitrary safety limit)
67 const int EXAMPLES_PER_ROW = 3;
68 const int ROWS_PER_PAGE = 3;
69 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
70 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
71 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
72 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f); ///< ScrollView's relative size to its parent
73 const Vector3 TABLE_RELATIVE_SIZE(0.95f, 0.9f, 0.8f); ///< TableView's relative size to the entire stage. The Y value means sum of the logo and table relative heights.
74 const float STENCIL_RELATIVE_SIZE = 1.0f;
76 const float EFFECT_SNAP_DURATION = 0.66f; ///< Scroll Snap Duration for Effects
77 const float EFFECT_FLICK_DURATION = 0.5f; ///< Scroll Flick Duration for Effects
78 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
80 const char* const BUBBLE_COLOR_STYLE_NAME[] =
86 const int NUMBER_OF_BUBBLE_COLORS(sizeof(BUBBLE_COLOR_STYLE_NAME) / sizeof(BUBBLE_COLOR_STYLE_NAME[0]));
88 const char* const SHAPE_IMAGE_TABLE[] =
90 DEMO_IMAGE_DIR "shape-circle.png",
91 DEMO_IMAGE_DIR "shape-bubble.png"};
92 const int NUMBER_OF_SHAPE_IMAGES(sizeof(SHAPE_IMAGE_TABLE) / sizeof(SHAPE_IMAGE_TABLE[0]));
94 const int NUM_BACKGROUND_IMAGES = 18;
95 const float BACKGROUND_SPREAD_SCALE = 1.5f;
97 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
99 const float BUBBLE_MIN_Z = -1.0;
100 const float BUBBLE_MAX_Z = 0.0f;
102 const char* const DEMO_BUILD_DATE = __DATE__ " " __TIME__;
105 * Creates the background image
107 Control CreateBackground(std::string stylename)
109 Control background = Control::New();
110 background.SetStyleName(stylename);
111 background.SetProperty(Actor::Property::NAME, "BACKGROUND");
112 background.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
113 background.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
114 background.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
119 * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
121 struct AnimateBubbleConstraint
124 AnimateBubbleConstraint(const Vector3& initialPos, float scale)
125 : mInitialX(initialPos.x),
130 void operator()(Vector3& position, const PropertyInputContainer& inputs)
132 const Vector3& parentSize = inputs[1]->GetVector3();
133 const Vector3& childSize = inputs[2]->GetVector3();
135 // Wrap bubbles vertically.
136 float range = parentSize.y + childSize.y;
137 // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
138 position.y -= range * (floor(position.y / range) + 0.5f);
140 // Bubbles X position moves parallax to horizontal
141 // panning by a scale factor unique to each bubble.
142 position.x = mInitialX + (inputs[0]->GetVector2().x * mScale);
151 * Constraint to precalculate values from the scroll-view
152 * and tile positions to pass to the tile shader.
154 struct TileShaderPositionConstraint
156 TileShaderPositionConstraint(float pageWidth, float tileXOffset)
157 : mPageWidth(pageWidth),
158 mTileXOffset(tileXOffset)
162 void operator()(Vector3& position, const PropertyInputContainer& inputs)
164 // Set up position.x as the tiles X offset (0.0 -> 1.0).
165 position.x = mTileXOffset;
166 // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
167 position.z = 1.0f * (-fmod(inputs[0]->GetVector2().x, mPageWidth) / mPageWidth);
168 // Set up position.y as a rectified version of the scroll-views X offset.
169 // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
170 if(position.z > 0.5f)
172 position.y = 1.0f - position.z;
176 position.y = position.z;
185 bool CompareByTitle(const Example& lhs, const Example& rhs)
187 return lhs.title < rhs.title;
192 DaliTableView::DaliTableView(Application& application)
193 : mApplication(application),
206 mBackgroundAnimations(),
211 mSortAlphabetically(false),
212 mBackgroundAnimsPlaying(false)
214 application.InitSignal().Connect(this, &DaliTableView::Initialize);
217 DaliTableView::~DaliTableView()
221 void DaliTableView::AddExample(Example example)
223 mExampleList.push_back(example);
226 void DaliTableView::SortAlphabetically(bool sortAlphabetically)
228 mSortAlphabetically = sortAlphabetically;
231 void DaliTableView::Initialize(Application& application)
233 Window window = application.GetWindow();
234 window.KeyEventSignal().Connect(this, &DaliTableView::OnKeyEvent);
235 const Window::WindowSize windowSize = window.GetSize();
238 mRootActor = CreateBackground("LauncherBackground");
239 window.Add(mRootActor);
242 ImageView logo = ImageView::New(LOGO_PATH);
243 logo.SetProperty(Actor::Property::NAME, "LOGO_IMAGE");
244 logo.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
245 logo.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 0.1f, 0.5f));
246 logo.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
247 // The logo should appear on top of everything.
248 logo.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
249 mRootActor.Add(logo);
251 // Show version in a popup when log is tapped
252 mLogoTapDetector = TapGestureDetector::New();
253 mLogoTapDetector.Attach(logo);
254 mLogoTapDetector.DetectedSignal().Connect(this, &DaliTableView::OnLogoTapped);
256 // Scrollview occupying the majority of the screen
257 mScrollView = ScrollView::New();
258 mScrollView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
259 mScrollView.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 1.0f - 0.05f, 0.5f));
260 mScrollView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
261 mScrollView.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT);
262 mScrollView.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.0f, 0.6f, 0.0f));
264 const float buttonsPageMargin = (1.0f - TABLE_RELATIVE_SIZE.x) * 0.5f * windowSize.GetWidth();
265 mScrollView.SetProperty(Actor::Property::PADDING, Padding(buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f));
267 mScrollView.SetAxisAutoLock(true);
268 mScrollView.ScrollCompletedSignal().Connect(this, &DaliTableView::OnScrollComplete);
269 mScrollView.ScrollStartedSignal().Connect(this, &DaliTableView::OnScrollStart);
270 mScrollView.TouchedSignal().Connect(this, &DaliTableView::OnScrollTouched);
272 mPageWidth = windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f;
274 // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
275 Actor bubbleContainer = Actor::New();
276 bubbleContainer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
277 bubbleContainer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
278 bubbleContainer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
279 SetupBackground(bubbleContainer);
281 mRootActor.Add(bubbleContainer);
282 mRootActor.Add(mScrollView);
284 // Add scroll view effect and setup constraints on pages
285 ApplyScrollViewEffect();
287 // Add pages and tiles
290 // Remove constraints for inner cube effect
291 ApplyCubeEffectToPages();
293 Dali::Window winHandle = application.GetWindow();
295 if(windowSize.GetWidth() <= windowSize.GetHeight())
297 winHandle.AddAvailableOrientation(Dali::Window::PORTRAIT);
298 winHandle.RemoveAvailableOrientation(Dali::Window::LANDSCAPE);
299 winHandle.AddAvailableOrientation(Dali::Window::PORTRAIT_INVERSE);
300 winHandle.RemoveAvailableOrientation(Dali::Window::LANDSCAPE_INVERSE);
304 winHandle.AddAvailableOrientation(Dali::Window::LANDSCAPE);
305 winHandle.RemoveAvailableOrientation(Dali::Window::PORTRAIT);
306 winHandle.AddAvailableOrientation(Dali::Window::LANDSCAPE_INVERSE);
307 winHandle.RemoveAvailableOrientation(Dali::Window::PORTRAIT_INVERSE);
310 // Set initial orientation
311 unsigned int degrees = 0;
314 // Background animation
315 mAnimationTimer = Timer::New(BACKGROUND_ANIMATION_DURATION);
316 mAnimationTimer.TickSignal().Connect(this, &DaliTableView::PauseBackgroundAnimation);
317 mAnimationTimer.Start();
318 mBackgroundAnimsPlaying = true;
323 void DaliTableView::CreateFocusEffect()
325 // Hook the required signals to manage the focus.
326 auto keyboardFocusManager = KeyboardFocusManager::Get();
327 keyboardFocusManager.PreFocusChangeSignal().Connect(this, &DaliTableView::OnKeyboardPreFocusChange);
328 keyboardFocusManager.FocusedActorEnterKeySignal().Connect(this, &DaliTableView::OnFocusedActorActivated);
330 // Loop to create both actors for the focus highlight effect.
331 for(unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
333 mFocusEffect[i].actor = ImageView::New();
334 mFocusEffect[i].actor.SetStyleName("FocusActor");
335 mFocusEffect[i].actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
336 mFocusEffect[i].actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
337 mFocusEffect[i].actor.SetProperty(Actor::Property::INHERIT_SCALE, false);
338 mFocusEffect[i].actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
340 KeyFrames alphaKeyFrames = KeyFrames::New();
341 alphaKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_ALPHA);
342 alphaKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, KEYBOARD_FOCUS_ANIMATING_ALPHA);
343 alphaKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_ALPHA);
345 KeyFrames scaleKeyFrames = KeyFrames::New();
346 scaleKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_SCALE);
347 scaleKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, FOCUS_INDICATOR_ANIMATING_SCALE);
348 scaleKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_SCALE);
350 mFocusEffect[i].animation = Animation::New(KEYBOARD_FOCUS_ANIMATION_DURATION);
351 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA), alphaKeyFrames);
352 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::SCALE), scaleKeyFrames);
354 mFocusEffect[i].animation.SetLoopCount(KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT);
357 // Parent the secondary effect from the primary.
358 mFocusEffect[0].actor.Add(mFocusEffect[1].actor);
360 keyboardFocusManager.SetFocusIndicatorActor(mFocusEffect[0].actor);
362 // Connect to the on & off scene signals of the indicator which represents when it is enabled & disabled respectively
363 mFocusEffect[0].actor.OnSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorEnabled);
364 mFocusEffect[0].actor.OffSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorDisabled);
367 void DaliTableView::OnFocusIndicatorEnabled(Actor /* actor */)
369 // Play the animation on the 1st glow object.
370 mFocusEffect[0].animation.Play();
372 // Stagger the animation on the 2nd glow object half way through.
373 mFocusEffect[1].animation.PlayFrom(KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f);
376 void DaliTableView::OnFocusIndicatorDisabled(Dali::Actor /* actor */)
378 // Stop the focus effect animations
379 for(auto i = 0u; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
381 mFocusEffect[i].animation.Stop();
385 void DaliTableView::ApplyCubeEffectToPages()
387 ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast(mScrollViewEffect);
388 unsigned int pageCount(0);
389 for(std::vector<Actor>::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter)
391 Actor page = *pageIter;
392 effect.ApplyToPage(page, pageCount++);
396 void DaliTableView::OnButtonsPageRelayout(const Dali::Actor& actor)
400 void DaliTableView::Populate()
402 const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
404 mTotalPages = (mExampleList.size() + EXAMPLES_PER_PAGE - 1) / EXAMPLES_PER_PAGE;
406 // Populate ScrollView.
407 if(mExampleList.size() > 0)
409 if(mSortAlphabetically)
411 sort(mExampleList.begin(), mExampleList.end(), CompareByTitle);
414 unsigned int exampleCount = 0;
415 ExampleListConstIter iter = mExampleList.begin();
417 for(int t = 0; t < mTotalPages; t++)
420 TableView page = TableView::New(ROWS_PER_PAGE, EXAMPLES_PER_ROW);
421 page.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
422 page.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
423 page.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
424 mScrollView.Add(page);
426 // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
427 const float margin = 2.0f;
428 const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
430 for(int row = 0; row < ROWS_PER_PAGE; row++)
432 for(int column = 0; column < EXAMPLES_PER_ROW; column++)
434 const Example& example = (*iter);
436 // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
437 Vector2 position(static_cast<float>(column) / (EXAMPLES_PER_ROW - 1.0f), static_cast<float>(row) / (EXAMPLES_PER_ROW - 1.0f));
438 Actor tile = CreateTile(example.name, example.title, Vector3(tileParentMultiplier, tileParentMultiplier, 1.0f), position);
439 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
440 accessibilityManager.SetFocusOrder(tile, ++exampleCount);
441 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL, example.title);
442 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile");
443 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT, "You can run this example");
445 tile.SetProperty(Actor::Property::PADDING, Padding(margin, margin, margin, margin));
446 page.AddChild(tile, TableView::CellPosition(row, column));
450 if(iter == mExampleList.end())
456 if(iter == mExampleList.end())
462 mPages.push_back(page);
464 if(iter == mExampleList.end())
471 // Update Ruler info.
472 mScrollRulerX = new FixedRuler(mPageWidth);
473 mScrollRulerY = new DefaultRuler();
474 mScrollRulerX->SetDomain(RulerDomain(0.0f, (mTotalPages + 1) * windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f, true));
475 mScrollRulerY->Disable();
476 mScrollView.SetRulerX(mScrollRulerX);
477 mScrollView.SetRulerY(mScrollRulerY);
480 void DaliTableView::Rotate(unsigned int degrees)
482 // Resize the root actor
483 const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
484 const Vector2 originalSize(windowSize.GetWidth(), windowSize.GetHeight());
485 Vector3 targetSize(originalSize.x, originalSize.y, 1.0f);
487 if(degrees == 90 || degrees == 270)
489 targetSize = Vector3(originalSize.y, originalSize.x, 1.0f);
494 mRotateAnimation.Stop();
495 mRotateAnimation.Clear();
498 mRotateAnimation = Animation::New(ROTATE_ANIMATION_TIME);
499 mRotateAnimation.AnimateTo(Property(mRootActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360 - degrees)), Vector3::ZAXIS), AlphaFunction::EASE_OUT);
500 mRotateAnimation.AnimateTo(Property(mRootActor, Actor::Property::SIZE), targetSize, AlphaFunction::EASE_OUT);
501 mRotateAnimation.Play();
504 Actor DaliTableView::CreateTile(const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position)
506 Toolkit::ImageView focusableTile = ImageView::New();
508 focusableTile.SetStyleName("DemoTile");
509 focusableTile.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
510 focusableTile.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
511 focusableTile.SetProperty(Actor::Property::SIZE_MODE_FACTOR, sizeMultiplier);
512 focusableTile.SetProperty(Actor::Property::NAME, name);
514 // Set the tile to be keyboard focusable
515 focusableTile.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
517 // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
518 Property::Value value = Vector3(0.0f, 0.0f, 0.0f);
519 Property::Index propertyIndex = focusableTile.RegisterProperty("uCustomPosition", value);
521 // We create a constraint to perform a precalculation on the scroll-view X offset
522 // and pass it to the shader uniform, along with the tile's position.
523 Constraint shaderPosition = Constraint::New<Vector3>(focusableTile, propertyIndex, TileShaderPositionConstraint(mPageWidth, position.x));
524 shaderPosition.AddSource(Source(mScrollView, ScrollView::Property::SCROLL_POSITION));
525 shaderPosition.SetRemoveAction(Constraint::DISCARD);
526 shaderPosition.Apply();
527 //focusableTile.Add( tileContent );
529 // Create an ImageView for the 9-patch border around the tile.
530 ImageView borderImage = ImageView::New();
531 borderImage.SetStyleName("DemoTileBorder");
532 borderImage.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
533 borderImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
534 borderImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
535 borderImage.SetProperty(Actor::Property::OPACITY, 0.8f);
536 DevelControl::AppendAccessibilityRelation(borderImage, focusableTile, Accessibility::RelationType::CONTROLLED_BY);
537 focusableTile.Add(borderImage);
539 TextLabel label = TextLabel::New();
540 label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
541 label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
542 label.SetStyleName("LauncherLabel");
543 label.SetProperty(TextLabel::Property::MULTI_LINE, true);
544 label.SetProperty(TextLabel::Property::TEXT, title);
545 label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
546 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
547 label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
549 // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
550 label.SetProperty(Actor::Property::PADDING, Padding(TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING));
551 DevelControl::AppendAccessibilityRelation(label, focusableTile, Accessibility::RelationType::CONTROLLED_BY);
552 focusableTile.Add(label);
554 // Connect to the touch events
555 focusableTile.TouchedSignal().Connect(this, &DaliTableView::OnTilePressed);
556 focusableTile.HoveredSignal().Connect(this, &DaliTableView::OnTileHovered);
557 focusableTile.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::PUSH_BUTTON);
558 DevelControl::AccessibilityActivateSignal(focusableTile).Connect(this, [=](){
559 DoTilePress(focusableTile, PointState::DOWN);
560 DoTilePress(focusableTile, PointState::UP);
563 return focusableTile;
566 bool DaliTableView::OnTilePressed(Actor actor, const TouchEvent& event)
568 return DoTilePress(actor, event.GetState(0));
571 bool DaliTableView::DoTilePress(Actor actor, PointState::Type pointState)
573 bool consumed = false;
575 if(PointState::DOWN == pointState)
577 mPressedActor = actor;
581 // A button press is only valid if the Down & Up events
582 // both occurred within the button.
583 if((PointState::UP == pointState) &&
584 (mPressedActor == actor))
586 // ignore Example button presses when scrolling or button animating.
587 if((!mScrolling) && (!mPressedAnimation))
589 std::string name = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
590 const ExampleListIter end = mExampleList.end();
591 for(ExampleListIter iter = mExampleList.begin(); iter != end; ++iter)
593 if((*iter).name == name)
595 // do nothing, until pressed animation finished.
604 mPressedAnimation = Animation::New(BUTTON_PRESS_ANIMATION_TIME);
605 mPressedAnimation.SetEndAction(Animation::DISCARD);
607 // scale the content actor within the Tile, as to not affect the placement within the Table.
608 Actor content = actor.GetChildAt(0);
609 mPressedAnimation.AnimateTo(Property(content, Actor::Property::SCALE), Vector3(0.7f, 0.7f, 1.0f), AlphaFunction::EASE_IN_OUT, TimePeriod(0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f));
610 mPressedAnimation.AnimateTo(Property(content, Actor::Property::SCALE), Vector3::ONE, AlphaFunction::EASE_IN_OUT, TimePeriod(BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f));
612 // Rotate button on the Y axis when pressed.
613 mPressedAnimation.AnimateBy(Property(content, Actor::Property::ORIENTATION), Quaternion(Degree(0.0f), Degree(180.0f), Degree(0.0f)));
615 mPressedAnimation.Play();
616 mPressedAnimation.FinishedSignal().Connect(this, &DaliTableView::OnPressedAnimationFinished);
622 void DaliTableView::OnPressedAnimationFinished(Dali::Animation& source)
624 mPressedAnimation.Reset();
627 std::string name = mPressedActor.GetProperty<std::string>(Dali::Actor::Property::NAME);
629 ExecuteProcess(name, mApplication);
631 mPressedActor.Reset();
635 void DaliTableView::OnScrollStart(const Dali::Vector2& position)
642 void DaliTableView::OnScrollComplete(const Dali::Vector2& position)
646 // move focus to 1st item of new page
647 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
648 accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0));
651 bool DaliTableView::OnScrollTouched(Actor actor, const TouchEvent& event)
653 if(PointState::DOWN == event.GetState(0))
655 mPressedActor = actor;
661 void DaliTableView::ApplyScrollViewEffect()
663 // Remove old effect if exists.
665 if(mScrollViewEffect)
667 mScrollView.RemoveEffect(mScrollViewEffect);
670 // Just one effect for now
671 SetupInnerPageCubeEffect();
673 mScrollView.ApplyEffect(mScrollViewEffect);
676 void DaliTableView::SetupInnerPageCubeEffect()
678 const Window::WindowSize windowDimensions = mApplication.GetWindow().GetSize();
679 const Vector2 windowSize(windowDimensions.GetWidth(), windowDimensions.GetHeight());
681 Dali::Path path = Dali::Path::New();
682 Dali::Property::Array points;
684 points[0] = Vector3(windowSize.x * 0.5, 0.0f, windowSize.x * 0.5f);
685 points[1] = Vector3(0.0f, 0.0f, 0.0f);
686 points[2] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.5f);
687 path.SetProperty(Path::Property::POINTS, points);
689 Dali::Property::Array controlPoints;
690 controlPoints.Resize(4);
691 controlPoints[0] = Vector3(windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
692 controlPoints[1] = Vector3(windowSize.x * 0.3f, 0.0f, 0.0f);
693 controlPoints[2] = Vector3(-windowSize.x * 0.3f, 0.0f, 0.0f);
694 controlPoints[3] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
695 path.SetProperty(Path::Property::CONTROL_POINTS, controlPoints);
697 mScrollViewEffect = ScrollViewPagePathEffect::New(path,
698 Vector3(-1.0f, 0.0f, 0.0f),
699 Toolkit::ScrollView::Property::SCROLL_FINAL_X,
700 Vector3(windowSize.x * TABLE_RELATIVE_SIZE.x, windowSize.y * TABLE_RELATIVE_SIZE.y, 0.0f),
704 void DaliTableView::OnKeyEvent(const KeyEvent& event)
706 if(event.GetState() == KeyEvent::DOWN)
708 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
710 // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
712 if(mVersionPopup && (mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN))
714 mVersionPopup.SetDisplayState(Popup::HIDDEN);
724 void DaliTableView::SetupBackground(Actor bubbleContainer)
726 // Add bubbles to the bubbleContainer.
727 // Note: The bubbleContainer is parented externally to this function.
728 AddBackgroundActors(bubbleContainer, NUM_BACKGROUND_IMAGES);
731 void DaliTableView::InitialiseBackgroundActors(Actor actor)
733 // Delete current animations
734 mBackgroundAnimations.clear();
736 // Create new animations
737 const Vector3 size = actor.GetTargetSize();
739 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
741 Actor child = actor.GetChildAt(i);
743 // Calculate a random position
744 Vector3 childPos(Random::Range(-size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE),
745 Random::Range(-size.y, size.y),
746 Random::Range(BUBBLE_MIN_Z, BUBBLE_MAX_Z));
748 child.SetProperty(Actor::Property::POSITION, childPos);
750 // Define bubble horizontal parallax and vertical wrapping
751 Constraint animConstraint = Constraint::New<Vector3>(child, Actor::Property::POSITION, AnimateBubbleConstraint(childPos, Random::Range(-0.85f, 0.25f)));
752 animConstraint.AddSource(Source(mScrollView, ScrollView::Property::SCROLL_POSITION));
753 animConstraint.AddSource(Dali::ParentSource(Dali::Actor::Property::SIZE));
754 animConstraint.AddSource(Dali::LocalSource(Dali::Actor::Property::SIZE));
755 animConstraint.SetRemoveAction(Constraint::DISCARD);
756 animConstraint.Apply();
759 Animation animation = Animation::New(Random::Range(30.0f, 160.0f));
760 animation.AnimateBy(Property(child, Actor::Property::POSITION), Vector3(0.0f, -2000.0f, 0.0f), AlphaFunction::LINEAR);
761 animation.SetLooping(true);
763 mBackgroundAnimations.push_back(animation);
767 void DaliTableView::AddBackgroundActors(Actor layer, int count)
769 for(int i = 0; i < count; ++i)
771 float randSize = Random::Range(10.0f, 400.0f);
772 int shapeType = static_cast<int>(Random::Range(0.0f, NUMBER_OF_SHAPE_IMAGES - 1) + 0.5f);
774 ImageView dfActor = ImageView::New();
775 dfActor.SetProperty(Actor::Property::SIZE, Vector2(randSize, randSize));
776 dfActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
778 // Set the Image URL and the custom shader at the same time
779 Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
780 Property::Map imageMap;
781 imageMap.Add(ImageVisual::Property::URL, SHAPE_IMAGE_TABLE[shapeType]);
782 imageMap.Add(Toolkit::Visual::Property::SHADER, effect);
783 dfActor.SetProperty(Toolkit::ImageView::Property::IMAGE, imageMap);
785 dfActor.SetStyleName(BUBBLE_COLOR_STYLE_NAME[i % NUMBER_OF_BUBBLE_COLORS]);
790 // Positioning will occur when the layer is relaid out
791 layer.OnRelayoutSignal().Connect(this, &DaliTableView::InitialiseBackgroundActors);
794 bool DaliTableView::PauseBackgroundAnimation()
801 void DaliTableView::PauseAnimation()
803 if(mBackgroundAnimsPlaying)
805 for(AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter)
807 Animation anim = *animIter;
812 mBackgroundAnimsPlaying = false;
816 void DaliTableView::PlayAnimation()
818 if(!mBackgroundAnimsPlaying)
820 for(AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter)
822 Animation anim = *animIter;
827 mBackgroundAnimsPlaying = true;
830 mAnimationTimer.SetInterval(BACKGROUND_ANIMATION_DURATION);
833 Dali::Actor DaliTableView::OnKeyboardPreFocusChange(Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction)
835 Actor nextFocusActor = proposed;
837 if(!current && !proposed)
839 // Set the initial focus to the first tile in the current page should be focused.
840 nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
844 // ScrollView is being focused but nothing in the current page can be focused further
845 // in the given direction. We should work out which page to scroll to next.
846 int currentPage = mScrollView.GetCurrentPage();
847 int newPage = currentPage;
848 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
852 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
857 newPage = std::max(0, std::min(mTotalPages - 1, newPage));
858 if(newPage == currentPage)
860 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
862 newPage = mTotalPages - 1;
864 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
870 // Scroll to the page in the given direction
871 mScrollView.ScrollTo(newPage);
873 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
875 // Work out the cell position for the last tile
876 int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
877 int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ((remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1);
878 int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : (remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1);
880 // Move the focus to the last tile in the new page.
881 nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
885 // Move the focus to the first tile in the new page.
886 nextFocusActor = mPages[newPage].GetChildAt(0);
890 return nextFocusActor;
893 void DaliTableView::OnFocusedActorActivated(Dali::Actor activatedActor)
897 mPressedActor = activatedActor;
899 // Activate the current focused actor;
900 DoTilePress(mPressedActor, PointState::UP);
904 bool DaliTableView::OnTileHovered(Actor actor, const HoverEvent& event)
906 KeyboardFocusManager::Get().SetCurrentFocusActor(actor);
910 void DaliTableView::OnLogoTapped(Dali::Actor actor, const Dali::TapGesture& tap)
912 // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
913 if(!mVersionPopup || (mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN))
917 std::ostringstream stream;
918 stream << "DALi Core: " << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl
919 << "(" << CORE_BUILD_DATE << ")\n";
920 stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl
921 << "(" << ADAPTOR_BUILD_DATE << ")\n";
922 stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl
923 << "(" << TOOLKIT_BUILD_DATE << ")\n";
924 stream << "DALi Demo:"
925 << "\n(" << DEMO_BUILD_DATE << ")\n";
927 mVersionPopup = Dali::Toolkit::Popup::New();
929 Toolkit::TextLabel titleActor = Toolkit::TextLabel::New("Version information");
930 titleActor.SetProperty(Actor::Property::NAME, "titleActor");
931 titleActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
932 titleActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
934 Toolkit::TextLabel contentActor = Toolkit::TextLabel::New(stream.str());
935 contentActor.SetProperty(Actor::Property::NAME, "contentActor");
936 contentActor.SetProperty(Toolkit::TextLabel::Property::MULTI_LINE, true);
937 contentActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
938 contentActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
939 contentActor.SetProperty(Actor::Property::PADDING, Padding(0.0f, 0.0f, 20.0f, 0.0f));
941 mVersionPopup.SetTitle(titleActor);
942 mVersionPopup.SetContent(contentActor);
944 mVersionPopup.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH);
945 mVersionPopup.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.75f, 1.0f, 1.0f));
946 mVersionPopup.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT);
948 mVersionPopup.OutsideTouchedSignal().Connect(this, &DaliTableView::HideVersionPopup);
949 mApplication.GetWindow().Add(mVersionPopup);
952 mVersionPopup.SetDisplayState(Popup::SHOWN);
956 void DaliTableView::HideVersionPopup()
958 // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
959 if(mVersionPopup && (mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN))
961 mVersionPopup.SetDisplayState(Popup::HIDDEN);