[NUI] Fix CircularPagination bugs (#1744)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / CircularPagination.cs
1 /*
2  * Copyright(c) 2020 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 using System;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Components;
22
23 namespace Tizen.NUI.Wearable
24 {
25     /// <summary>
26     /// CircularPagination shows the number of pages available and the currently active page.
27     /// Especially, CircularPagination provides indicators specific to wearable device.
28     /// </summary>
29     /// <since_tizen> 8 </since_tizen>
30     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
31     [EditorBrowsable(EditorBrowsableState.Never)]
32     public class CircularPagination: Control
33     {
34         private CircularPaginationStyle circularPaginationStyle;
35
36         private VisualView container;
37
38         private List<ImageVisual> indicatorList = new List<ImageVisual>();
39
40         private bool isSymmetrical = true;
41         private int middleIndex = 9;
42         private int indicatorCount = 0;
43         private int leftIndicatorCount = 0;
44         private int rightIndicatorCount = 0;
45         private int selectedIndex = -1;
46         private bool isCenterImageSet = false; // When CenterIndicatorImageURL is set, this variable becomes true.
47         private bool isCurrentIndicatorCentered = false; // When the current indicator is the center one, this variable becomes true.
48         private bool isOddNumber = true;
49         private bool uninitializedLeftIndicator = true; // Need it when the indicators are asymmetry and the right indicator count is set earlier than left one.
50         private Animation selectAnimation = null;
51         private bool isNeedAnimation = false; // TODO : Animation will support using override function later.
52
53         Position2D[] oddArray = new Position2D[] { new Position2D(36, 74), new Position2D(47, 60), new Position2D(60, 47), new Position2D(74, 36),
54                                                    new Position2D(89, 26), new Position2D(105, 18), new Position2D(122, 11), new Position2D(139, 7),
55                                                    new Position2D(157, 4), new Position2D(175, 3), new Position2D(193, 4), new Position2D(211, 7),
56                                                    new Position2D(228, 11), new Position2D(245, 18), new Position2D(261, 26), new Position2D(276, 36),
57                                                    new Position2D(290, 47), new Position2D(303, 60), new Position2D(314, 73) };
58
59         Position2D[] evenArray = new Position2D[] { new Position2D(41, 67), new Position2D(53, 53), new Position2D(67, 41), new Position2D(81, 31),
60                                                    new Position2D(97, 22), new Position2D(113, 14), new Position2D(131, 9), new Position2D(148, 5),
61                                                    new Position2D(166, 3), new Position2D(184, 3), new Position2D(202, 5), new Position2D(220, 9),
62                                                    new Position2D(237, 14), new Position2D(253, 22), new Position2D(269, 31), new Position2D(283, 41),
63                                                    new Position2D(297, 53), new Position2D(309, 67) };
64
65         static CircularPagination() { }
66
67         /// <summary>
68         /// Creates a new instance of a CircularPagination.
69         /// </summary>
70         /// <since_tizen> 8 </since_tizen>
71         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public CircularPagination() : base()
74         {
75             Initialize();
76         }
77
78         /// <summary>
79         /// Creates a new instance of a CircularPagination using style.
80         /// </summary>
81         /// <since_tizen> 8 </since_tizen>
82         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
83         [EditorBrowsable(EditorBrowsableState.Never)]
84         public CircularPagination(CircularPaginationStyle style) : base(style)
85         {
86             Initialize();
87         }
88
89         /// <summary>
90         /// Gets or sets the size of the indicator.
91         /// </summary>
92         /// <since_tizen> 8 </since_tizen>
93         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
94         [EditorBrowsable(EditorBrowsableState.Never)]
95         public Size IndicatorSize
96         {
97             get
98             {
99                 return circularPaginationStyle?.IndicatorSize;
100             }
101             set
102             {
103                 if (value == null || circularPaginationStyle == null)
104                 {
105                     return;
106                 }
107                 circularPaginationStyle.IndicatorSize = value;
108                 UpdateVisual();
109             }
110         }
111
112         /// <summary>
113         /// Gets or sets the background resource of indicator.
114         /// </summary>
115         /// <since_tizen> 8 </since_tizen>
116         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
117         [EditorBrowsable(EditorBrowsableState.Never)]
118         public Selector<string> IndicatorImageURL
119         {
120             get
121             {
122                 return circularPaginationStyle?.IndicatorImageURL;
123             }
124             set
125             {
126                 if (value == null || circularPaginationStyle == null)
127                 {
128                     return;
129                 }
130                 circularPaginationStyle.IndicatorImageURL = value;
131                 UpdateVisual();
132             }
133         }
134
135         /// <summary>
136         /// Gets or sets the background resource of the center indicator.
137         /// </summary>
138         /// <since_tizen> 8 </since_tizen>
139         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
140         [EditorBrowsable(EditorBrowsableState.Never)]
141         public Selector<string> CenterIndicatorImageURL
142         {
143             get
144             {
145                 if (isCenterImageSet)
146                 {
147                     return circularPaginationStyle?.CenterIndicatorImageURL;
148                 }
149                 else
150                 {
151                     Log.Info("NUI", "CenterIndicatorImageURL is not set yet. \n");
152                     return "";
153                 }
154
155             }
156             set
157             {
158                 if (value == null || circularPaginationStyle == null)
159                 {
160                     return;
161                 }
162                 circularPaginationStyle.CenterIndicatorImageURL = value;
163                 isCenterImageSet = true;
164                 UpdateVisual();
165             }
166         }
167
168         /// <summary>
169         /// Checks whether the indicators are symmetrical or not.
170         ///
171         /// The default value is true.
172         /// If the value is true, the user just can set IndicatorCount.
173         /// If false, the user should set both the number of Left Indicators and the number of Right Indicators.
174         /// Please refer to LeftIndicatorCount and RightIndicatorCount.
175         /// </summary>
176         /// <since_tizen> 8 </since_tizen>
177         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public bool IsSymmetrical
180         {
181             get
182             {
183                 return isSymmetrical;
184             }
185             set
186             {
187                 if (isSymmetrical == value)
188                 {
189                     return;
190                 }
191                 if (value == false)
192                 {
193                     isOddNumber = true;
194                     CreateIndicator(middleIndex);
195                 }
196
197                 isSymmetrical = value;
198                 UpdateContainer();
199                 UpdateVisual();
200             }
201         }
202
203
204         /// <summary>
205         /// Gets or sets the number of the pages/indicators.
206         ///
207         /// This value is for symmetrical indicators.
208         /// </summary>
209         /// <since_tizen> 8 </since_tizen>
210         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
211         [EditorBrowsable(EditorBrowsableState.Never)]
212         public int IndicatorCount
213         {
214             get
215             {
216                 return indicatorCount;
217             }
218             set
219             {
220                 if (indicatorCount == value || indicatorCount < 0 || value <= 0)
221                 {
222                     return;
223                 }
224                 if (isSymmetrical == false)
225                 {
226                     Log.Info("NUI", "This property is not for asymmetric pagination. Change to symmetrical pagination.\n");
227                     isSymmetrical = true;
228                 }
229
230                 if (value % 2 == 1) // Odd number
231                 {
232                     isOddNumber = true;
233                 }
234                 else // Even number
235                 {
236                     isOddNumber = false;
237                 }
238
239                 if (indicatorCount < value)
240                 {
241                     int arrayIndex = 0;
242                     if (isOddNumber)
243                     {
244                         arrayIndex = (19 - value) / 2;
245                     }
246                     else
247                     {
248                         arrayIndex = (18 - value) / 2;
249                     }
250                     if (arrayIndex < 0) return;
251
252                     for (int i = (indicatorCount + 1); i <= value; i++)
253                     {
254                         CreateIndicator( arrayIndex );
255                         arrayIndex++;
256                     }
257
258                     // If selectedIndex is not set yet, the default value is middle index.
259                     if (selectedIndex == -1)
260                     {
261                         selectedIndex = value / 2;
262                         SelectIn(indicatorList[selectedIndex]);
263                     }
264                 }
265                 else
266                 {
267                     for (int i = value; i < indicatorCount; i++)
268                     {
269                         ImageVisual indicator = indicatorList[i];
270                         container.RemoveVisual("Indicator" + i);
271                     }
272                     indicatorList.RemoveRange(value, indicatorCount - value);
273
274                     if (selectedIndex >= value)
275                     {
276                         selectedIndex = value - 1;
277                         SelectIn(indicatorList[selectedIndex]);
278                     }
279                 }
280                 indicatorCount = value;
281
282                 UpdateContainer();
283                 UpdateVisual();
284             }
285         }
286
287         /// <summary>
288         /// Gets or sets the number of the left pages/indicators.
289         ///
290         /// This value can be set when IsSymmetrical API is false.
291         /// </summary>
292         /// <since_tizen> 8 </since_tizen>
293         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
294         [EditorBrowsable(EditorBrowsableState.Never)]
295         public int LeftIndicatorCount
296         {
297             get
298             {
299                 return leftIndicatorCount;
300             }
301             set
302             {
303                 if (isSymmetrical == true)
304                 {
305                     Log.Info("NUI", "This variable is not for symmetric pagination. \n");
306                     isSymmetrical = false;
307                     //return;
308                 }
309                 if (leftIndicatorCount == value || leftIndicatorCount < 0 || value > 9 || value < 0)
310                 {
311                     return;
312                 }
313
314                 isOddNumber = true;
315
316                 if (leftIndicatorCount < value)
317                 {
318                     for (int i = (middleIndex - value); i < (middleIndex - leftIndicatorCount); i++)
319                     {
320                         CreateIndicator( i );
321                         selectedIndex++;
322                     }
323                 }
324                 else
325                 {
326                     for (int i = 0; i < (leftIndicatorCount - value); i++)
327                     {
328                         ImageVisual indicator = indicatorList[i];
329                         container.RemoveVisual("Indicator" + i);
330                     }
331                     indicatorList.RemoveRange(0, (leftIndicatorCount - value)); // LeftIndicator starts from index 0.
332
333                     if (selectedIndex == 0)
334                     {
335                         selectedIndex++;
336                         SelectIn(indicatorList[selectedIndex]);
337                     }
338                     else
339                     {
340                         selectedIndex--;
341                         SelectIn(indicatorList[selectedIndex]);
342                     }
343                 }
344                 leftIndicatorCount = value;
345                 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
346
347                 // When RightIndicatorCount is set before, then selectedIndex should be updated using the current LeftIndicatorCount.
348                 if (uninitializedLeftIndicator && selectedIndex == 0)
349                 {
350                     selectedIndex = leftIndicatorCount;
351                 }
352                 uninitializedLeftIndicator = false;
353
354                 UpdateContainer();
355                 UpdateAsymmetry();
356             }
357         }
358
359         /// <summary>
360         /// Gets or sets the number of the right pages/indicators.
361         ///
362         /// This value can be set when IsSymmetrical API is false.
363         /// </summary>
364         /// <since_tizen> 8 </since_tizen>
365         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
366         [EditorBrowsable(EditorBrowsableState.Never)]
367         public int RightIndicatorCount
368         {
369             get
370             {
371                 return rightIndicatorCount;
372             }
373             set
374             {
375                 if (isSymmetrical == true)
376                 {
377                     Log.Info("NUI", "This variable is not for symmetric pagination. \n");
378                     isSymmetrical = false;
379                     //return;
380                 }
381                 if (rightIndicatorCount == value || rightIndicatorCount < 0 || value > 9 || value < 0)
382                 {
383                     return;
384                 }
385
386                 isOddNumber = true;
387
388                 if (rightIndicatorCount < value)
389                 {
390                     for (int i = (middleIndex + rightIndicatorCount + 1); i <= (middleIndex + value); i++)
391                     {
392                         CreateIndicator( i );
393                     }
394                 }
395                 else
396                 {
397                     for (int i = (leftIndicatorCount + value + 1); i < (leftIndicatorCount + rightIndicatorCount); i++)
398                     {
399                         ImageVisual indicator = indicatorList[i];
400                         container.RemoveVisual("Indicator" + i);
401                     }
402                     indicatorList.RemoveRange((leftIndicatorCount + value), (rightIndicatorCount - value));
403
404                     if (selectedIndex >= (leftIndicatorCount + rightIndicatorCount))
405                     {
406                         selectedIndex--;
407                         SelectIn(indicatorList[selectedIndex]);
408                     }
409                 }
410                 rightIndicatorCount = value;
411                 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
412
413                 UpdateContainer();
414                 UpdateAsymmetry();
415             }
416         }
417
418         /// <summary>
419         /// Gets or sets the index of the select indicator.
420         ///
421         /// If no value is set, the default value is the center indicator.
422         /// </summary>
423         /// <since_tizen> 8 </since_tizen>
424         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
425         [EditorBrowsable(EditorBrowsableState.Never)]
426         public int SelectedIndex
427         {
428             get
429             {
430                 return selectedIndex;
431             }
432             set
433             {
434                 if (selectedIndex == value || value < 0 || value >= indicatorCount)
435                 {
436                     return;
437                 }
438
439                 // TODO : Here it needs to add virtual function for Animation.
440
441                 if (selectedIndex >= 0)
442                 {
443                     if ( (isSymmetrical && selectedIndex < indicatorCount) ||
444                          (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
445                     {
446                         CheckCenterIndicator(selectedIndex);
447                         SelectOut(indicatorList[selectedIndex]);
448                     }
449                 }
450                 selectedIndex = value;
451                 if (selectedIndex >= 0)
452                 {
453                     if ( (isSymmetrical && selectedIndex < indicatorCount) ||
454                          (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
455                     {
456                         CheckCenterIndicator(selectedIndex);
457                         SelectIn(indicatorList[selectedIndex]);
458                     }
459                 }
460             }
461         }
462
463         /// <summary>
464         /// Retrieves the position of a indicator by index.
465         /// </summary>
466         /// <param name="index">Indicator index</param>
467         /// <returns>The position of a indicator by index</returns>
468         /// <since_tizen> 8 </since_tizen>
469         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
470         [EditorBrowsable(EditorBrowsableState.Never)]
471         public Position GetIndicatorPosition(int index)
472         {
473             if (index < 0 || index >= indicatorList.Count)
474             {
475                 return null;
476             }
477             return new Position(indicatorList[index].Position.X, indicatorList[index].Position.Y);
478         }
479
480         /// <summary>
481         /// Sets the position of a indicator by index.
482         /// </summary>
483         /// <param name="index">Indicator index</param>
484         /// <param name="position">The position of a indicator by index</param>
485         /// <since_tizen> 8 </since_tizen>
486         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
487         [EditorBrowsable(EditorBrowsableState.Never)]
488         public virtual void SetIndicatorPosition(int index, Position position)
489         {
490             // Update odd / even Array and List by each converted index.
491             if (isOddNumber)
492             {
493                 if (isSymmetrical)
494                 {
495                     oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
496                 }
497                 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
498                 {
499                     oddArray[(middleIndex - leftIndicatorCount) + index] = position;
500                 }
501                 indicatorList[index].Position.X = position.X;
502                 indicatorList[index].Position.Y = position.Y;
503             }
504             else // Only symmetry circular pagination can be even number.
505             {
506                 evenArray[(middleIndex - (indicatorCount / 2) + index)] = position;
507                 indicatorList[index].Position.X = position.X;
508                 indicatorList[index].Position.Y = position.Y;
509             }
510             UpdateVisual();
511         }
512
513         private void CreateSelectAnimation()
514         {
515             if (selectAnimation == null)
516             {
517                 selectAnimation = new Animation(250);
518             }
519         }
520
521         /// <summary>
522         /// You can override it to do your select out operation.
523         /// </summary>
524         /// <param name="selectOutIndicator">The indicator will be selected out</param>
525         /// <since_tizen> 8 </since_tizen>
526         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
527         [EditorBrowsable(EditorBrowsableState.Never)]
528         protected virtual void SelectOut(VisualMap selectOutIndicator)
529         {
530             if (!(selectOutIndicator is ImageVisual visual)) return;
531             if (isCurrentIndicatorCentered)
532             {
533                 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
534             }
535             else
536             {
537                 visual.URL = circularPaginationStyle.IndicatorImageURL.Normal;
538             }
539             visual.Opacity = 0.5f;
540         }
541
542         /// <summary>
543         /// You can override it to do your select in operation.
544         /// </summary>
545         /// <param name="selectInIndicator">The indicator will be selected in</param>
546         /// <since_tizen> 8 </since_tizen>
547         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
548         [EditorBrowsable(EditorBrowsableState.Never)]
549         protected virtual void SelectIn(VisualMap selectInIndicator)
550         {
551             if (!(selectInIndicator is ImageVisual visual)) return;
552             if (isCurrentIndicatorCentered)
553             {
554                 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
555             }
556             else
557             {
558                 visual.URL = circularPaginationStyle.IndicatorImageURL.Selected;
559             }
560             visual.Opacity = 1.0f;
561         }
562
563         /// <summary>
564         /// you can override it to create your own default style.
565         /// </summary>
566         /// <since_tizen> 8 </since_tizen>
567         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
568         [EditorBrowsable(EditorBrowsableState.Never)]
569         protected override ViewStyle CreateViewStyle()
570         {
571             return new CircularPaginationStyle();
572         }
573
574         /// <summary>
575         /// you can override it to clean-up your own resources.
576         /// </summary>
577         /// <param name="type">DisposeTypes</param>
578         /// <since_tizen> 8 </since_tizen>
579         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
580         [EditorBrowsable(EditorBrowsableState.Never)]
581         protected override void Dispose(DisposeTypes type)
582         {
583             if (disposed)
584             {
585                 return;
586             }
587
588             if (type == DisposeTypes.Explicit)
589             {
590                 if (selectAnimation != null)
591                 {
592                     if (selectAnimation.State == Animation.States.Playing)
593                     {
594                         selectAnimation.Stop();
595                     }
596                     selectAnimation.Dispose();
597                     selectAnimation = null;
598                 }
599
600                 container.RemoveAll();
601                 indicatorList.Clear();
602
603                 this.Remove(container);
604                 container.Dispose();
605                 container = null;
606             }
607
608             base.Dispose(type);
609         }
610
611         private void Initialize()
612         {
613             circularPaginationStyle = Style as CircularPaginationStyle;
614             if (circularPaginationStyle == null)
615             {
616                 throw new Exception("CircularPagination style is null.");
617             }
618
619             container = new VisualView()
620             {
621                 Name = "Container",
622                 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
623                 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
624                 PositionUsesPivotPoint = true,
625             };
626             this.Add(container);
627         }
628
629         // The parameter, index, is for the index of either oddArray or evenArray.
630         private void CreateIndicator(int index)
631         {
632             if (circularPaginationStyle == null)
633             {
634                 return;
635             }
636
637             ImageVisual indicator = new ImageVisual
638             {
639                 URL = circularPaginationStyle.IndicatorImageURL.Normal,
640                 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
641                 Opacity = 0.5f,
642             };
643
644             if (isOddNumber)
645             {
646                 indicator.Position = oddArray[index];
647             }
648             else
649             {
650                 indicator.Position = evenArray[index];
651             }
652
653             container.AddVisual("Indicator" + indicatorList.Count, indicator);
654             indicatorList.Add(indicator);
655         }
656
657         private void CheckCenterIndicator(int index)
658         {
659             if (isCenterImageSet &&
660                 (isSymmetrical && (index == indicatorCount / 2)) ||
661                 (!isSymmetrical && (index == leftIndicatorCount)) )
662             {
663                 isCurrentIndicatorCentered  = true;
664             }
665             else
666             {
667                 isCurrentIndicatorCentered = false;
668             }
669         }
670
671         private void UpdateContainer()
672         {
673             if (circularPaginationStyle == null)
674             {
675                 return;
676             }
677             if (indicatorList.Count > 0)
678             {
679                 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
680             }
681             else
682             {
683                 container.SizeWidth = 0;
684             }
685             container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
686         }
687
688         private void UpdateVisual()
689         {
690             if (null == circularPaginationStyle.IndicatorSize) return;
691             if (null == circularPaginationStyle.IndicatorImageURL) return;
692             if (indicatorCount <= 0) return;
693
694             for (int i = 0; i < indicatorList.Count; i++)
695             {
696                 ImageVisual indicator = indicatorList[i];
697                 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
698
699                 CheckCenterIndicator(i);
700
701                 if (i == selectedIndex)
702                 {
703                     // If the center image is set before, then should update the center visual separately.
704                     if (isCurrentIndicatorCentered)
705                     {
706                         indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
707                     }
708                     else
709                     {
710                         indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
711                     }
712                     indicator.Opacity = 1.0f;
713                 }
714                 else
715                 {
716                     // If the center image is set before, then should update the center visual separately.
717                     if (isCurrentIndicatorCentered)
718                     {
719                         indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
720                     }
721                     else
722                     {
723                         indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
724                     }
725                     indicator.Opacity = 0.5f;
726                 }
727
728                 if (isOddNumber)
729                 {
730                     if (isSymmetrical)
731                     {
732                         indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
733                     }
734                     else
735                     {
736                         indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
737                     }
738
739                 }
740                 else
741                 {
742                     indicator.Position = evenArray[middleIndex - (indicatorCount / 2) + i];
743                 }
744             }
745         }
746
747         private void UpdateAsymmetry()
748         {
749             if (null == circularPaginationStyle.IndicatorSize) return;
750             if (null == circularPaginationStyle.IndicatorImageURL) return;
751
752             int listCount = indicatorList.Count;
753
754             for (int i = 0; i < listCount; i++)
755             {
756                 container.RemoveVisual("Indicator" + i);
757             }
758             container.RemoveAll();
759             indicatorList.Clear();
760
761             for (int i = 0; i < listCount; i++)
762             {
763                 ImageVisual newOne = new ImageVisual
764                 {
765                     Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
766                     Position = oddArray[i + (middleIndex - leftIndicatorCount)]
767                 };
768
769                 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
770                 {
771                     newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
772                 }
773                 else
774                 {
775                     newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
776                 }
777                 newOne.Opacity  = 0.5f;
778                 container.AddVisual("Indicator" + i, newOne);
779                 indicatorList.Add(newOne);
780             }
781
782             // If selectedIndex is not set yet, the default value is middle index.
783             if (selectedIndex == -1)
784             {
785                 selectedIndex = leftIndicatorCount;
786             }
787
788             if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
789             {
790                 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
791                 indicatorList[selectedIndex].Opacity = 1.0f;
792             }
793             else
794             {
795                 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
796                 indicatorList[selectedIndex].Opacity = 1.0f;
797             }
798         }
799     }
800 }