Merge "Stopped image-scaling examples using Deprecated SetImage( ResourceImage )...
[platform/core/uifw/dali-demo.git] / examples / image-scaling-irregular-grid / image-scaling-irregular-grid-example.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 /**
19  * @file image-scaling-irregular-grid-example.cpp
20  * @brief Demonstrates how to use image scaling modes when loading images.
21  *
22  * If an image is going to be drawn on-screen at a lower resolution than it is
23  * stored at on-disk, the scaling feature of the image loader can be used to
24  * reduce the image to save memory, improve performance, and potentially display
25  * a better small version of the image than if the default size were loaded.
26  *
27  * The functions CreateImage and CreateImageView below show how to build an
28  * image using a scaling mode to have %Dali resize it during loading.
29  *
30  * This demo defaults to the SCALE_TO_FILL mode of ImageAttributes which makes
31  * sure that every pixel in the loaded image is filled with a source colour
32  * from the image's central region while losing the minimum number of pixels
33  * from its periphery.
34  * It is the best option for producing thumbnails of input images that have
35  * diverse aspect ratios.
36  *
37  * The other four scaling modes of dali can be cycled-through for the whole
38  * grid  using the button in the top-right of the toolbar.
39  * A single image can be cycled by clicking the image directly.
40  *
41  * @see CreateImage CreateImageView
42  */
43
44 // EXTERNAL INCLUDES
45 #include <algorithm>
46 #include <map>
47 #include <dali-toolkit/dali-toolkit.h>
48 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
49 #include <iostream>
50
51 // INTERNAL INCLUDES
52 #include "grid-flags.h"
53 #include "shared/view.h"
54
55 using namespace Dali;
56 using namespace Dali::Toolkit;
57 using namespace Dali::Demo;
58
59 namespace
60 {
61
62 /** Controls the output of application logging. */
63 //#define DEBUG_PRINT_DIAGNOSTICS;
64
65 const char* BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-gradient.jpg" );
66 const char* TOOLBAR_IMAGE( DEMO_IMAGE_DIR "top-bar.png" );
67 const char* APPLICATION_TITLE( "Image Scaling Modes" );
68 const char* TOGGLE_SCALING_IMAGE( DEMO_IMAGE_DIR "icon-change.png" );
69 const char* TOGGLE_SCALING_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-change-selected.png" );
70
71 /** The width of the grid in whole grid cells. */
72 const unsigned GRID_WIDTH = 9;
73 /** Limit the grid to be no higher than this in units of a cell. */
74 const unsigned GRID_MAX_HEIGHT = 600;
75
76 /** The space between the edge of a grid cell and the image embedded within it. */
77 const unsigned GRID_CELL_PADDING = 4;
78
79 /** The aspect ratio of cells in the image grid. */
80 const float CELL_ASPECT_RATIO = 1.33333333333333333333f;
81
82 const Dali::FittingMode::Type DEFAULT_SCALING_MODE = Dali::FittingMode::SCALE_TO_FILL;
83
84 /** The number of times to spin an image on touching, each spin taking a second.*/
85 const float SPIN_DURATION = 1.0f;
86
87 /** The target image sizes in grid cells. */
88 const Vector2 IMAGE_SIZES[] = {
89  Vector2( 1, 1 ),
90  Vector2( 2, 1 ),
91  Vector2( 3, 1 ),
92  Vector2( 1, 2 ),
93  Vector2( 1, 3 ),
94  Vector2( 2, 3 ),
95  Vector2( 3, 2 ),
96  // Large, tall configuration:
97  Vector2( GRID_WIDTH / 2, GRID_WIDTH + GRID_WIDTH / 2 ),
98  // Large, square-ish images to show shrink-to-fit well with wide and tall images:
99  Vector2( GRID_WIDTH / 2, GRID_WIDTH / 2.0f * CELL_ASPECT_RATIO + 0.5f ),
100  Vector2( GRID_WIDTH - 2, (GRID_WIDTH - 2) * CELL_ASPECT_RATIO + 0.5f ),
101 };
102 const unsigned NUM_IMAGE_SIZES = sizeof(IMAGE_SIZES) / sizeof(IMAGE_SIZES[0]);
103
104 /** Images to load into the grid. These are mostly large and non-square to
105  *  show the scaling. */
106 const char* IMAGE_PATHS[] = {
107
108   DEMO_IMAGE_DIR "dali-logo.png",
109   DEMO_IMAGE_DIR "com.samsung.dali-demo.ico",
110   DEMO_IMAGE_DIR "square_primitive_shapes.bmp",
111   DEMO_IMAGE_DIR "gallery-large-14.wbmp",
112
113   // Images that show aspect ratio changes clearly in primitive shapes:
114
115   DEMO_IMAGE_DIR "portrait_screen_primitive_shapes.gif",
116   DEMO_IMAGE_DIR "landscape_screen_primitive_shapes.gif",
117
118   // Images from other demos that are tall, wide or just large:
119
120   DEMO_IMAGE_DIR "gallery-large-1.jpg",
121   DEMO_IMAGE_DIR "gallery-large-2.jpg",
122   DEMO_IMAGE_DIR "gallery-large-3.jpg",
123   DEMO_IMAGE_DIR "gallery-large-4.jpg",
124   DEMO_IMAGE_DIR "gallery-large-5.jpg",
125   DEMO_IMAGE_DIR "gallery-large-6.jpg",
126   DEMO_IMAGE_DIR "gallery-large-7.jpg",
127   DEMO_IMAGE_DIR "gallery-large-8.jpg",
128   DEMO_IMAGE_DIR "gallery-large-9.jpg",
129   DEMO_IMAGE_DIR "gallery-large-10.jpg",
130   DEMO_IMAGE_DIR "gallery-large-11.jpg",
131   DEMO_IMAGE_DIR "gallery-large-12.jpg",
132   DEMO_IMAGE_DIR "gallery-large-13.jpg",
133   DEMO_IMAGE_DIR "gallery-large-14.jpg",
134   DEMO_IMAGE_DIR "gallery-large-15.jpg",
135   DEMO_IMAGE_DIR "gallery-large-16.jpg",
136   DEMO_IMAGE_DIR "gallery-large-17.jpg",
137   DEMO_IMAGE_DIR "gallery-large-18.jpg",
138   DEMO_IMAGE_DIR "gallery-large-19.jpg",
139   DEMO_IMAGE_DIR "gallery-large-20.jpg",
140   DEMO_IMAGE_DIR "gallery-large-21.jpg",
141
142   DEMO_IMAGE_DIR "background-1.jpg",
143   DEMO_IMAGE_DIR "background-2.jpg",
144   DEMO_IMAGE_DIR "background-3.jpg",
145   DEMO_IMAGE_DIR "background-4.jpg",
146   DEMO_IMAGE_DIR "background-5.jpg",
147   DEMO_IMAGE_DIR "background-blocks.jpg",
148   DEMO_IMAGE_DIR "background-magnifier.jpg",
149
150   DEMO_IMAGE_DIR "background-1.jpg",
151   DEMO_IMAGE_DIR "background-2.jpg",
152   DEMO_IMAGE_DIR "background-3.jpg",
153   DEMO_IMAGE_DIR "background-4.jpg",
154   DEMO_IMAGE_DIR "background-5.jpg",
155   DEMO_IMAGE_DIR "background-blocks.jpg",
156   DEMO_IMAGE_DIR "background-magnifier.jpg",
157
158   DEMO_IMAGE_DIR "book-landscape-cover-back.jpg",
159   DEMO_IMAGE_DIR "book-landscape-cover.jpg",
160   DEMO_IMAGE_DIR "book-landscape-p1.jpg",
161   DEMO_IMAGE_DIR "book-landscape-p2.jpg",
162
163   DEMO_IMAGE_DIR "book-portrait-cover.jpg",
164   DEMO_IMAGE_DIR "book-portrait-p1.jpg",
165   DEMO_IMAGE_DIR "book-portrait-p2.jpg",
166   NULL
167 };
168 const unsigned NUM_IMAGE_PATHS = sizeof(IMAGE_PATHS) / sizeof(IMAGE_PATHS[0]) - 1u;
169
170
171
172 /**
173  * Creates an ImageView
174  *
175  * @param[in] filename The path of the image.
176  * @param[in] width The width of the image in pixels.
177  * @param[in] height The height of the image in pixels.
178  * @param[in] fittingMode The mode to use when scaling the image to fit the desired dimensions.
179  */
180 ImageView CreateImageView(const std::string& filename, int width, int height, Dali::FittingMode::Type fittingMode )
181 {
182
183   ImageView imageView = ImageView::New();
184
185   Property::Map map;
186   map[Toolkit::ImageVisual::Property::URL] = filename;
187   map[Toolkit::ImageVisual::Property::DESIRED_WIDTH] = width;
188   map[Toolkit::ImageVisual::Property::DESIRED_HEIGHT] = height;
189   map[Toolkit::ImageVisual::Property::FITTING_MODE] = fittingMode;
190   imageView.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
191
192   imageView.SetName( filename );
193   imageView.SetParentOrigin(ParentOrigin::CENTER);
194   imageView.SetAnchorPoint(AnchorPoint::CENTER);
195
196   return imageView;
197 }
198
199 /** Cycle the scaling mode options. */
200 Dali::FittingMode::Type NextMode( const Dali::FittingMode::Type oldMode )
201 {
202   Dali::FittingMode::Type newMode = FittingMode::SHRINK_TO_FIT;
203   switch ( oldMode )
204   {
205     case FittingMode::SHRINK_TO_FIT:
206       newMode = FittingMode::SCALE_TO_FILL;
207       break;
208     case FittingMode::SCALE_TO_FILL:
209       newMode = FittingMode::FIT_WIDTH;
210       break;
211     case FittingMode::FIT_WIDTH:
212       newMode = FittingMode::FIT_HEIGHT;
213       break;
214     case FittingMode::FIT_HEIGHT:
215       newMode = FittingMode::SHRINK_TO_FIT;
216       break;
217   }
218   return newMode;
219 }
220
221 /**
222  * Bundle an image path with the rectangle to pack it into.
223  * */
224 struct ImageConfiguration
225 {
226   ImageConfiguration( const char * const path, const Vector2 dimensions ) :
227     path( path ),
228     dimensions( dimensions )
229   {}
230   const char * path;
231   Vector2 dimensions;
232 };
233
234 /**
235  * Post-layout image data.
236  */
237 struct PositionedImage
238 {
239   PositionedImage(ImageConfiguration& configuration, unsigned cellX, unsigned cellY, Vector2 imageGridDims) :
240     configuration( configuration ),
241     cellX( cellX ),
242     cellY( cellY ),
243     imageGridDims( imageGridDims )
244   {}
245
246   ImageConfiguration configuration;
247   unsigned cellX;
248   unsigned cellY;
249   Vector2 imageGridDims;
250 };
251
252 }
253
254 /**
255  * @brief The main class of the demo.
256  */
257 class ImageScalingIrregularGridController : public ConnectionTracker
258 {
259 public:
260
261   ImageScalingIrregularGridController( Application& application )
262   : mApplication( application ),
263     mScrolling( false )
264   {
265     std::cout << "ImageScalingIrregularGridController::ImageScalingIrregularGridController" << std::endl;
266
267     // Connect to the Application's Init signal
268     mApplication.InitSignal().Connect( this, &ImageScalingIrregularGridController::Create );
269   }
270
271   ~ImageScalingIrregularGridController()
272   {
273     // Nothing to do here.
274   }
275
276   /**
277    * One-time setup in response to Application InitSignal.
278    */
279   void Create( Application& application )
280   {
281     std::cout << "ImageScalingIrregularGridController::Create" << std::endl;
282
283     // Get a handle to the stage:
284     Stage stage = Stage::GetCurrent();
285
286     // Connect to input event signals:
287     stage.KeyEventSignal().Connect(this, &ImageScalingIrregularGridController::OnKeyEvent);
288
289     // Hide the indicator bar
290     mApplication.GetWindow().ShowIndicator(Dali::Window::INVISIBLE);
291
292     // Create a default view with a default tool bar:
293     mContentLayer = DemoHelper::CreateView( mApplication,
294                                             mView,
295                                             mToolBar,
296                                             BACKGROUND_IMAGE,
297                                             TOOLBAR_IMAGE,
298                                             "" );
299
300     // Create an image scaling toggle button. (right of toolbar)
301     Toolkit::PushButton toggleScalingButton = Toolkit::PushButton::New();
302     toggleScalingButton.SetProperty( Toolkit::DevelButton::Property::UNSELECTED_BACKGROUND_VISUAL, TOGGLE_SCALING_IMAGE );
303     toggleScalingButton.SetProperty( Toolkit::DevelButton::Property::SELECTED_BACKGROUND_VISUAL, TOGGLE_SCALING_IMAGE_SELECTED );
304     toggleScalingButton.ClickedSignal().Connect( this, &ImageScalingIrregularGridController::OnToggleScalingTouched );
305     mToolBar.AddControl( toggleScalingButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
306
307     SetTitle( APPLICATION_TITLE );
308
309     // Build the main content of the widow:
310     PopulateContentLayer( DEFAULT_SCALING_MODE );
311   }
312
313   /**
314    * Build the main part of the application's view.
315    */
316   void PopulateContentLayer( const Dali::FittingMode::Type fittingMode )
317   {
318     Stage stage = Stage::GetCurrent();
319     Vector2 stageSize = stage.GetSize();
320
321     float fieldHeight;
322     Actor imageField = BuildImageField( stageSize.x, GRID_WIDTH, GRID_MAX_HEIGHT, fittingMode, fieldHeight );
323
324     mScrollView = ScrollView::New();
325
326     mScrollView.ScrollStartedSignal().Connect( this, &ImageScalingIrregularGridController::OnScrollStarted );
327     mScrollView.ScrollCompletedSignal().Connect( this, &ImageScalingIrregularGridController::OnScrollCompleted );
328
329     mScrollView.SetAnchorPoint(AnchorPoint::CENTER);
330     mScrollView.SetParentOrigin(ParentOrigin::CENTER);
331
332     mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
333
334     mScrollView.SetAxisAutoLock( true );
335     mScrollView.SetAxisAutoLockGradient( 1.0f );
336
337     // Restrict scrolling to mostly vertical only, but with some horizontal wiggle-room:
338
339     RulerPtr rulerX = new FixedRuler( stageSize.width ); //< Pull the view back to the grid's centre-line when touch is release using a snapping ruler.
340     rulerX->SetDomain( RulerDomain( stageSize.width * -0.125f, stageSize.width * 1.125f ) ); //< Scroll slightly left/right of image field.
341     mScrollView.SetRulerX ( rulerX );
342
343     RulerPtr rulerY = new DefaultRuler(); //< Snap in multiples of a screen / stage height
344     rulerY->SetDomain( RulerDomain( - fieldHeight * 0.5f + stageSize.height * 0.5f - GRID_CELL_PADDING, fieldHeight * 0.5f + stageSize.height * 0.5f + GRID_CELL_PADDING ) );
345     mScrollView.SetRulerY ( rulerY );
346
347     mContentLayer.Add( mScrollView );
348     mScrollView.Add( imageField );
349     mGridActor = imageField;
350
351     // Create the scroll bar
352     mScrollBarVertical = ScrollBar::New(Toolkit::ScrollBar::Vertical);
353     mScrollBarVertical.SetParentOrigin(ParentOrigin::TOP_RIGHT);
354     mScrollBarVertical.SetAnchorPoint(AnchorPoint::TOP_RIGHT);
355     mScrollBarVertical.SetResizePolicy(Dali::ResizePolicy::FILL_TO_PARENT, Dali::Dimension::HEIGHT);
356     mScrollBarVertical.SetResizePolicy(Dali::ResizePolicy::FIT_TO_CHILDREN, Dali::Dimension::WIDTH);
357     mScrollView.Add(mScrollBarVertical);
358
359     mScrollBarHorizontal = ScrollBar::New(Toolkit::ScrollBar::Horizontal);
360     mScrollBarHorizontal.SetParentOrigin(ParentOrigin::BOTTOM_LEFT);
361     mScrollBarHorizontal.SetAnchorPoint(AnchorPoint::TOP_LEFT);
362     mScrollBarHorizontal.SetResizePolicy(Dali::ResizePolicy::FIT_TO_CHILDREN, Dali::Dimension::WIDTH);
363     mScrollBarHorizontal.SetOrientation(Quaternion(Radian( 1.5f * Math::PI ), Vector3::ZAXIS));
364     mScrollView.Add(mScrollBarHorizontal);
365
366     mScrollView.OnRelayoutSignal().Connect( this, &ImageScalingIrregularGridController::OnScrollViewRelayout );
367
368     // Scroll to top of grid so first images loaded are on-screen:
369     mScrollView.ScrollTo( Vector2( 0, -1000000 ) );
370   }
371
372   void OnScrollViewRelayout(Actor actor)
373   {
374     // Make the height of the horizontal scroll bar to be the same as the width of scroll view.
375     mScrollBarHorizontal.SetSize(Vector2(0.0f, mScrollView.GetRelayoutSize( Dimension::WIDTH) ));
376   }
377
378   /**
379    * Build a field of images scaled into a variety of shapes from very wide,
380    * through square, to very tall. The images are direct children of the Dali::Actor
381    * returned.
382    **/
383   Actor BuildImageField( const float fieldWidth,
384                            const unsigned gridWidth,
385                            const unsigned maxGridHeight,
386                            Dali::FittingMode::Type fittingMode,
387                            float & outFieldHeight )
388   {
389     // Generate the list of image configurations to be fitted into the field:
390
391     std::vector<ImageConfiguration> configurations;
392     configurations.reserve( NUM_IMAGE_PATHS * NUM_IMAGE_SIZES );
393     for( unsigned imageIndex = 0; imageIndex < NUM_IMAGE_PATHS; ++imageIndex )
394     {
395       for( unsigned dimensionsIndex = 0; dimensionsIndex < NUM_IMAGE_SIZES; ++ dimensionsIndex )
396       {
397         configurations.push_back( ImageConfiguration( IMAGE_PATHS[imageIndex], IMAGE_SIZES[dimensionsIndex] ) );
398       }
399     }
400     // Stir-up the list to get some nice irregularity in the generated field:
401     std::random_shuffle( configurations.begin(), configurations.end() );
402     std::random_shuffle( configurations.begin(), configurations.end() );
403
404     // Place the images in the grid:
405
406     std::vector<ImageConfiguration>::iterator config, end;
407     GridFlags grid( gridWidth, maxGridHeight );
408     std::vector<PositionedImage> placedImages;
409
410     for( config = configurations.begin(), end = configurations.end(); config != end; ++config )
411     {
412       unsigned cellX, cellY;
413       Vector2 imageGridDims;
414
415       // Allocate a region of the grid for the image:
416       bool allocated = grid.AllocateRegion( config->dimensions, cellX, cellY, imageGridDims );
417       if( !allocated )
418       {
419 #ifdef DEBUG_PRINT_DIAGNOSTICS
420           fprintf( stderr, "Failed to allocate image in grid with dims (%f, %f) and path: %s.\n", config->dimensions.x, config->dimensions.y, config->path );
421 #endif
422         continue;
423       }
424
425       placedImages.push_back( PositionedImage( *config, cellX, cellY, imageGridDims ) );
426     }
427     DALI_ASSERT_DEBUG( grid.DebugCheckGridValid() && "Cells were set more than once, indicating erroneous overlap in placing images on the grid." );
428     const unsigned actualGridHeight = grid.GetHighestUsedRow() + 1;
429
430     // Take the images images in the grid and turn their logical locations into
431     // coordinates in a frame defined by a parent actor:
432
433     Actor gridActor = Actor::New();
434     gridActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
435     gridActor.SetParentOrigin( ParentOrigin::CENTER );
436     gridActor.SetAnchorPoint( AnchorPoint::CENTER );
437
438     // Work out the constants of the grid and cell dimensions and positions:
439     const float cellWidth = fieldWidth / gridWidth;
440     const float cellHeight = cellWidth / CELL_ASPECT_RATIO;
441     const Vector2 cellSize = Vector2( cellWidth, cellHeight );
442     outFieldHeight = actualGridHeight * cellHeight;
443     const Vector2 gridOrigin = Vector2( -fieldWidth * 0.5f, -outFieldHeight * 0.5 );
444
445     // Build the image actors in their right locations in their parent's frame:
446     for( std::vector<PositionedImage>::const_iterator i = placedImages.begin(), end = placedImages.end(); i != end; ++i )
447     {
448       const PositionedImage& imageSource = *i;
449       const Vector2 imageSize = imageSource.imageGridDims * cellSize - Vector2( GRID_CELL_PADDING * 2, GRID_CELL_PADDING * 2 );
450       const Vector2 imageRegionCorner = gridOrigin + cellSize * Vector2( imageSource.cellX, imageSource.cellY );
451       const Vector2 imagePosition = imageRegionCorner + Vector2( GRID_CELL_PADDING , GRID_CELL_PADDING ) + imageSize * 0.5f;
452
453       ImageView image = CreateImageView( imageSource.configuration.path, imageSize.x, imageSize.y, fittingMode );
454       image.SetPosition( Vector3( imagePosition.x, imagePosition.y, 0 ) );
455       image.SetSize( imageSize );
456       image.TouchSignal().Connect( this, &ImageScalingIrregularGridController::OnTouchImage );
457       mFittingModes[image.GetId()] = fittingMode;
458       mResourceUrls[image.GetId()] = imageSource.configuration.path;
459       mSizes[image.GetId()] = imageSize;
460
461       gridActor.Add( image );
462     }
463
464     return gridActor;
465   }
466
467  /**
468   * Upon Touching an image (Release), change its scaling mode and make it spin, provided we're not scrolling.
469   * @param[in] actor The actor touched
470   * @param[in] event The Touch information.
471   */
472   bool OnTouchImage( Actor actor, const TouchData& event )
473   {
474     if( ( event.GetPointCount() > 0 ) && ( !mScrolling ) )
475     {
476       if( event.GetState( 0 ) == PointState::UP )
477       {
478         // Spin the image a few times:
479         Animation animation = Animation::New(SPIN_DURATION);
480         animation.AnimateBy( Property( actor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree(360.0f * SPIN_DURATION) ), Vector3::XAXIS ), AlphaFunction::EASE_OUT );
481         animation.Play();
482
483         // Change the scaling mode:
484         const unsigned id = actor.GetId();
485         Dali::FittingMode::Type newMode = NextMode( mFittingModes[id] );
486         const Vector2 imageSize = mSizes[actor.GetId()];
487
488         ImageView imageView = ImageView::DownCast( actor );
489         if( imageView)
490         {
491           Property::Map map;
492           map[Visual::Property::TYPE] = Visual::IMAGE;
493           map[ImageVisual::Property::URL] = mResourceUrls[id];
494           map[ImageVisual::Property::DESIRED_WIDTH] = imageSize.width + 0.5f;
495           map[ImageVisual::Property::DESIRED_HEIGHT] =  imageSize.height + 0.5f;
496           map[ImageVisual::Property::FITTING_MODE] = newMode;
497           imageView.SetProperty( ImageView::Property::IMAGE, map );
498         }
499
500         mFittingModes[id] = newMode;
501       }
502     }
503     return false;
504   }
505
506  /**
507   * Main key event handler.
508   * Quit on escape key.
509   */
510   void OnKeyEvent(const KeyEvent& event)
511   {
512     if( event.state == KeyEvent::Down )
513     {
514       if( IsKey( event, Dali::DALI_KEY_ESCAPE )
515           || IsKey( event, Dali::DALI_KEY_BACK ) )
516       {
517         mApplication.Quit();
518       }
519     }
520   }
521
522  /**
523   * Signal handler, called when the 'Scaling' button has been touched.
524   *
525   * @param[in] button The button that was pressed.
526   */
527   bool OnToggleScalingTouched( Button button )
528   {
529     const unsigned numChildren = mGridActor.GetChildCount();
530
531     for( unsigned i = 0; i < numChildren; ++i )
532     {
533       ImageView gridImageView = ImageView::DownCast( mGridActor.GetChildAt( i ) );
534       if( gridImageView )
535       {
536         // Cycle the scaling mode options:
537         unsigned int id = gridImageView.GetId();
538
539         const Vector2 imageSize = mSizes[ id ];
540         Dali::FittingMode::Type newMode = NextMode( mFittingModes[ id ] );
541
542         Property::Map map;
543         map[Visual::Property::TYPE] = Visual::IMAGE;
544         map[ImageVisual::Property::URL] = mResourceUrls[id];
545         map[ImageVisual::Property::DESIRED_WIDTH] = imageSize.width;
546         map[ImageVisual::Property::DESIRED_HEIGHT] =  imageSize.height;
547         map[ImageVisual::Property::FITTING_MODE] = newMode;
548         gridImageView.SetProperty( ImageView::Property::IMAGE, map );
549
550
551
552         mFittingModes[ id ] = newMode;
553
554         SetTitle( std::string( newMode == FittingMode::SHRINK_TO_FIT ? "SHRINK_TO_FIT" : newMode == FittingMode::SCALE_TO_FILL ?  "SCALE_TO_FILL" : newMode == FittingMode::FIT_WIDTH ? "FIT_WIDTH" : "FIT_HEIGHT" ) );
555       }
556     }
557     return true;
558   }
559
560   /**
561    * Sets/Updates the title of the View
562    * @param[in] title The new title for the view.
563    */
564   void SetTitle(const std::string& title)
565   {
566     if(!mTitleActor)
567     {
568       mTitleActor = DemoHelper::CreateToolBarLabel( "" );
569       // Add title to the tool bar.
570       mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Alignment::HorizontalCenter );
571     }
572
573     mTitleActor.SetProperty( TextLabel::Property::TEXT, title );
574   }
575
576   /**
577    * When scroll starts (i.e. user starts to drag scrollview),
578    * note this state (mScrolling = true)
579    * @param[in] position Current Scroll Position
580    */
581   void OnScrollStarted( const Vector2& position )
582   {
583     mScrolling = true;
584   }
585
586   /**
587    * When scroll starts (i.e. user stops dragging scrollview, and scrollview has snapped to destination),
588    * note this state (mScrolling = false).
589    * @param[in] position Current Scroll Position
590    */
591   void OnScrollCompleted( const Vector2& position )
592   {
593     mScrolling = false;
594   }
595
596 private:
597   Application&  mApplication;
598
599   Layer mContentLayer;                ///< The content layer (contains non gui chrome actors)
600   Toolkit::Control mView;             ///< The View instance.
601   Toolkit::ToolBar mToolBar;          ///< The View's Toolbar.
602   TextLabel mTitleActor;               ///< The Toolbar's Title.
603   Actor mGridActor;                   ///< The container for the grid of images
604   ScrollView mScrollView;             ///< ScrollView UI Component
605   ScrollBar mScrollBarVertical;
606   ScrollBar mScrollBarHorizontal;
607   bool mScrolling;                    ///< ScrollView scrolling state (true = scrolling, false = stationary)
608   std::map<unsigned, Dali::FittingMode::Type> mFittingModes; ///< Stores the current scaling mode of each image, keyed by image actor id.
609   std::map<unsigned, std::string> mResourceUrls; ///< Stores the url of each image, keyed by image actor id.
610   std::map<unsigned, Vector2> mSizes; ///< Stores the current size of each image, keyed by image actor id.
611 };
612
613 void RunTest( Application& application )
614 {
615   ImageScalingIrregularGridController test( application );
616
617   application.MainLoop();
618 }
619
620 /** Entry point for Linux & Tizen applications */
621 int DALI_EXPORT_API main( int argc, char **argv )
622 {
623   Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
624
625   RunTest( application );
626
627   return 0;
628 }