2 * Copyright(c) 2020 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.
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Components;
23 namespace Tizen.NUI.Wearable
26 /// CircularPagination shows the number of pages available and the currently active page.
27 /// Especially, CircularPagination provides indicators specific to wearable device.
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
34 private CircularPaginationStyle circularPaginationStyle;
36 private VisualView container;
38 private List<ImageVisual> indicatorList = new List<ImageVisual>();
40 private bool isSymmetrical = true;
41 private int middleIndex = 9;
42 private int indicatorCount = 1;
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.
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) };
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) };
65 static CircularPagination() { }
68 /// Creates a new instance of a CircularPagination.
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()
79 /// Creates a new instance of a CircularPagination using style.
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(string style) : base(style)
90 /// Creates a new instance of a CircularPagination using style.
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 CircularPagination(CircularPaginationStyle style) : base(style)
101 /// Gets or sets the size of the indicator.
103 /// <since_tizen> 8 </since_tizen>
104 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
105 [EditorBrowsable(EditorBrowsableState.Never)]
106 public Size IndicatorSize
110 return circularPaginationStyle?.IndicatorSize;
114 if (value == null || circularPaginationStyle == null)
118 circularPaginationStyle.IndicatorSize = value;
124 /// Gets or sets the background resource of indicator.
126 /// <since_tizen> 8 </since_tizen>
127 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
128 [EditorBrowsable(EditorBrowsableState.Never)]
129 public Selector<string> IndicatorImageURL
133 return circularPaginationStyle?.IndicatorImageURL;
137 if (value == null || circularPaginationStyle == null)
141 circularPaginationStyle.IndicatorImageURL = value;
147 /// Gets or sets the background resource of the center indicator.
149 /// <since_tizen> 8 </since_tizen>
150 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
151 [EditorBrowsable(EditorBrowsableState.Never)]
152 public Selector<string> CenterIndicatorImageURL
156 if (isCenterImageSet)
158 return circularPaginationStyle?.CenterIndicatorImageURL;
162 Log.Info("NUI", "CenterIndicatorImageURL is not set yet. \n");
169 if (value == null || circularPaginationStyle == null)
173 circularPaginationStyle.CenterIndicatorImageURL = value;
174 isCenterImageSet = true;
180 /// Checks whether the indicators are symmetrical or not.
182 /// The default value is true.
183 /// If the value is true, the user just can set IndicatorCount.
184 /// If false, the user should set both the number of Left Indicators and the number of Right Indicators.
185 /// Please refer to LeftIndicatorCount and RightIndicatorCount.
187 /// <since_tizen> 8 </since_tizen>
188 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
189 [EditorBrowsable(EditorBrowsableState.Never)]
190 public bool IsSymmetrical
194 return isSymmetrical;
198 if (isSymmetrical == value)
204 CreateIndicator(middleIndex);
207 isSymmetrical = value;
215 /// Gets or sets the number of the pages/indicators.
217 /// This value is for symmetrical indicators.
219 /// <since_tizen> 8 </since_tizen>
220 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
221 [EditorBrowsable(EditorBrowsableState.Never)]
222 public int IndicatorCount
226 return indicatorCount;
230 if (indicatorCount == value || indicatorCount < 0)
234 if (value % 2 == 1) // Odd number
243 if (indicatorCount < value)
248 arrayIndex = (19 - value) / 2;
252 arrayIndex = (18 - value) / 2;
255 for (int i = indicatorCount; i <= value; i++)
257 CreateIndicator( arrayIndex );
261 // If selectedIndex is not set yet, the default value is middle index.
262 if (selectedIndex == -1)
264 selectedIndex = value / 2;
265 SelectIn(indicatorList[selectedIndex]);
270 for (int i = value; i < indicatorCount; i++)
272 ImageVisual indicator = indicatorList[i];
273 container.RemoveVisual("Indicator" + i);
275 indicatorList.RemoveRange(value, indicatorCount - value);
277 if (selectedIndex >= value)
280 SelectIn(indicatorList[selectedIndex]);
283 indicatorCount = value;
290 /// Gets or sets the number of the left pages/indicators.
292 /// This value can be set when IsSymmetrical API is false.
294 /// <since_tizen> 8 </since_tizen>
295 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
296 [EditorBrowsable(EditorBrowsableState.Never)]
297 public int LeftIndicatorCount
301 return leftIndicatorCount;
305 if (isSymmetrical == true)
307 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
308 isSymmetrical = false;
311 if (leftIndicatorCount == value || leftIndicatorCount < 0 || leftIndicatorCount > 9)
318 if (leftIndicatorCount < value)
320 for (int i = (middleIndex - value); i < (middleIndex - leftIndicatorCount); i++)
322 CreateIndicator( i );
327 for (int i = 0; i < (leftIndicatorCount - value); i++)
329 ImageVisual indicator = indicatorList[i];
330 container.RemoveVisual("Indicator" + i);
332 indicatorList.RemoveRange(0, (leftIndicatorCount - value)); // LeftIndicator starts from index 0.
334 if (selectedIndex >= ( value + rightIndicatorCount ))
337 SelectIn(indicatorList[selectedIndex]);
339 else if (selectedIndex == 0)
342 SelectIn(indicatorList[selectedIndex]);
345 leftIndicatorCount = value;
346 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
348 // When RightIndicatorCount is set before, then selectedIndex should be updated using the current LeftIndicatorCount.
349 if (uninitializedLeftIndicator && selectedIndex == 0)
351 selectedIndex = leftIndicatorCount;
353 uninitializedLeftIndicator = false;
361 /// Gets or sets the number of the right pages/indicators.
363 /// This value can be set when IsSymmetrical API is false.
365 /// <since_tizen> 8 </since_tizen>
366 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
367 [EditorBrowsable(EditorBrowsableState.Never)]
368 public int RightIndicatorCount
372 return rightIndicatorCount;
376 if (isSymmetrical == true)
378 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
379 isSymmetrical = false;
382 if (rightIndicatorCount == value || rightIndicatorCount < 0 || rightIndicatorCount > 9)
389 if (rightIndicatorCount < value)
391 for (int i = (middleIndex + rightIndicatorCount + 1); i <= (middleIndex + value); i++)
393 CreateIndicator( i );
398 for (int i = (leftIndicatorCount + value + 1); i < (leftIndicatorCount + rightIndicatorCount); i++)
400 ImageVisual indicator = indicatorList[i];
401 container.RemoveVisual("Indicator" + i);
403 indicatorList.RemoveRange((leftIndicatorCount + value), (rightIndicatorCount - value));
405 if (selectedIndex >= (leftIndicatorCount + rightIndicatorCount))
408 SelectIn(indicatorList[selectedIndex]);
411 rightIndicatorCount = value;
412 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
420 /// Gets or sets the index of the select indicator.
422 /// If no value is set, the default value is the center indicator.
424 /// <since_tizen> 8 </since_tizen>
425 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
426 [EditorBrowsable(EditorBrowsableState.Never)]
427 public int SelectedIndex
431 return selectedIndex;
435 if (selectedIndex == value)
440 // TODO : Here it needs to add virtual function for Animation.
442 if (selectedIndex >= 0)
444 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
445 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
447 CheckCenterIndicator(selectedIndex);
448 SelectOut(indicatorList[selectedIndex]);
451 selectedIndex = value;
452 if (selectedIndex >= 0)
454 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
455 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
457 CheckCenterIndicator(selectedIndex);
458 SelectIn(indicatorList[selectedIndex]);
465 /// Retrieves the position of a indicator by index.
467 /// <param name="index">Indicator index</param>
468 /// <returns>The position of a indicator by index</returns>
469 /// <since_tizen> 8 </since_tizen>
470 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
471 [EditorBrowsable(EditorBrowsableState.Never)]
472 public Position GetIndicatorPosition(int index)
474 if (index < 0 || index >= indicatorList.Count)
478 return new Position(indicatorList[index].Position.X, indicatorList[index].Position.Y);
482 /// Sets the position of a indicator by index.
484 /// <param name="index">Indicator index</param>
485 /// <param name="position">The position of a indicator by index</param>
486 /// <since_tizen> 8 </since_tizen>
487 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
488 [EditorBrowsable(EditorBrowsableState.Never)]
489 public virtual void SetIndicatorPosition(int index, Position position)
491 // Update odd / even Array and List by each converted index.
496 oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
498 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
500 oddArray[(middleIndex - leftIndicatorCount) + index] = position;
502 indicatorList[index].Position.X = position.X;
503 indicatorList[index].Position.Y = position.Y;
505 else // Only symmetry circular pagination can be even number.
507 evenArray[IndicatorCount/2 + index - 1] = position;
508 indicatorList[index].Position.X = position.X;
509 indicatorList[index].Position.Y = position.Y;
514 private void CreateSelectAnimation()
516 if (selectAnimation == null)
518 selectAnimation = new Animation(250);
523 /// You can override it to do your select out operation.
525 /// <param name="selectOutIndicator">The indicator will be selected out</param>
526 /// <since_tizen> 8 </since_tizen>
527 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
528 [EditorBrowsable(EditorBrowsableState.Never)]
529 protected virtual void SelectOut(VisualMap selectOutIndicator)
531 if (!(selectOutIndicator is ImageVisual visual)) return;
532 if (isCurrentIndicatorCentered)
534 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
538 visual.URL = circularPaginationStyle.IndicatorImageURL.Normal;
540 visual.Opacity = 0.5f;
544 /// You can override it to do your select in operation.
546 /// <param name="selectInIndicator">The indicator will be selected in</param>
547 /// <since_tizen> 8 </since_tizen>
548 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
549 [EditorBrowsable(EditorBrowsableState.Never)]
550 protected virtual void SelectIn(VisualMap selectInIndicator)
552 if (!(selectInIndicator is ImageVisual visual)) return;
553 if (isCurrentIndicatorCentered)
555 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
559 visual.URL = circularPaginationStyle.IndicatorImageURL.Selected;
561 visual.Opacity = 1.0f;
565 /// you can override it to create your own default style.
567 /// <since_tizen> 8 </since_tizen>
568 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
569 [EditorBrowsable(EditorBrowsableState.Never)]
570 protected override ViewStyle GetViewStyle()
572 return new CircularPaginationStyle();
576 /// you can override it to clean-up your own resources.
578 /// <param name="type">DisposeTypes</param>
579 /// <since_tizen> 8 </since_tizen>
580 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
581 [EditorBrowsable(EditorBrowsableState.Never)]
582 protected override void Dispose(DisposeTypes type)
589 if (type == DisposeTypes.Explicit)
591 if (selectAnimation != null)
593 if (selectAnimation.State == Animation.States.Playing)
595 selectAnimation.Stop();
597 selectAnimation.Dispose();
598 selectAnimation = null;
601 container.RemoveAll();
602 indicatorList.Clear();
604 this.Remove(container);
612 private void Initialize()
614 circularPaginationStyle = Style as CircularPaginationStyle;
615 if (circularPaginationStyle == null)
617 throw new Exception("CircularPagination style is null.");
620 container = new VisualView()
623 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
624 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
625 PositionUsesPivotPoint = true,
630 // The parameter, index, is for the index of either oddArray or evenArray.
631 private void CreateIndicator(int index)
633 if (circularPaginationStyle == null)
638 ImageVisual indicator = new ImageVisual
640 URL = circularPaginationStyle.IndicatorImageURL.Normal,
641 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
647 indicator.Position = oddArray[index];
651 indicator.Position = evenArray[index];
654 container.AddVisual("Indicator" + indicatorList.Count, indicator);
655 indicatorList.Add(indicator);
658 private void CheckCenterIndicator(int index)
660 if (isCenterImageSet &&
661 (isSymmetrical && (index == indicatorCount / 2)) ||
662 (!isSymmetrical && (index == leftIndicatorCount)) )
664 isCurrentIndicatorCentered = true;
668 isCurrentIndicatorCentered = false;
672 private void UpdateContainer()
674 if (circularPaginationStyle == null)
678 if (indicatorList.Count > 0)
680 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
684 container.SizeWidth = 0;
686 container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
689 private void UpdateVisual()
691 if (null == circularPaginationStyle.IndicatorSize) return;
692 if (null == circularPaginationStyle.IndicatorImageURL) return;
693 if (indicatorCount < 0) return;
695 for (int i = 0; i < indicatorList.Count; i++)
697 ImageVisual indicator = indicatorList[i];
698 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
700 CheckCenterIndicator(i);
702 if (i == selectedIndex)
704 // If the center image is set before, then should update the center visual separately.
705 if (isCurrentIndicatorCentered)
707 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
711 indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
713 indicator.Opacity = 1.0f;
717 // If the center image is set before, then should update the center visual separately.
718 if (isCurrentIndicatorCentered)
720 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
724 indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
726 indicator.Opacity = 0.5f;
733 indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
737 indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
743 indicator.Position = evenArray[IndicatorCount/2 + i - 1];
748 private void UpdateAsymmetry()
750 if (null == circularPaginationStyle.IndicatorSize) return;
751 if (null == circularPaginationStyle.IndicatorImageURL) return;
753 int listCount = indicatorList.Count;
755 for (int i = 0; i < listCount; i++)
757 container.RemoveVisual("Indicator" + i);
759 container.RemoveAll();
760 indicatorList.Clear();
762 for (int i = 0; i < listCount; i++)
764 ImageVisual newOne = new ImageVisual
766 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
767 Position = oddArray[i + (middleIndex - leftIndicatorCount)]
770 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
772 newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
776 newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
778 newOne.Opacity = 0.5f;
779 container.AddVisual("Indicator" + i, newOne);
780 indicatorList.Add(newOne);
783 // If selectedIndex is not set yet, the default value is middle index.
784 if (selectedIndex == -1)
786 selectedIndex = leftIndicatorCount;
789 if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
791 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
792 indicatorList[selectedIndex].Opacity = 1.0f;
796 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
797 indicatorList[selectedIndex].Opacity = 1.0f;