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-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/accessibility-manager/accessibility-manager.h>
24 #include <dali-toolkit/devel-api/controls/table-view/table-view.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/devel-api/visual-factory/visual-factory.h>
28 #include <dali/devel-api/actors/actor-devel.h>
29 #include <dali/devel-api/images/distance-field.h>
33 #include "shared/execute-process.h"
34 #include "shared/utility.h"
35 #include "shared/view.h"
38 using namespace Dali::Toolkit;
40 ///////////////////////////////////////////////////////////////////////////////
44 const std::string LOGO_PATH(DEMO_IMAGE_DIR "Logo-for-demo.png");
46 // Keyboard focus effect constants.
47 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f; ///< The total duration of the keyboard focus animation
48 const int32_t KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT = 5; ///< The number of loops for the keyboard focus animation
49 const float KEYBOARD_FOCUS_FINAL_SCALE_FLOAT = 1.05f; ///< The final scale of the focus highlight
50 const float KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT = 1.18f; ///< The scale of the focus highlight during the animation
51 const float KEYBOARD_FOCUS_FINAL_ALPHA = 0.7f; ///< The final alpha of the focus highlight
52 const float KEYBOARD_FOCUS_ANIMATING_ALPHA = 0.0f; ///< The alpha of the focus highlight during the animation
53 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
54 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
55 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
56 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
58 const float TILE_LABEL_PADDING = 8.0f; ///< Border between edge of tile and the example text
59 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f; ///< Time to perform button scale effect.
60 const float ROTATE_ANIMATION_TIME = 0.5f; ///< Time to perform rotate effect.
61 const int MAX_PAGES = 256; ///< Maximum pages (arbitrary safety limit)
62 const int EXAMPLES_PER_ROW = 3;
63 const int ROWS_PER_PAGE = 3;
64 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
65 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
66 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
67 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f); ///< ScrollView's relative size to its parent
68 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.
69 const float STENCIL_RELATIVE_SIZE = 1.0f;
71 const float EFFECT_SNAP_DURATION = 0.66f; ///< Scroll Snap Duration for Effects
72 const float EFFECT_FLICK_DURATION = 0.5f; ///< Scroll Flick Duration for Effects
73 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
75 const char* const BUBBLE_COLOR_STYLE_NAME[] =
81 const int NUMBER_OF_BUBBLE_COLORS(sizeof(BUBBLE_COLOR_STYLE_NAME) / sizeof(BUBBLE_COLOR_STYLE_NAME[0]));
83 const char* const SHAPE_IMAGE_TABLE[] =
85 DEMO_IMAGE_DIR "shape-circle.png",
86 DEMO_IMAGE_DIR "shape-bubble.png"};
87 const int NUMBER_OF_SHAPE_IMAGES(sizeof(SHAPE_IMAGE_TABLE) / sizeof(SHAPE_IMAGE_TABLE[0]));
89 const int NUM_BACKGROUND_IMAGES = 18;
90 const float BACKGROUND_SPREAD_SCALE = 1.5f;
92 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
94 const float BUBBLE_MIN_Z = -1.0;
95 const float BUBBLE_MAX_Z = 0.0f;
97 const char* const DEMO_BUILD_DATE = __DATE__ " " __TIME__;
100 * Creates the background image
102 Control CreateBackground(std::string stylename)
104 Control background = Control::New();
105 background.SetStyleName(stylename);
106 background.SetProperty(Actor::Property::NAME, "BACKGROUND");
107 background.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
108 background.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
109 background.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
114 * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
116 struct AnimateBubbleConstraint
119 AnimateBubbleConstraint(const Vector3& initialPos, float scale)
120 : mInitialX(initialPos.x),
125 void operator()(Vector3& position, const PropertyInputContainer& inputs)
127 const Vector3& parentSize = inputs[1]->GetVector3();
128 const Vector3& childSize = inputs[2]->GetVector3();
130 // Wrap bubbles vertically.
131 float range = parentSize.y + childSize.y;
132 // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
133 position.y -= range * (floor(position.y / range) + 0.5f);
135 // Bubbles X position moves parallax to horizontal
136 // panning by a scale factor unique to each bubble.
137 position.x = mInitialX + (inputs[0]->GetVector2().x * mScale);
146 * Constraint to precalculate values from the scroll-view
147 * and tile positions to pass to the tile shader.
149 struct TileShaderPositionConstraint
151 TileShaderPositionConstraint(float pageWidth, float tileXOffset)
152 : mPageWidth(pageWidth),
153 mTileXOffset(tileXOffset)
157 void operator()(Vector3& position, const PropertyInputContainer& inputs)
159 // Set up position.x as the tiles X offset (0.0 -> 1.0).
160 position.x = mTileXOffset;
161 // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
162 position.z = 1.0f * (-fmod(inputs[0]->GetVector2().x, mPageWidth) / mPageWidth);
163 // Set up position.y as a rectified version of the scroll-views X offset.
164 // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
165 if(position.z > 0.5f)
167 position.y = 1.0f - position.z;
171 position.y = position.z;
180 bool CompareByTitle(const Example& lhs, const Example& rhs)
182 return lhs.title < rhs.title;
187 DaliTableView::DaliTableView(Application& application)
188 : mApplication(application),
201 mBackgroundAnimations(),
206 mSortAlphabetically(false),
207 mBackgroundAnimsPlaying(false)
209 application.InitSignal().Connect(this, &DaliTableView::Initialize);
212 DaliTableView::~DaliTableView()
216 void DaliTableView::AddExample(Example example)
218 mExampleList.push_back(example);
221 void DaliTableView::SortAlphabetically(bool sortAlphabetically)
223 mSortAlphabetically = sortAlphabetically;
226 void DaliTableView::Initialize(Application& application)
228 Window window = application.GetWindow();
229 window.KeyEventSignal().Connect(this, &DaliTableView::OnKeyEvent);
230 const Window::WindowSize windowSize = window.GetSize();
233 mRootActor = CreateBackground("LauncherBackground");
234 window.Add(mRootActor);
237 ImageView logo = ImageView::New(LOGO_PATH);
238 logo.SetProperty(Actor::Property::NAME, "LOGO_IMAGE");
239 logo.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
240 logo.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 0.1f, 0.5f));
241 logo.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
242 // The logo should appear on top of everything.
243 logo.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
244 mRootActor.Add(logo);
246 // Show version in a popup when log is tapped
247 mLogoTapDetector = TapGestureDetector::New();
248 mLogoTapDetector.Attach(logo);
249 mLogoTapDetector.DetectedSignal().Connect(this, &DaliTableView::OnLogoTapped);
251 // Scrollview occupying the majority of the screen
252 mScrollView = ScrollView::New();
253 mScrollView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
254 mScrollView.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 1.0f - 0.05f, 0.5f));
255 mScrollView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
256 mScrollView.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT);
257 mScrollView.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.0f, 0.6f, 0.0f));
259 const float buttonsPageMargin = (1.0f - TABLE_RELATIVE_SIZE.x) * 0.5f * windowSize.GetWidth();
260 mScrollView.SetProperty(Actor::Property::PADDING, Padding(buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f));
262 mScrollView.SetAxisAutoLock(true);
263 mScrollView.ScrollCompletedSignal().Connect(this, &DaliTableView::OnScrollComplete);
264 mScrollView.ScrollStartedSignal().Connect(this, &DaliTableView::OnScrollStart);
265 mScrollView.TouchedSignal().Connect(this, &DaliTableView::OnScrollTouched);
267 mPageWidth = windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f;
269 // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
270 Actor bubbleContainer = Actor::New();
271 bubbleContainer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
272 bubbleContainer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
273 bubbleContainer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
274 SetupBackground(bubbleContainer);
276 mRootActor.Add(bubbleContainer);
277 mRootActor.Add(mScrollView);
279 // Add scroll view effect and setup constraints on pages
280 ApplyScrollViewEffect();
282 // Add pages and tiles
285 // Remove constraints for inner cube effect
286 ApplyCubeEffectToPages();
288 Dali::Window winHandle = application.GetWindow();
290 if(windowSize.GetWidth() <= windowSize.GetHeight())
292 winHandle.AddAvailableOrientation(Dali::Window::PORTRAIT);
293 winHandle.RemoveAvailableOrientation(Dali::Window::LANDSCAPE);
294 winHandle.AddAvailableOrientation(Dali::Window::PORTRAIT_INVERSE);
295 winHandle.RemoveAvailableOrientation(Dali::Window::LANDSCAPE_INVERSE);
299 winHandle.AddAvailableOrientation(Dali::Window::LANDSCAPE);
300 winHandle.RemoveAvailableOrientation(Dali::Window::PORTRAIT);
301 winHandle.AddAvailableOrientation(Dali::Window::LANDSCAPE_INVERSE);
302 winHandle.RemoveAvailableOrientation(Dali::Window::PORTRAIT_INVERSE);
305 // Set initial orientation
306 unsigned int degrees = 0;
309 // Background animation
310 mAnimationTimer = Timer::New(BACKGROUND_ANIMATION_DURATION);
311 mAnimationTimer.TickSignal().Connect(this, &DaliTableView::PauseBackgroundAnimation);
312 mAnimationTimer.Start();
313 mBackgroundAnimsPlaying = true;
318 void DaliTableView::CreateFocusEffect()
320 // Hook the required signals to manage the focus.
321 auto keyboardFocusManager = KeyboardFocusManager::Get();
322 keyboardFocusManager.PreFocusChangeSignal().Connect(this, &DaliTableView::OnKeyboardPreFocusChange);
323 keyboardFocusManager.FocusedActorEnterKeySignal().Connect(this, &DaliTableView::OnFocusedActorActivated);
324 AccessibilityManager::Get().FocusedActorActivatedSignal().Connect(this, &DaliTableView::OnFocusedActorActivated);
326 // Loop to create both actors for the focus highlight effect.
327 for(unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
329 mFocusEffect[i].actor = ImageView::New();
330 mFocusEffect[i].actor.SetStyleName("FocusActor");
331 mFocusEffect[i].actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
332 mFocusEffect[i].actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
333 mFocusEffect[i].actor.SetProperty(Actor::Property::INHERIT_SCALE, false);
334 mFocusEffect[i].actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
336 KeyFrames alphaKeyFrames = KeyFrames::New();
337 alphaKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_ALPHA);
338 alphaKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, KEYBOARD_FOCUS_ANIMATING_ALPHA);
339 alphaKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_ALPHA);
341 KeyFrames scaleKeyFrames = KeyFrames::New();
342 scaleKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_SCALE);
343 scaleKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, FOCUS_INDICATOR_ANIMATING_SCALE);
344 scaleKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_SCALE);
346 mFocusEffect[i].animation = Animation::New(KEYBOARD_FOCUS_ANIMATION_DURATION);
347 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA), alphaKeyFrames);
348 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::SCALE), scaleKeyFrames);
350 mFocusEffect[i].animation.SetLoopCount(KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT);
353 // Parent the secondary effect from the primary.
354 mFocusEffect[0].actor.Add(mFocusEffect[1].actor);
356 keyboardFocusManager.SetFocusIndicatorActor(mFocusEffect[0].actor);
358 // Connect to the on & off scene signals of the indicator which represents when it is enabled & disabled respectively
359 mFocusEffect[0].actor.OnSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorEnabled);
360 mFocusEffect[0].actor.OffSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorDisabled);
363 void DaliTableView::OnFocusIndicatorEnabled(Actor /* actor */)
365 // Play the animation on the 1st glow object.
366 mFocusEffect[0].animation.Play();
368 // Stagger the animation on the 2nd glow object half way through.
369 mFocusEffect[1].animation.PlayFrom(KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f);
372 void DaliTableView::OnFocusIndicatorDisabled(Dali::Actor /* actor */)
374 // Stop the focus effect animations
375 for(auto i = 0u; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
377 mFocusEffect[i].animation.Stop();
381 void DaliTableView::ApplyCubeEffectToPages()
383 ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast(mScrollViewEffect);
384 unsigned int pageCount(0);
385 for(std::vector<Actor>::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter)
387 Actor page = *pageIter;
388 effect.ApplyToPage(page, pageCount++);
392 void DaliTableView::OnButtonsPageRelayout(const Dali::Actor& actor)
396 void DaliTableView::Populate()
398 const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
400 mTotalPages = (mExampleList.size() + EXAMPLES_PER_PAGE - 1) / EXAMPLES_PER_PAGE;
402 // Populate ScrollView.
403 if(mExampleList.size() > 0)
405 if(mSortAlphabetically)
407 sort(mExampleList.begin(), mExampleList.end(), CompareByTitle);
410 unsigned int exampleCount = 0;
411 ExampleListConstIter iter = mExampleList.begin();
413 for(int t = 0; t < mTotalPages; t++)
416 TableView page = TableView::New(ROWS_PER_PAGE, EXAMPLES_PER_ROW);
417 page.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
418 page.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
419 page.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
420 mScrollView.Add(page);
422 // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
423 const float margin = 2.0f;
424 const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
426 for(int row = 0; row < ROWS_PER_PAGE; row++)
428 for(int column = 0; column < EXAMPLES_PER_ROW; column++)
430 const Example& example = (*iter);
432 // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
433 Vector2 position(static_cast<float>(column) / (EXAMPLES_PER_ROW - 1.0f), static_cast<float>(row) / (EXAMPLES_PER_ROW - 1.0f));
434 Actor tile = CreateTile(example.name, example.title, Vector3(tileParentMultiplier, tileParentMultiplier, 1.0f), position);
435 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
436 accessibilityManager.SetFocusOrder(tile, ++exampleCount);
437 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL, example.title);
438 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile");
439 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT, "You can run this example");
441 tile.SetProperty(Actor::Property::PADDING, Padding(margin, margin, margin, margin));
442 page.AddChild(tile, TableView::CellPosition(row, column));
446 if(iter == mExampleList.end())
452 if(iter == mExampleList.end())
458 mPages.push_back(page);
460 if(iter == mExampleList.end())
467 // Update Ruler info.
468 mScrollRulerX = new FixedRuler(mPageWidth);
469 mScrollRulerY = new DefaultRuler();
470 mScrollRulerX->SetDomain(RulerDomain(0.0f, (mTotalPages + 1) * windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f, true));
471 mScrollRulerY->Disable();
472 mScrollView.SetRulerX(mScrollRulerX);
473 mScrollView.SetRulerY(mScrollRulerY);
476 void DaliTableView::Rotate(unsigned int degrees)
478 // Resize the root actor
479 const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
480 const Vector2 originalSize(windowSize.GetWidth(), windowSize.GetHeight());
481 Vector3 targetSize(originalSize.x, originalSize.y, 1.0f);
483 if(degrees == 90 || degrees == 270)
485 targetSize = Vector3(originalSize.y, originalSize.x, 1.0f);
490 mRotateAnimation.Stop();
491 mRotateAnimation.Clear();
494 mRotateAnimation = Animation::New(ROTATE_ANIMATION_TIME);
495 mRotateAnimation.AnimateTo(Property(mRootActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360 - degrees)), Vector3::ZAXIS), AlphaFunction::EASE_OUT);
496 mRotateAnimation.AnimateTo(Property(mRootActor, Actor::Property::SIZE), targetSize, AlphaFunction::EASE_OUT);
497 mRotateAnimation.Play();
500 Actor DaliTableView::CreateTile(const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position)
502 Toolkit::ImageView focusableTile = ImageView::New();
504 focusableTile.SetStyleName("DemoTile");
505 focusableTile.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
506 focusableTile.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
507 focusableTile.SetProperty(Actor::Property::SIZE_MODE_FACTOR, sizeMultiplier);
508 focusableTile.SetProperty(Actor::Property::NAME, name);
510 // Set the tile to be keyboard focusable
511 focusableTile.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
513 // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
514 Property::Value value = Vector3(0.0f, 0.0f, 0.0f);
515 Property::Index propertyIndex = focusableTile.RegisterProperty("uCustomPosition", value);
517 // We create a constraint to perform a precalculation on the scroll-view X offset
518 // and pass it to the shader uniform, along with the tile's position.
519 Constraint shaderPosition = Constraint::New<Vector3>(focusableTile, propertyIndex, TileShaderPositionConstraint(mPageWidth, position.x));
520 shaderPosition.AddSource(Source(mScrollView, ScrollView::Property::SCROLL_POSITION));
521 shaderPosition.SetRemoveAction(Constraint::DISCARD);
522 shaderPosition.Apply();
523 //focusableTile.Add( tileContent );
525 // Create an ImageView for the 9-patch border around the tile.
526 ImageView borderImage = ImageView::New();
527 borderImage.SetStyleName("DemoTileBorder");
528 borderImage.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
529 borderImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
530 borderImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
531 borderImage.SetProperty(Actor::Property::OPACITY, 0.8f);
532 focusableTile.Add(borderImage);
534 TextLabel label = TextLabel::New();
535 label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
536 label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
537 label.SetStyleName("LauncherLabel");
538 label.SetProperty(TextLabel::Property::MULTI_LINE, true);
539 label.SetProperty(TextLabel::Property::TEXT, title);
540 label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
541 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
542 label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
544 // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
545 label.SetProperty(Actor::Property::PADDING, Padding(TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING));
546 focusableTile.Add(label);
548 // Connect to the touch events
549 focusableTile.TouchedSignal().Connect(this, &DaliTableView::OnTilePressed);
550 focusableTile.HoveredSignal().Connect(this, &DaliTableView::OnTileHovered);
552 return focusableTile;
555 bool DaliTableView::OnTilePressed(Actor actor, const TouchEvent& event)
557 return DoTilePress(actor, event.GetState(0));
560 bool DaliTableView::DoTilePress(Actor actor, PointState::Type pointState)
562 bool consumed = false;
564 if(PointState::DOWN == pointState)
566 mPressedActor = actor;
570 // A button press is only valid if the Down & Up events
571 // both occurred within the button.
572 if((PointState::UP == pointState) &&
573 (mPressedActor == actor))
575 // ignore Example button presses when scrolling or button animating.
576 if((!mScrolling) && (!mPressedAnimation))
578 std::string name = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
579 const ExampleListIter end = mExampleList.end();
580 for(ExampleListIter iter = mExampleList.begin(); iter != end; ++iter)
582 if((*iter).name == name)
584 // do nothing, until pressed animation finished.
593 mPressedAnimation = Animation::New(BUTTON_PRESS_ANIMATION_TIME);
594 mPressedAnimation.SetEndAction(Animation::DISCARD);
596 // scale the content actor within the Tile, as to not affect the placement within the Table.
597 Actor content = actor.GetChildAt(0);
598 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));
599 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));
601 // Rotate button on the Y axis when pressed.
602 mPressedAnimation.AnimateBy(Property(content, Actor::Property::ORIENTATION), Quaternion(Degree(0.0f), Degree(180.0f), Degree(0.0f)));
604 mPressedAnimation.Play();
605 mPressedAnimation.FinishedSignal().Connect(this, &DaliTableView::OnPressedAnimationFinished);
611 void DaliTableView::OnPressedAnimationFinished(Dali::Animation& source)
613 mPressedAnimation.Reset();
616 std::string name = mPressedActor.GetProperty<std::string>(Dali::Actor::Property::NAME);
618 ExecuteProcess(name, mApplication);
620 mPressedActor.Reset();
624 void DaliTableView::OnScrollStart(const Dali::Vector2& position)
631 void DaliTableView::OnScrollComplete(const Dali::Vector2& position)
635 // move focus to 1st item of new page
636 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
637 accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0));
640 bool DaliTableView::OnScrollTouched(Actor actor, const TouchEvent& event)
642 if(PointState::DOWN == event.GetState(0))
644 mPressedActor = actor;
650 void DaliTableView::ApplyScrollViewEffect()
652 // Remove old effect if exists.
654 if(mScrollViewEffect)
656 mScrollView.RemoveEffect(mScrollViewEffect);
659 // Just one effect for now
660 SetupInnerPageCubeEffect();
662 mScrollView.ApplyEffect(mScrollViewEffect);
665 void DaliTableView::SetupInnerPageCubeEffect()
667 const Window::WindowSize windowDimensions = mApplication.GetWindow().GetSize();
668 const Vector2 windowSize(windowDimensions.GetWidth(), windowDimensions.GetHeight());
670 Dali::Path path = Dali::Path::New();
671 Dali::Property::Array points;
673 points[0] = Vector3(windowSize.x * 0.5, 0.0f, windowSize.x * 0.5f);
674 points[1] = Vector3(0.0f, 0.0f, 0.0f);
675 points[2] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.5f);
676 path.SetProperty(Path::Property::POINTS, points);
678 Dali::Property::Array controlPoints;
679 controlPoints.Resize(4);
680 controlPoints[0] = Vector3(windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
681 controlPoints[1] = Vector3(windowSize.x * 0.3f, 0.0f, 0.0f);
682 controlPoints[2] = Vector3(-windowSize.x * 0.3f, 0.0f, 0.0f);
683 controlPoints[3] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
684 path.SetProperty(Path::Property::CONTROL_POINTS, controlPoints);
686 mScrollViewEffect = ScrollViewPagePathEffect::New(path,
687 Vector3(-1.0f, 0.0f, 0.0f),
688 Toolkit::ScrollView::Property::SCROLL_FINAL_X,
689 Vector3(windowSize.x * TABLE_RELATIVE_SIZE.x, windowSize.y * TABLE_RELATIVE_SIZE.y, 0.0f),
693 void DaliTableView::OnKeyEvent(const KeyEvent& event)
695 if(event.GetState() == KeyEvent::DOWN)
697 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
699 // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
701 if(mVersionPopup && (mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN))
703 mVersionPopup.SetDisplayState(Popup::HIDDEN);
713 void DaliTableView::SetupBackground(Actor bubbleContainer)
715 // Add bubbles to the bubbleContainer.
716 // Note: The bubbleContainer is parented externally to this function.
717 AddBackgroundActors(bubbleContainer, NUM_BACKGROUND_IMAGES);
720 void DaliTableView::InitialiseBackgroundActors(Actor actor)
722 // Delete current animations
723 mBackgroundAnimations.clear();
725 // Create new animations
726 const Vector3 size = actor.GetTargetSize();
728 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
730 Actor child = actor.GetChildAt(i);
732 // Calculate a random position
733 Vector3 childPos(Random::Range(-size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE),
734 Random::Range(-size.y, size.y),
735 Random::Range(BUBBLE_MIN_Z, BUBBLE_MAX_Z));
737 child.SetProperty(Actor::Property::POSITION, childPos);
739 // Define bubble horizontal parallax and vertical wrapping
740 Constraint animConstraint = Constraint::New<Vector3>(child, Actor::Property::POSITION, AnimateBubbleConstraint(childPos, Random::Range(-0.85f, 0.25f)));
741 animConstraint.AddSource(Source(mScrollView, ScrollView::Property::SCROLL_POSITION));
742 animConstraint.AddSource(Dali::ParentSource(Dali::Actor::Property::SIZE));
743 animConstraint.AddSource(Dali::LocalSource(Dali::Actor::Property::SIZE));
744 animConstraint.SetRemoveAction(Constraint::DISCARD);
745 animConstraint.Apply();
748 Animation animation = Animation::New(Random::Range(30.0f, 160.0f));
749 animation.AnimateBy(Property(child, Actor::Property::POSITION), Vector3(0.0f, -2000.0f, 0.0f), AlphaFunction::LINEAR);
750 animation.SetLooping(true);
752 mBackgroundAnimations.push_back(animation);
756 void DaliTableView::AddBackgroundActors(Actor layer, int count)
758 for(int i = 0; i < count; ++i)
760 float randSize = Random::Range(10.0f, 400.0f);
761 int shapeType = static_cast<int>(Random::Range(0.0f, NUMBER_OF_SHAPE_IMAGES - 1) + 0.5f);
763 ImageView dfActor = ImageView::New();
764 dfActor.SetProperty(Actor::Property::SIZE, Vector2(randSize, randSize));
765 dfActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
767 // Set the Image URL and the custom shader at the same time
768 Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
769 Property::Map imageMap;
770 imageMap.Add(ImageVisual::Property::URL, SHAPE_IMAGE_TABLE[shapeType]);
771 imageMap.Add(Toolkit::Visual::Property::SHADER, effect);
772 dfActor.SetProperty(Toolkit::ImageView::Property::IMAGE, imageMap);
774 dfActor.SetStyleName(BUBBLE_COLOR_STYLE_NAME[i % NUMBER_OF_BUBBLE_COLORS]);
779 // Positioning will occur when the layer is relaid out
780 layer.OnRelayoutSignal().Connect(this, &DaliTableView::InitialiseBackgroundActors);
783 bool DaliTableView::PauseBackgroundAnimation()
790 void DaliTableView::PauseAnimation()
792 if(mBackgroundAnimsPlaying)
794 for(AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter)
796 Animation anim = *animIter;
801 mBackgroundAnimsPlaying = false;
805 void DaliTableView::PlayAnimation()
807 if(!mBackgroundAnimsPlaying)
809 for(AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter)
811 Animation anim = *animIter;
816 mBackgroundAnimsPlaying = true;
819 mAnimationTimer.SetInterval(BACKGROUND_ANIMATION_DURATION);
822 Dali::Actor DaliTableView::OnKeyboardPreFocusChange(Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction)
824 Actor nextFocusActor = proposed;
826 if(!current && !proposed)
828 // Set the initial focus to the first tile in the current page should be focused.
829 nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
833 // ScrollView is being focused but nothing in the current page can be focused further
834 // in the given direction. We should work out which page to scroll to next.
835 int currentPage = mScrollView.GetCurrentPage();
836 int newPage = currentPage;
837 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
841 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
846 newPage = std::max(0, std::min(mTotalPages - 1, newPage));
847 if(newPage == currentPage)
849 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
851 newPage = mTotalPages - 1;
853 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
859 // Scroll to the page in the given direction
860 mScrollView.ScrollTo(newPage);
862 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
864 // Work out the cell position for the last tile
865 int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
866 int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ((remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1);
867 int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : (remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1);
869 // Move the focus to the last tile in the new page.
870 nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
874 // Move the focus to the first tile in the new page.
875 nextFocusActor = mPages[newPage].GetChildAt(0);
879 return nextFocusActor;
882 void DaliTableView::OnFocusedActorActivated(Dali::Actor activatedActor)
886 mPressedActor = activatedActor;
888 // Activate the current focused actor;
889 DoTilePress(mPressedActor, PointState::UP);
893 bool DaliTableView::OnTileHovered(Actor actor, const HoverEvent& event)
895 KeyboardFocusManager::Get().SetCurrentFocusActor(actor);
899 void DaliTableView::OnLogoTapped(Dali::Actor actor, const Dali::TapGesture& tap)
901 // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
902 if(!mVersionPopup || (mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN))
906 std::ostringstream stream;
907 stream << "DALi Core: " << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl
908 << "(" << CORE_BUILD_DATE << ")\n";
909 stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl
910 << "(" << ADAPTOR_BUILD_DATE << ")\n";
911 stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl
912 << "(" << TOOLKIT_BUILD_DATE << ")\n";
913 stream << "DALi Demo:"
914 << "\n(" << DEMO_BUILD_DATE << ")\n";
916 mVersionPopup = Dali::Toolkit::Popup::New();
918 Toolkit::TextLabel titleActor = Toolkit::TextLabel::New("Version information");
919 titleActor.SetProperty(Actor::Property::NAME, "titleActor");
920 titleActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
921 titleActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
923 Toolkit::TextLabel contentActor = Toolkit::TextLabel::New(stream.str());
924 contentActor.SetProperty(Actor::Property::NAME, "contentActor");
925 contentActor.SetProperty(Toolkit::TextLabel::Property::MULTI_LINE, true);
926 contentActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
927 contentActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
928 contentActor.SetProperty(Actor::Property::PADDING, Padding(0.0f, 0.0f, 20.0f, 0.0f));
930 mVersionPopup.SetTitle(titleActor);
931 mVersionPopup.SetContent(contentActor);
933 mVersionPopup.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH);
934 mVersionPopup.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.75f, 1.0f, 1.0f));
935 mVersionPopup.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT);
937 mVersionPopup.OutsideTouchedSignal().Connect(this, &DaliTableView::HideVersionPopup);
938 mApplication.GetWindow().Add(mVersionPopup);
941 mVersionPopup.SetDisplayState(Popup::SHOWN);
945 void DaliTableView::HideVersionPopup()
947 // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
948 if(mVersionPopup && (mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN))
950 mVersionPopup.SetDisplayState(Popup::HIDDEN);