2 * Copyright (c) 2016 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"
25 #include <dali/devel-api/images/distance-field.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/dali-toolkit.h>
29 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
32 #include "shared/view.h"
33 #include "shared/utility.h"
36 using namespace Dali::Toolkit;
38 ///////////////////////////////////////////////////////////////////////////////
43 const std::string LOGO_PATH( DEMO_IMAGE_DIR "Logo-for-demo.png" );
44 const std::string TILE_BACKGROUND(DEMO_IMAGE_DIR "item-background.9.png");
45 const std::string TILE_BACKGROUND_ALPHA( DEMO_IMAGE_DIR "demo-tile-texture.9.png" );
47 const float TILE_LABEL_PADDING = 8.0f; ///< Border between edge of tile and the example text
48 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f; ///< Time to perform button scale effect.
49 const float ROTATE_ANIMATION_TIME = 0.5f; ///< Time to perform rotate effect.
50 const int MAX_PAGES = 256; ///< Maximum pages (arbitrary safety limit)
51 const int EXAMPLES_PER_ROW = 3;
52 const int ROWS_PER_PAGE = 3;
53 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
54 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
55 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
56 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f ); ///< ScrollView's relative size to its parent
57 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.
58 const float STENCIL_RELATIVE_SIZE = 1.0f;
60 const float EFFECT_SNAP_DURATION = 0.66f; ///< Scroll Snap Duration for Effects
61 const float EFFECT_FLICK_DURATION = 0.5f; ///< Scroll Flick Duration for Effects
62 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
63 const Vector4 TILE_COLOR( 0.4f, 0.6f, 0.9f, 0.6f );
65 const Vector4 BUBBLE_COLOR[] =
67 Vector4( 0.3255f, 0.3412f, 0.6353f, 0.32f ),
68 Vector4( 0.3647f, 0.7569f, 0.8157f, 0.32f ),
69 Vector4( 0.3804f, 0.7412f, 0.6510f, 0.32f ),
70 Vector4( 1.f, 1.f, 1.f, 0.13f )
72 const int NUMBER_OF_BUBBLE_COLOR( sizeof(BUBBLE_COLOR) / sizeof(BUBBLE_COLOR[0]) );
74 const int NUM_BACKGROUND_IMAGES = 18;
75 const float BACKGROUND_SWIPE_SCALE = 0.025f;
76 const float BACKGROUND_SPREAD_SCALE = 1.5f;
77 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
78 const float SCALE_SPEED = 10.0f;
79 const float SCALE_SPEED_SIN = 0.1f;
81 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
83 const Vector4 BACKGROUND_COLOR( 0.3569f, 0.5451f, 0.7294f, 1.0f );
85 const float BUBBLE_MIN_Z = -1.0;
86 const float BUBBLE_MAX_Z = 0.0f;
88 // This shader takes a texture.
89 // An alpha discard is performed.
90 // The shader uses the tiles position within the scroll-view page and the scroll-views rotation position to create a parallax effect.
91 const char* FRAGMENT_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
92 varying mediump vec2 vTexCoord;
93 varying mediump vec3 vIllumination;
94 uniform lowp vec4 uColor;
95 uniform sampler2D sTexture;
96 uniform mediump vec3 uCustomPosition;
100 if( texture2D( sTexture, vTexCoord ).a <= 0.0001 )
105 mediump vec2 wrapTexCoord = vec2( ( vTexCoord.x / 4.0 ) + ( uCustomPosition.x / 4.0 ) + ( uCustomPosition.z / 2.0 ), vTexCoord.y / 4.0 );
106 mediump vec4 color = texture2D( sTexture, wrapTexCoord );
107 mediump float positionWeight = ( uCustomPosition.y + 0.3 ) * color.r * 2.0;
109 gl_FragColor = vec4( positionWeight, positionWeight, positionWeight, 0.9 ) * uColor + vec4( uColor.xyz, 0.0 );
114 * Creates the background image
116 Control CreateBackground( std::string stylename )
118 Control background = Control::New();
119 Stage::GetCurrent().Add( background );
120 background.SetStyleName( stylename );
121 background.SetName( "BACKGROUND" );
122 background.SetAnchorPoint( AnchorPoint::CENTER );
123 background.SetParentOrigin( ParentOrigin::CENTER );
124 background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
129 * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
131 struct AnimateBubbleConstraint
134 AnimateBubbleConstraint( const Vector3& initialPos, float scale )
135 : mInitialX( initialPos.x ),
140 void operator()( Vector3& position, const PropertyInputContainer& inputs )
142 const Vector3& parentSize = inputs[1]->GetVector3();
143 const Vector3& childSize = inputs[2]->GetVector3();
145 // Wrap bubbles vertically.
146 float range = parentSize.y + childSize.y;
147 // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
148 position.y -= range * ( floor( position.y / range ) + 0.5f );
150 // Bubbles X position moves parallax to horizontal
151 // panning by a scale factor unique to each bubble.
152 position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
161 * Constraint to precalculate values from the scroll-view
162 * and tile positions to pass to the tile shader.
164 struct TileShaderPositionConstraint
166 TileShaderPositionConstraint( float pageWidth, float tileXOffset )
167 : mPageWidth( pageWidth ),
168 mTileXOffset( tileXOffset )
172 void operator()( Vector3& position, const PropertyInputContainer& inputs )
174 // Set up position.x as the tiles X offset (0.0 -> 1.0).
175 position.x = mTileXOffset;
176 // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
177 position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
178 // Set up position.y as a rectified version of the scroll-views X offset.
179 // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
180 if( position.z > 0.5f )
182 position.y = 1.0f - position.z;
186 position.y = position.z;
195 bool CompareByTitle( const Example& lhs, const Example& rhs )
197 return lhs.title < rhs.title;
202 DaliTableView::DaliTableView( Application& application )
203 : mApplication( application ),
216 mBackgroundAnimations(),
220 mSortAlphabetically( false ),
221 mBackgroundAnimsPlaying( false )
223 application.InitSignal().Connect( this, &DaliTableView::Initialize );
226 DaliTableView::~DaliTableView()
230 void DaliTableView::AddExample( Example example )
232 mExampleList.push_back( example );
235 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
237 mSortAlphabetically = sortAlphabetically;
240 void DaliTableView::Initialize( Application& application )
242 Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
243 const Vector2 stageSize = Stage::GetCurrent().GetSize();
246 mRootActor = CreateBackground( "LauncherBackground" );
247 Stage::GetCurrent().Add( mRootActor );
250 ImageView logo = CreateLogo( LOGO_PATH );
251 logo.SetName( "LOGO_IMAGE" );
252 logo.SetAnchorPoint( AnchorPoint::TOP_CENTER );
253 logo.SetParentOrigin( Vector3( 0.5f, 0.1f, 0.5f ) );
254 logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
255 // The logo should appear on top of everything.
256 logo.SetDrawMode( DrawMode::OVERLAY_2D );
258 // Show version in a popup when log is tapped
259 mLogoTapDetector = TapGestureDetector::New();
260 mLogoTapDetector.Attach( logo );
261 mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
263 // Scrollview occupying the majority of the screen
264 mScrollView = ScrollView::New();
265 mScrollView.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
266 mScrollView.SetParentOrigin( Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
267 mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
268 mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
269 mScrollView.SetSizeModeFactor( Vector3( 0.0f, 0.6f, 0.0f ) );
271 const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
272 mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
274 mScrollView.SetAxisAutoLock( true );
275 mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
276 mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
277 mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
279 mPageWidth = stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f;
281 // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
282 Actor bubbleContainer = Actor::New();
283 bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
284 bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
285 bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
286 SetupBackground( bubbleContainer );
288 mRootActor.Add( logo );
289 mRootActor.Add( bubbleContainer );
290 mRootActor.Add( mScrollView );
292 // Add scroll view effect and setup constraints on pages
293 ApplyScrollViewEffect();
295 // Add pages and tiles
298 // Remove constraints for inner cube effect
299 ApplyCubeEffectToPages();
301 Dali::Window winHandle = application.GetWindow();
302 winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
303 winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
304 winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
305 winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
307 // Set initial orientation
308 unsigned int degrees = 0;
311 winHandle.ShowIndicator( Dali::Window::INVISIBLE );
313 // Background animation
314 mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
315 mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
316 mAnimationTimer.Start();
317 mBackgroundAnimsPlaying = true;
319 KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
320 KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
321 AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
324 void DaliTableView::ApplyCubeEffectToPages()
326 ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
327 unsigned int pageCount(0);
328 for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
330 Actor page = *pageIter;
331 effect.ApplyToPage( page, pageCount++ );
335 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
339 void DaliTableView::Populate()
341 const Vector2 stageSize = Stage::GetCurrent().GetSize();
343 mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
345 // Populate ScrollView.
346 if( mExampleList.size() > 0 )
348 if( mSortAlphabetically )
350 sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
353 unsigned int exampleCount = 0;
354 ExampleListConstIter iter = mExampleList.begin();
356 for( int t = 0; t < mTotalPages; t++ )
359 TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
360 page.SetAnchorPoint( AnchorPoint::CENTER );
361 page.SetParentOrigin( ParentOrigin::CENTER );
362 page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
363 mScrollView.Add( page );
365 // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
366 const float margin = 2.0f;
367 const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
369 for(int row = 0; row < ROWS_PER_PAGE; row++)
371 for(int column = 0; column < EXAMPLES_PER_ROW; column++)
373 const Example& example = ( *iter );
375 // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
376 Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
377 Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
378 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
379 accessibilityManager.SetFocusOrder( tile, ++exampleCount );
380 accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
382 accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
383 accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
384 "You can run this example" );
386 tile.SetPadding( Padding( margin, margin, margin, margin ) );
387 page.AddChild( tile, TableView::CellPosition( row, column ) );
391 if( iter == mExampleList.end() )
397 if( iter == mExampleList.end() )
403 // Set tableview position
404 Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
405 page.SetPosition( pagePos );
407 mPages.push_back( page );
409 if( iter == mExampleList.end() )
416 // Update Ruler info.
417 mScrollRulerX = new FixedRuler( mPageWidth );
418 mScrollRulerY = new DefaultRuler();
419 mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
420 mScrollRulerY->Disable();
421 mScrollView.SetRulerX( mScrollRulerX );
422 mScrollView.SetRulerY( mScrollRulerY );
425 void DaliTableView::Rotate( unsigned int degrees )
427 // Resize the root actor
428 Vector2 stageSize = Stage::GetCurrent().GetSize();
429 Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
431 if( degrees == 90 || degrees == 270 )
433 targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
436 if( mRotateAnimation )
438 mRotateAnimation.Stop();
439 mRotateAnimation.Clear();
442 mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
443 mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
444 mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
445 mRotateAnimation.Play();
448 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
450 Actor content = Actor::New();
451 content.SetName( name );
452 content.SetAnchorPoint( AnchorPoint::CENTER );
453 content.SetParentOrigin( ParentOrigin::CENTER );
454 content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
455 content.SetSizeModeFactor( sizeMultiplier );
457 Toolkit::ImageView tileContent = ImageView::New();
458 tileContent.SetParentOrigin( ParentOrigin::CENTER );
459 tileContent.SetAnchorPoint( AnchorPoint::CENTER );
460 tileContent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
462 // Add the image via the property first.
463 tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, TILE_BACKGROUND_ALPHA );
464 // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
465 Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
466 Property::Index propertyIndex = tileContent.RegisterProperty( "uCustomPosition", value );
468 // Add a shader to the image (details in shader source).
470 Property::Map customShader;
471 customShader[ Visual::Shader::Property::FRAGMENT_SHADER ] = FRAGMENT_SHADER_TEXTURED;
472 map[ Visual::Property::SHADER ] = customShader;
473 tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
474 tileContent.SetColor( TILE_COLOR );
476 // We create a constraint to perform a precalculation on the scroll-view X offset
477 // and pass it to the shader uniform, along with the tile's position.
478 Constraint shaderPosition = Constraint::New < Vector3 > ( tileContent, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
479 shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
480 shaderPosition.SetRemoveAction( Constraint::Discard );
481 shaderPosition.Apply();
482 content.Add( tileContent );
484 // Create an ImageView for the 9-patch border around the tile.
485 ImageView image = ImageView::New( TILE_BACKGROUND );
486 image.SetAnchorPoint( AnchorPoint::CENTER );
487 image.SetParentOrigin( ParentOrigin::CENTER );
488 image.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
489 image.SetOpacity( 0.8f );
490 tileContent.Add( image );
492 TextLabel label = TextLabel::New();
493 label.SetAnchorPoint( AnchorPoint::CENTER );
494 label.SetParentOrigin( ParentOrigin::CENTER );
495 label.SetStyleName( "LauncherLabel" );
496 label.SetProperty( TextLabel::Property::MULTI_LINE, true );
497 label.SetProperty( TextLabel::Property::TEXT, title );
498 label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
499 label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
500 label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
502 // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
503 label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
504 content.Add( label );
506 // Set the tile to be keyboard focusable
507 content.SetKeyboardFocusable(true);
509 // connect to the touch events
510 content.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
511 content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
516 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
518 return DoTilePress( actor, event.GetState( 0 ) );
521 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
523 bool consumed = false;
525 if( PointState::DOWN == pointState )
527 mPressedActor = actor;
531 // A button press is only valid if the Down & Up events
532 // both occurred within the button.
533 if( ( PointState::UP == pointState ) &&
534 ( mPressedActor == actor ) )
536 // ignore Example button presses when scrolling or button animating.
537 if( ( !mScrolling ) && ( !mPressedAnimation ) )
539 std::string name = actor.GetName();
540 const ExampleListIter end = mExampleList.end();
541 for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
543 if( (*iter).name == name )
545 // do nothing, until pressed animation finished.
554 mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
555 mPressedAnimation.SetEndAction( Animation::Discard );
557 // scale the content actor within the Tile, as to not affect the placement within the Table.
558 Actor content = actor.GetChildAt(0);
559 mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
560 TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
561 mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
562 TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
564 // Rotate button on the Y axis when pressed.
565 mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
567 mPressedAnimation.Play();
568 mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
574 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
576 mPressedAnimation.Reset();
579 std::string name = mPressedActor.GetName();
581 std::stringstream stream;
582 stream << DEMO_EXAMPLE_BIN << name.c_str();
586 execlp( stream.str().c_str(), name.c_str(), NULL );
587 DALI_ASSERT_ALWAYS(false && "exec failed!");
589 mPressedActor.Reset();
593 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
600 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
604 // move focus to 1st item of new page
605 AccessibilityManager accessibilityManager = AccessibilityManager::Get();
606 accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
609 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
611 if( PointState::DOWN == event.GetState( 0 ) )
613 mPressedActor = actor;
619 void DaliTableView::ApplyScrollViewEffect()
621 // Remove old effect if exists.
623 if( mScrollViewEffect )
625 mScrollView.RemoveEffect( mScrollViewEffect );
628 // Just one effect for now
629 SetupInnerPageCubeEffect();
631 mScrollView.ApplyEffect( mScrollViewEffect );
634 void DaliTableView::SetupInnerPageCubeEffect()
636 const Vector2 stageSize = Stage::GetCurrent().GetSize();
638 Dali::Path path = Dali::Path::New();
639 Dali::Property::Array points;
641 points[0] = Vector3( stageSize.x*0.5, 0.0f, stageSize.x*0.5f);
642 points[1] = Vector3( 0.0f, 0.0f, 0.0f );
643 points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
644 path.SetProperty( Path::Property::POINTS, points );
646 Dali::Property::Array controlPoints;
647 controlPoints.Resize(4);
648 controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
649 controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
650 controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
651 controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
652 path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
655 mScrollViewEffect = ScrollViewPagePathEffect::New(path,
656 Vector3(-1.0f,0.0f,0.0f),
657 Toolkit::ScrollView::Property::SCROLL_FINAL_X,
658 Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
661 void DaliTableView::OnKeyEvent( const KeyEvent& event )
663 if( event.state == KeyEvent::Down )
665 if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
667 // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
669 if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
671 mVersionPopup.SetDisplayState( Popup::HIDDEN );
681 void DaliTableView::SetupBackground( Actor bubbleContainer )
683 // Create distance field shapes.
684 BufferImage distanceFields[2];
685 Size imageSize( 512, 512 );
687 CreateShapeImage( CIRCLE, imageSize, distanceFields[0] );
688 CreateShapeImage( BUBBLE, imageSize, distanceFields[1] );
690 // Add bubbles to the bubbleContainer.
691 // Note: The bubbleContainer is parented externally to this function.
692 AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceFields );
695 void DaliTableView::InitialiseBackgroundActors( Actor actor )
697 // Delete current animations
698 mBackgroundAnimations.clear();
700 // Create new animations
701 const Vector3 size = actor.GetTargetSize();
703 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
705 Actor child = actor.GetChildAt( i );
707 // Calculate a random position
708 Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
709 Random::Range( -size.y, size.y ),
710 Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
712 child.SetPosition( childPos );
714 // Define bubble horizontal parallax and vertical wrapping
715 Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
716 animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
717 animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
718 animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
719 animConstraint.SetRemoveAction( Constraint::Discard );
720 animConstraint.Apply();
723 Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
724 animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
725 animation.SetLooping( true );
727 mBackgroundAnimations.push_back( animation );
731 void DaliTableView::AddBackgroundActors( Actor layer, int count, BufferImage* distanceField )
733 for( int i = 0; i < count; ++i )
735 float randSize = Random::Range( 10.0f, 400.0f );
736 int distanceFieldType = static_cast<int>( Random::Range( 0.0f, 1.0f ) + 0.5f );
737 ImageView dfActor = ImageView::New( distanceField[ distanceFieldType ] );
738 dfActor.SetSize( Vector2( randSize, randSize ) );
739 dfActor.SetParentOrigin( ParentOrigin::CENTER );
741 Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
742 dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, effect );
743 dfActor.SetColor( BUBBLE_COLOR[ i%NUMBER_OF_BUBBLE_COLOR ] );
745 layer.Add( dfActor );
748 // Positioning will occur when the layer is relaid out
749 layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
752 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
754 // this bitmap will hold the alpha map for the distance field shader
755 distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
757 // Generate bit pattern
758 std::vector< unsigned char > imageDataA8;
759 imageDataA8.reserve( size.width * size.height ); // A8
764 GenerateCircle( size, imageDataA8 );
767 GenerateCircle( size, imageDataA8, true );
773 PixelBuffer* buffer = distanceFieldOut.GetBuffer();
776 GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
777 distanceFieldOut.Update();
781 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut, bool hollow )
783 const float radius = size.width * 0.5f * size.width * 0.5f;
784 Vector2 center( size.width / 2, size.height / 2 );
786 for( int h = 0; h < size.height; ++h )
788 for( int w = 0; w < size.width; ++w )
791 Vector2 dist = pos - center;
793 float distance = ( dist.x * dist.x ) + ( dist.y * dist.y );
795 // If hollow, check the distance against a min & max value, otherwise just use the max value.
796 unsigned char fillByte = ( hollow ? ( ( distance <= radius ) && ( distance > ( radius * 0.7f ) ) ) : ( distance <= radius ) ) ? 0xFF : 0x00;
798 distanceFieldOut.push_back( fillByte );
803 ImageView DaliTableView::CreateLogo( std::string imagePath )
805 ImageView logo = ImageView::New( imagePath );
807 logo.SetAnchorPoint( AnchorPoint::CENTER );
808 logo.SetParentOrigin( ParentOrigin::CENTER );
813 bool DaliTableView::PauseBackgroundAnimation()
820 void DaliTableView::PauseAnimation()
822 if( mBackgroundAnimsPlaying )
824 for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
826 Animation anim = *animIter;
831 mBackgroundAnimsPlaying = false;
835 void DaliTableView::PlayAnimation()
837 if ( !mBackgroundAnimsPlaying )
839 for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
841 Animation anim = *animIter;
846 mBackgroundAnimsPlaying = true;
849 mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
852 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
854 Actor nextFocusActor = proposed;
856 if( !current && !proposed )
858 // Set the initial focus to the first tile in the current page should be focused.
859 nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
863 // ScrollView is being focused but nothing in the current page can be focused further
864 // in the given direction. We should work out which page to scroll to next.
865 int currentPage = mScrollView.GetCurrentPage();
866 int newPage = currentPage;
867 if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
871 else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
876 newPage = std::max(0, std::min(mTotalPages - 1, newPage));
877 if( newPage == currentPage )
879 if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
881 newPage = mTotalPages - 1;
882 } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
888 // Scroll to the page in the given direction
889 mScrollView.ScrollTo(newPage);
891 if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
893 // Work out the cell position for the last tile
894 int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
895 int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
896 int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
898 // Move the focus to the last tile in the new page.
899 nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
903 // Move the focus to the first tile in the new page.
904 nextFocusActor = mPages[newPage].GetChildAt(0);
908 return nextFocusActor;
911 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
915 mPressedActor = activatedActor;
917 // Activate the current focused actor;
918 DoTilePress( mPressedActor, PointState::UP );
922 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
924 KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
928 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
930 // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
931 if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
933 if ( !mVersionPopup )
935 std::ostringstream stream;
936 stream << "DALi Core: " << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
937 stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
938 stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
940 mVersionPopup = Dali::Toolkit::Popup::New();
942 Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
943 titleActor.SetName( "titleActor" );
944 titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
946 Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
947 contentActor.SetName( "contentActor" );
948 contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
949 contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
950 contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
952 mVersionPopup.SetTitle( titleActor );
953 mVersionPopup.SetContent( contentActor );
955 mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
956 mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
957 mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
959 mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
960 Stage::GetCurrent().Add( mVersionPopup );
963 mVersionPopup.SetDisplayState( Popup::SHOWN );
967 void DaliTableView::HideVersionPopup()
969 // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
970 if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
972 mVersionPopup.SetDisplayState( Popup::HIDDEN );