2 * Copyright (c) 2021 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/control-devel.h>
25 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
26 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
27 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
28 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
29 #include <dali/devel-api/actors/actor-devel.h>
30 #include <dali/devel-api/images/distance-field.h>
34 #include "shared/execute-process.h"
35 #include "shared/utility.h"
36 #include "shared/view.h"
42 #include <sys/ioctl.h>
43 #include <sys/types.h>
49 using namespace Dali::Toolkit;
51 ///////////////////////////////////////////////////////////////////////////////
55 const std::string LOGO_PATH(DEMO_IMAGE_DIR "Logo-for-demo.png");
57 // Keyboard focus effect constants.
58 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f; ///< The total duration of the keyboard focus animation
59 const int32_t KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT = 5; ///< The number of loops for the keyboard focus animation
60 const float KEYBOARD_FOCUS_FINAL_SCALE_FLOAT = 1.05f; ///< The final scale of the focus highlight
61 const float KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT = 1.18f; ///< The scale of the focus highlight during the animation
62 const float KEYBOARD_FOCUS_FINAL_ALPHA = 0.7f; ///< The final alpha of the focus highlight
63 const float KEYBOARD_FOCUS_ANIMATING_ALPHA = 0.0f; ///< The alpha of the focus highlight during the animation
64 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
65 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
66 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
67 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
69 const float TILE_LABEL_PADDING = 8.0f; ///< Border between edge of tile and the example text
70 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f; ///< Time to perform button scale effect.
71 const int EXAMPLES_PER_ROW = 3;
72 const int ROWS_PER_PAGE = 3;
73 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
74 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.
76 const char* const DEMO_BUILD_DATE = __DATE__ " " __TIME__;
79 * Creates the background image
81 Actor CreateBackground(std::string stylename)
83 Control background = Control::New();
84 background.SetStyleName(stylename);
85 background.SetProperty(Actor::Property::NAME, "BACKGROUND");
86 background.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
87 background.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
88 background.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
93 * Constraint to precalculate values from the scroll-view
94 * and tile positions to pass to the tile shader.
96 struct TileShaderPositionConstraint
98 TileShaderPositionConstraint(float pageWidth, float tileXOffset)
99 : mPageWidth(pageWidth),
100 mTileXOffset(tileXOffset)
104 void operator()(Vector3& position, const PropertyInputContainer& inputs)
106 // Set up position.x as the tiles X offset (0.0 -> 1.0).
107 position.x = mTileXOffset;
108 // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
109 position.z = 1.0f * (-fmod(inputs[0]->GetVector2().x, mPageWidth) / mPageWidth);
110 // Set up position.y as a rectified version of the scroll-views X offset.
111 // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
112 if(position.z > 0.5f)
114 position.y = 1.0f - position.z;
118 position.y = position.z;
127 void AppendVersionString(std::ostringstream& stream)
129 stream << "DALi Core: " << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl
130 << "(" << CORE_BUILD_DATE << ")\n";
131 stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl
132 << "(" << ADAPTOR_BUILD_DATE << ")\n";
133 stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl
134 << "(" << TOOLKIT_BUILD_DATE << ")\n";
135 stream << "DALi Demo:"
136 << "\n(" << DEMO_BUILD_DATE << ")\n\n";
139 void AppendIpAddress(std::ostringstream& stream)
141 // Append IP addresses
142 struct ifaddrs *interfaceAddresses, *head;
143 if(!getifaddrs(&interfaceAddresses))
145 head = interfaceAddresses;
147 char host[NI_MAXHOST];
149 while(interfaceAddresses)
151 if(strcmp(interfaceAddresses->ifa_name, "lo"))
153 struct sockaddr* address = interfaceAddresses->ifa_addr;
154 if(address != nullptr && address->sa_family == AF_INET)
156 if(getnameinfo(address, sizeof(struct sockaddr_in), host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST) == 0)
158 stream<<interfaceAddresses->ifa_name<<": "<<host<<std::endl;
163 interfaceAddresses = interfaceAddresses->ifa_next;
170 * Creates a popup that shows the version information of the DALi libraries and demo
172 Dali::Toolkit::Popup CreateVersionPopup(Application& application, ConnectionTrackerInterface& connectionTracker)
174 std::ostringstream stream;
175 AppendVersionString(stream);
176 AppendIpAddress(stream);
178 Dali::Toolkit::Popup popup = Dali::Toolkit::Popup::New();
180 Toolkit::TextLabel titleActor = Toolkit::TextLabel::New("Version information");
181 titleActor.SetProperty(Actor::Property::NAME, "titleActor");
182 titleActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
183 titleActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
185 Toolkit::TextLabel contentActor = Toolkit::TextLabel::New(stream.str());
186 contentActor.SetProperty(Actor::Property::NAME, "contentActor");
187 contentActor.SetProperty(Toolkit::TextLabel::Property::MULTI_LINE, true);
188 contentActor.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER);
189 contentActor.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE);
190 contentActor.SetProperty(Actor::Property::PADDING, Padding(0.0f, 0.0f, 20.0f, 0.0f));
192 popup.SetTitle(titleActor);
193 popup.SetContent(contentActor);
195 popup.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH);
196 popup.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.75f, 1.0f, 1.0f));
197 popup.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT);
199 application.GetWindow().Add(popup);
201 // Hide the popup when touched outside
202 popup.OutsideTouchedSignal().Connect(
205 if(popup && (popup.GetDisplayState() == Toolkit::Popup::SHOWN))
207 popup.SetDisplayState(Popup::HIDDEN);
214 /// Sets up the inner cube effect
215 Dali::Toolkit::ScrollViewEffect SetupInnerPageCubeEffect(const Vector2 windowSize, int totalPages)
217 Dali::Path path = Dali::Path::New();
218 Dali::Property::Array points;
220 points[0] = Vector3(windowSize.x * 0.5, 0.0f, windowSize.x * 0.5f);
221 points[1] = Vector3(0.0f, 0.0f, 0.0f);
222 points[2] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.5f);
223 path.SetProperty(Path::Property::POINTS, points);
225 Dali::Property::Array controlPoints;
226 controlPoints.Resize(4);
227 controlPoints[0] = Vector3(windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
228 controlPoints[1] = Vector3(windowSize.x * 0.3f, 0.0f, 0.0f);
229 controlPoints[2] = Vector3(-windowSize.x * 0.3f, 0.0f, 0.0f);
230 controlPoints[3] = Vector3(-windowSize.x * 0.5f, 0.0f, windowSize.x * 0.3f);
231 path.SetProperty(Path::Property::CONTROL_POINTS, controlPoints);
233 return ScrollViewPagePathEffect::New(path,
234 Vector3(-1.0f, 0.0f, 0.0f),
235 Toolkit::ScrollView::Property::SCROLL_FINAL_X,
236 Vector3(windowSize.x * TABLE_RELATIVE_SIZE.x, windowSize.y * TABLE_RELATIVE_SIZE.y, 0.0f),
240 /// Sets up the scroll view rulers
241 void SetupScrollViewRulers(ScrollView& scrollView, const uint16_t windowWidth, const float pageWidth, const int totalPages)
243 // Update Ruler info. for the scroll-view
244 Dali::Toolkit::RulerPtr rulerX = new FixedRuler(pageWidth);
245 Dali::Toolkit::RulerPtr rulerY = new DefaultRuler();
246 rulerX->SetDomain(RulerDomain(0.0f, (totalPages + 1) * windowWidth * TABLE_RELATIVE_SIZE.x * 0.5f, true));
248 scrollView.SetRulerX(rulerX);
249 scrollView.SetRulerY(rulerY);
254 DaliTableView::DaliTableView(Application& application)
255 : mApplication(application),
268 mSortAlphabetically(false)
270 application.InitSignal().Connect(this, &DaliTableView::Initialize);
273 void DaliTableView::AddExample(Example example)
275 mExampleList.push_back(example);
278 void DaliTableView::SortAlphabetically(bool sortAlphabetically)
280 mSortAlphabetically = sortAlphabetically;
283 void DaliTableView::Initialize(Application& application)
285 Window window = application.GetWindow();
286 window.KeyEventSignal().Connect(this, &DaliTableView::OnKeyEvent);
287 const Window::WindowSize windowSize = window.GetSize();
290 mRootActor = CreateBackground("LauncherBackground");
291 window.Add(mRootActor);
294 ImageView logo = ImageView::New(LOGO_PATH);
295 logo.SetProperty(Actor::Property::NAME, "LOGO_IMAGE");
296 logo.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
297 logo.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 0.1f, 0.5f));
298 logo.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
299 // The logo should appear on top of everything.
300 logo.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
301 mRootActor.Add(logo);
303 // Show version in a popup when log is tapped
304 mLogoTapDetector = TapGestureDetector::New();
305 mLogoTapDetector.Attach(logo);
306 mLogoTapDetector.DetectedSignal().Connect(this, &DaliTableView::OnLogoTapped);
308 // Scrollview occupying the majority of the screen
309 mScrollView = ScrollView::New();
310 mScrollView.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
311 mScrollView.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 1.0f - 0.05f, 0.5f));
312 mScrollView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
313 mScrollView.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT);
314 mScrollView.SetProperty(Actor::Property::SIZE_MODE_FACTOR, Vector3(0.0f, 0.6f, 0.0f));
316 const float buttonsPageMargin = (1.0f - TABLE_RELATIVE_SIZE.x) * 0.5f * windowSize.GetWidth();
317 mScrollView.SetProperty(Actor::Property::PADDING, Padding(buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f));
319 mScrollView.SetAxisAutoLock(true);
320 mScrollView.ScrollCompletedSignal().Connect(this, &DaliTableView::OnScrollComplete);
321 mScrollView.ScrollStartedSignal().Connect(this, &DaliTableView::OnScrollStart);
322 mScrollView.TouchedSignal().Connect(this, &DaliTableView::OnScrollTouched);
324 mPageWidth = windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f;
326 mBubbleAnimator.Initialize(mRootActor, mScrollView);
328 mRootActor.Add(mScrollView);
330 // Add scroll view effect and setup constraints on pages
331 ApplyScrollViewEffect();
333 // Add pages and tiles
336 // Remove constraints for inner cube effect
337 ApplyCubeEffectToPages();
339 Dali::Window winHandle = application.GetWindow();
341 if(windowSize.GetWidth() <= windowSize.GetHeight())
343 winHandle.AddAvailableOrientation(Dali::WindowOrientation::PORTRAIT);
344 winHandle.RemoveAvailableOrientation(Dali::WindowOrientation::LANDSCAPE);
345 winHandle.AddAvailableOrientation(Dali::WindowOrientation::PORTRAIT_INVERSE);
346 winHandle.RemoveAvailableOrientation(Dali::WindowOrientation::LANDSCAPE_INVERSE);
350 winHandle.AddAvailableOrientation(Dali::WindowOrientation::LANDSCAPE);
351 winHandle.RemoveAvailableOrientation(Dali::WindowOrientation::PORTRAIT);
352 winHandle.AddAvailableOrientation(Dali::WindowOrientation::LANDSCAPE_INVERSE);
353 winHandle.RemoveAvailableOrientation(Dali::WindowOrientation::PORTRAIT_INVERSE);
359 void DaliTableView::CreateFocusEffect()
361 // Hook the required signals to manage the focus.
362 auto keyboardFocusManager = KeyboardFocusManager::Get();
363 keyboardFocusManager.PreFocusChangeSignal().Connect(this, &DaliTableView::OnKeyboardPreFocusChange);
364 keyboardFocusManager.FocusedActorEnterKeySignal().Connect(this, &DaliTableView::OnFocusedActorActivated);
366 // Loop to create both actors for the focus highlight effect.
367 for(unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
369 mFocusEffect[i].actor = ImageView::New();
370 mFocusEffect[i].actor.SetStyleName("FocusActor");
371 mFocusEffect[i].actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
372 mFocusEffect[i].actor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
373 mFocusEffect[i].actor.SetProperty(Actor::Property::INHERIT_SCALE, false);
374 mFocusEffect[i].actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
376 KeyFrames alphaKeyFrames = KeyFrames::New();
377 alphaKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_ALPHA);
378 alphaKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, KEYBOARD_FOCUS_ANIMATING_ALPHA);
379 alphaKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_ALPHA);
381 KeyFrames scaleKeyFrames = KeyFrames::New();
382 scaleKeyFrames.Add(0.0f, KEYBOARD_FOCUS_FINAL_SCALE);
383 scaleKeyFrames.Add(KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, FOCUS_INDICATOR_ANIMATING_SCALE);
384 scaleKeyFrames.Add(KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_SCALE);
386 mFocusEffect[i].animation = Animation::New(KEYBOARD_FOCUS_ANIMATION_DURATION);
387 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA), alphaKeyFrames);
388 mFocusEffect[i].animation.AnimateBetween(Property(mFocusEffect[i].actor, Actor::Property::SCALE), scaleKeyFrames);
390 mFocusEffect[i].animation.SetLoopCount(KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT);
393 // Parent the secondary effect from the primary.
394 mFocusEffect[0].actor.Add(mFocusEffect[1].actor);
396 keyboardFocusManager.SetFocusIndicatorActor(mFocusEffect[0].actor);
398 // Connect to the on & off scene signals of the indicator which represents when it is enabled & disabled respectively
399 mFocusEffect[0].actor.OnSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorEnabled);
400 mFocusEffect[0].actor.OffSceneSignal().Connect(this, &DaliTableView::OnFocusIndicatorDisabled);
403 void DaliTableView::OnFocusIndicatorEnabled(Actor /* actor */)
405 // Play the animation on the 1st glow object.
406 mFocusEffect[0].animation.Play();
408 // Stagger the animation on the 2nd glow object half way through.
409 mFocusEffect[1].animation.PlayFrom(KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f);
412 void DaliTableView::OnFocusIndicatorDisabled(Dali::Actor /* actor */)
414 // Stop the focus effect animations
415 for(auto i = 0u; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i)
417 mFocusEffect[i].animation.Stop();
421 void DaliTableView::ApplyCubeEffectToPages()
423 ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast(mScrollViewEffect);
424 unsigned int pageCount(0);
425 for(std::vector<Actor>::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter)
427 Actor page = *pageIter;
428 effect.ApplyToPage(page, pageCount++);
432 void DaliTableView::Populate()
434 const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
436 mTotalPages = (mExampleList.size() + EXAMPLES_PER_PAGE - 1) / EXAMPLES_PER_PAGE;
438 // Populate ScrollView.
439 if(mExampleList.size() > 0)
441 if(mSortAlphabetically)
443 sort(mExampleList.begin(), mExampleList.end(), [](auto& lhs, auto& rhs) -> bool { return lhs.title < rhs.title; });
446 unsigned int exampleCount = 0;
447 ExampleListConstIter iter = mExampleList.begin();
449 for(int t = 0; t < mTotalPages && iter != mExampleList.end(); t++)
452 TableView page = TableView::New(ROWS_PER_PAGE, EXAMPLES_PER_ROW);
453 page.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
454 page.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
455 page.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
456 mScrollView.Add(page);
458 // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
459 const float margin = 2.0f;
460 const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
462 for(int row = 0; row < ROWS_PER_PAGE && iter != mExampleList.end(); row++)
464 for(int column = 0; column < EXAMPLES_PER_ROW && iter != mExampleList.end(); column++)
466 const Example& example = (*iter);
468 // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
469 Vector2 position(static_cast<float>(column) / (EXAMPLES_PER_ROW - 1.0f), static_cast<float>(row) / (EXAMPLES_PER_ROW - 1.0f));
470 Actor tile = CreateTile(example.name, example.title, Vector3(tileParentMultiplier, tileParentMultiplier, 1.0f), position);
471 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
472 accessibilityManager.SetFocusOrder(tile, ++exampleCount);
473 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL, example.title);
474 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile");
475 accessibilityManager.SetAccessibilityAttribute(tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT, "You can run this example");
477 tile.SetProperty(Actor::Property::PADDING, Padding(margin, margin, margin, margin));
478 page.AddChild(tile, TableView::CellPosition(row, column));
484 mPages.push_back(page);
488 SetupScrollViewRulers(mScrollView, windowSize.GetWidth(), mPageWidth, mTotalPages);
491 Actor DaliTableView::CreateTile(const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position)
493 Toolkit::ImageView focusableTile = ImageView::New();
495 focusableTile.SetStyleName("DemoTile");
496 focusableTile.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
497 focusableTile.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
498 focusableTile.SetProperty(Actor::Property::SIZE_MODE_FACTOR, sizeMultiplier);
499 focusableTile.SetProperty(Actor::Property::NAME, name);
501 // Set the tile to be keyboard focusable
502 focusableTile.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
504 // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
505 Property::Value value = Vector3(0.0f, 0.0f, 0.0f);
506 Property::Index propertyIndex = focusableTile.RegisterProperty("uCustomPosition", value);
508 // We create a constraint to perform a precalculation on the scroll-view X offset
509 // and pass it to the shader uniform, along with the tile's position.
510 Constraint shaderPosition = Constraint::New<Vector3>(focusableTile, propertyIndex, TileShaderPositionConstraint(mPageWidth, position.x));
511 shaderPosition.AddSource(Source(mScrollView, ScrollView::Property::SCROLL_POSITION));
512 shaderPosition.SetRemoveAction(Constraint::DISCARD);
513 shaderPosition.Apply();
514 //focusableTile.Add( tileContent );
516 // Create an ImageView for the 9-patch border around the tile.
517 ImageView borderImage = ImageView::New();
518 borderImage.SetStyleName("DemoTileBorder");
519 borderImage.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
520 borderImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
521 borderImage.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
522 borderImage.SetProperty(Actor::Property::OPACITY, 0.8f);
523 DevelControl::AppendAccessibilityRelation(borderImage, focusableTile, Accessibility::RelationType::CONTROLLED_BY);
524 focusableTile.Add(borderImage);
526 TextLabel label = TextLabel::New();
527 label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
528 label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
529 label.SetStyleName("LauncherLabel");
530 label.SetProperty(TextLabel::Property::MULTI_LINE, true);
531 label.SetProperty(TextLabel::Property::TEXT, title);
532 label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
533 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
534 label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
536 // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
537 label.SetProperty(Actor::Property::PADDING, Padding(TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING));
538 DevelControl::AppendAccessibilityRelation(label, focusableTile, Accessibility::RelationType::CONTROLLED_BY);
539 focusableTile.Add(label);
541 // Connect to the touch events
542 focusableTile.TouchedSignal().Connect(this, &DaliTableView::OnTilePressed);
543 focusableTile.HoveredSignal().Connect(this, &DaliTableView::OnTileHovered);
544 focusableTile.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::PUSH_BUTTON);
545 DevelControl::AccessibilityActivateSignal(focusableTile).Connect(this, [=]() {
546 DoTilePress(focusableTile, PointState::DOWN);
547 DoTilePress(focusableTile, PointState::UP);
550 return focusableTile;
553 bool DaliTableView::OnTilePressed(Actor actor, const TouchEvent& event)
555 return DoTilePress(actor, event.GetState(0));
558 bool DaliTableView::DoTilePress(Actor actor, PointState::Type pointState)
560 bool consumed = false;
562 if(PointState::DOWN == pointState)
564 mPressedActor = actor;
568 // A button press is only valid if the Down & Up events
569 // both occurred within the button.
570 if((PointState::UP == pointState) &&
571 (mPressedActor == actor))
573 // ignore Example button presses when scrolling or button animating.
574 if((!mScrolling) && (!mPressedAnimation))
576 std::string name = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
577 for(Example& example : mExampleList)
579 if(example.name == name)
581 // do nothing, until pressed animation finished.
590 mPressedAnimation = Animation::New(BUTTON_PRESS_ANIMATION_TIME);
591 mPressedAnimation.SetEndAction(Animation::DISCARD);
593 // scale the content actor within the Tile, as to not affect the placement within the Table.
594 Actor content = actor.GetChildAt(0);
595 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));
596 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));
598 // Rotate button on the Y axis when pressed.
599 mPressedAnimation.AnimateBy(Property(content, Actor::Property::ORIENTATION), Quaternion(Degree(0.0f), Degree(180.0f), Degree(0.0f)));
601 mPressedAnimation.Play();
602 mPressedAnimation.FinishedSignal().Connect(this, &DaliTableView::OnPressedAnimationFinished);
608 void DaliTableView::OnPressedAnimationFinished(Dali::Animation& source)
610 mPressedAnimation.Reset();
613 std::string name = mPressedActor.GetProperty<std::string>(Dali::Actor::Property::NAME);
615 ExecuteProcess(name, mApplication);
617 mPressedActor.Reset();
621 void DaliTableView::OnScrollStart(const Dali::Vector2& position)
625 mBubbleAnimator.PlayAnimation();
628 void DaliTableView::OnScrollComplete(const Dali::Vector2& position)
632 // move focus to 1st item of new page
633 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
634 accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0));
637 bool DaliTableView::OnScrollTouched(Actor actor, const TouchEvent& event)
639 if(PointState::DOWN == event.GetState(0))
641 mPressedActor = actor;
647 void DaliTableView::ApplyScrollViewEffect()
649 // Remove old effect if exists.
651 if(mScrollViewEffect)
653 mScrollView.RemoveEffect(mScrollViewEffect);
656 // Just one effect for now
657 mScrollViewEffect = SetupInnerPageCubeEffect(mApplication.GetWindow().GetSize(), mTotalPages);
659 mScrollView.ApplyEffect(mScrollViewEffect);
662 void DaliTableView::OnKeyEvent(const KeyEvent& event)
664 if(event.GetState() == KeyEvent::DOWN)
666 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
668 // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
670 if(mVersionPopup && (mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN))
672 mVersionPopup.SetDisplayState(Popup::HIDDEN);
682 Dali::Actor DaliTableView::OnKeyboardPreFocusChange(Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction)
684 Actor nextFocusActor = proposed;
686 if(!current && !proposed)
688 // Set the initial focus to the first tile in the current page should be focused.
689 nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
693 // ScrollView is being focused but nothing in the current page can be focused further
694 // in the given direction. We should work out which page to scroll to next.
695 int currentPage = mScrollView.GetCurrentPage();
696 int newPage = currentPage;
697 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
701 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
706 newPage = std::max(0, std::min(mTotalPages - 1, newPage));
707 if(newPage == currentPage)
709 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
711 newPage = mTotalPages - 1;
713 else if(direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT)
719 // Scroll to the page in the given direction
720 mScrollView.ScrollTo(newPage);
722 if(direction == Dali::Toolkit::Control::KeyboardFocus::LEFT)
724 // Work out the cell position for the last tile
725 int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
726 int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ((remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1);
727 int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : (remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1);
729 // Move the focus to the last tile in the new page.
730 nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
734 // Move the focus to the first tile in the new page.
735 nextFocusActor = mPages[newPage].GetChildAt(0);
739 return nextFocusActor;
742 void DaliTableView::OnFocusedActorActivated(Dali::Actor activatedActor)
746 mPressedActor = activatedActor;
748 // Activate the current focused actor;
749 DoTilePress(mPressedActor, PointState::UP);
753 bool DaliTableView::OnTileHovered(Actor actor, const HoverEvent& event)
755 KeyboardFocusManager::Get().SetCurrentFocusActor(actor);
759 void DaliTableView::OnLogoTapped(Dali::Actor actor, const Dali::TapGesture& tap)
761 // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
762 if(!mVersionPopup || (mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN))
766 mVersionPopup = CreateVersionPopup(mApplication, *this);
769 mVersionPopup.SetDisplayState(Popup::SHOWN);