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