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 = 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.
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(CircularPaginationStyle style) : base(style)
90 /// Gets or sets the size of the indicator.
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
99 return circularPaginationStyle?.IndicatorSize;
103 if (value == null || circularPaginationStyle == null)
107 circularPaginationStyle.IndicatorSize = value;
113 /// Gets or sets the background resource of indicator.
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
122 return circularPaginationStyle?.IndicatorImageURL;
126 if (value == null || circularPaginationStyle == null)
130 circularPaginationStyle.IndicatorImageURL = value;
136 /// Gets or sets the background resource of the center indicator.
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
145 if (isCenterImageSet)
147 return circularPaginationStyle?.CenterIndicatorImageURL;
151 Log.Info("NUI", "CenterIndicatorImageURL is not set yet. \n");
158 if (value == null || circularPaginationStyle == null)
162 circularPaginationStyle.CenterIndicatorImageURL = value;
163 isCenterImageSet = true;
169 /// Checks whether the indicators are symmetrical or not.
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.
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
183 return isSymmetrical;
187 if (isSymmetrical == value)
194 CreateIndicator(middleIndex);
197 isSymmetrical = value;
205 /// Gets or sets the number of the pages/indicators.
207 /// This value is for symmetrical indicators.
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
216 return indicatorCount;
220 if (indicatorCount == value || indicatorCount < 0 || value <= 0)
224 if (isSymmetrical == false)
226 Log.Info("NUI", "This property is not for asymmetric pagination. Change to symmetrical pagination.\n");
227 isSymmetrical = true;
230 if (value % 2 == 1) // Odd number
239 if (indicatorCount < value)
244 arrayIndex = (19 - value) / 2;
248 arrayIndex = (18 - value) / 2;
250 if (arrayIndex < 0) return;
252 for (int i = (indicatorCount + 1); i <= value; i++)
254 CreateIndicator( arrayIndex );
258 // If selectedIndex is not set yet, the default value is middle index.
259 if (selectedIndex == -1)
261 selectedIndex = value / 2;
262 SelectIn(indicatorList[selectedIndex]);
267 for (int i = value; i < indicatorCount; i++)
269 ImageVisual indicator = indicatorList[i];
270 container.RemoveVisual("Indicator" + i);
272 indicatorList.RemoveRange(value, indicatorCount - value);
274 if (selectedIndex >= value)
276 selectedIndex = value - 1;
277 SelectIn(indicatorList[selectedIndex]);
280 indicatorCount = value;
288 /// Gets or sets the number of the left pages/indicators.
290 /// This value can be set when IsSymmetrical API is false.
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
299 return leftIndicatorCount;
303 if (isSymmetrical == true)
305 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
306 isSymmetrical = false;
309 if (leftIndicatorCount == value || leftIndicatorCount < 0 || value > 9 || value < 0)
316 if (leftIndicatorCount < value)
318 for (int i = (middleIndex - value); i < (middleIndex - leftIndicatorCount); i++)
320 CreateIndicator( i );
326 for (int i = 0; i < (leftIndicatorCount - value); i++)
328 ImageVisual indicator = indicatorList[i];
329 container.RemoveVisual("Indicator" + i);
331 indicatorList.RemoveRange(0, (leftIndicatorCount - value)); // LeftIndicator starts from index 0.
333 if (selectedIndex == 0)
336 SelectIn(indicatorList[selectedIndex]);
341 SelectIn(indicatorList[selectedIndex]);
344 leftIndicatorCount = value;
345 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
347 // When RightIndicatorCount is set before, then selectedIndex should be updated using the current LeftIndicatorCount.
348 if (uninitializedLeftIndicator && selectedIndex == 0)
350 selectedIndex = leftIndicatorCount;
352 uninitializedLeftIndicator = false;
360 /// Gets or sets the number of the right pages/indicators.
362 /// This value can be set when IsSymmetrical API is false.
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
371 return rightIndicatorCount;
375 if (isSymmetrical == true)
377 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
378 isSymmetrical = false;
381 if (rightIndicatorCount == value || rightIndicatorCount < 0 || value > 9 || value < 0)
388 if (rightIndicatorCount < value)
390 for (int i = (middleIndex + rightIndicatorCount + 1); i <= (middleIndex + value); i++)
392 CreateIndicator( i );
397 for (int i = (leftIndicatorCount + value + 1); i < (leftIndicatorCount + rightIndicatorCount); i++)
399 ImageVisual indicator = indicatorList[i];
400 container.RemoveVisual("Indicator" + i);
402 indicatorList.RemoveRange((leftIndicatorCount + value), (rightIndicatorCount - value));
404 if (selectedIndex >= (leftIndicatorCount + rightIndicatorCount))
407 SelectIn(indicatorList[selectedIndex]);
410 rightIndicatorCount = value;
411 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
419 /// Gets or sets the index of the select indicator.
421 /// If no value is set, the default value is the center indicator.
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
430 return selectedIndex;
434 if (selectedIndex == value || value < 0 || value >= indicatorCount)
439 // TODO : Here it needs to add virtual function for Animation.
441 if (selectedIndex >= 0)
443 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
444 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
446 CheckCenterIndicator(selectedIndex);
447 SelectOut(indicatorList[selectedIndex]);
450 selectedIndex = value;
451 if (selectedIndex >= 0)
453 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
454 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
456 CheckCenterIndicator(selectedIndex);
457 SelectIn(indicatorList[selectedIndex]);
464 /// Retrieves the position of a indicator by index.
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)
473 if (index < 0 || index >= indicatorList.Count)
477 return new Position(indicatorList[index].Position.X, indicatorList[index].Position.Y);
481 /// Sets the position of a indicator by index.
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)
490 if (position == null)
492 throw new ArgumentNullException(nameof(position));
494 // Update odd / even Array and List by each converted index.
499 oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
501 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
503 oddArray[(middleIndex - leftIndicatorCount) + index] = position;
505 indicatorList[index].Position = new Vector2(position.X, position.Y);
507 else // Only symmetry circular pagination can be even number.
509 evenArray[(middleIndex - (indicatorCount / 2) + index)] = position;
510 indicatorList[index].Position = new Vector2(position.X, position.Y);
515 private void CreateSelectAnimation()
517 if (selectAnimation == null)
519 selectAnimation = new Animation(250);
524 /// You can override it to do your select out operation.
526 /// <param name="selectOutIndicator">The indicator will be selected out</param>
527 /// <since_tizen> 8 </since_tizen>
528 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
529 [EditorBrowsable(EditorBrowsableState.Never)]
530 protected virtual void SelectOut(VisualMap selectOutIndicator)
532 if (!(selectOutIndicator is ImageVisual visual)) return;
533 if (isCurrentIndicatorCentered)
535 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Normal;
539 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Normal;
541 visual.Opacity = 0.5f;
545 /// You can override it to do your select in operation.
547 /// <param name="selectInIndicator">The indicator will be selected in</param>
548 /// <since_tizen> 8 </since_tizen>
549 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
550 [EditorBrowsable(EditorBrowsableState.Never)]
551 protected virtual void SelectIn(VisualMap selectInIndicator)
553 if (!(selectInIndicator is ImageVisual visual)) return;
554 if (isCurrentIndicatorCentered)
556 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Selected;
560 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Selected;
562 visual.Opacity = 1.0f;
566 /// you can override it to create your own default style.
568 /// <since_tizen> 8 </since_tizen>
569 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
570 [EditorBrowsable(EditorBrowsableState.Never)]
571 protected override ViewStyle CreateViewStyle()
573 return new CircularPaginationStyle();
577 /// you can override it to clean-up your own resources.
579 /// <param name="type">DisposeTypes</param>
580 /// <since_tizen> 8 </since_tizen>
581 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
582 [EditorBrowsable(EditorBrowsableState.Never)]
583 protected override void Dispose(DisposeTypes type)
590 if (type == DisposeTypes.Explicit)
592 if (selectAnimation != null)
594 if (selectAnimation.State == Animation.States.Playing)
596 selectAnimation.Stop();
598 selectAnimation.Dispose();
599 selectAnimation = null;
602 container.RemoveAll();
603 indicatorList.Clear();
605 this.Remove(container);
613 private void Initialize()
615 circularPaginationStyle = Style as CircularPaginationStyle;
616 if (circularPaginationStyle == null)
618 throw new Exception("CircularPagination style is null.");
621 container = new VisualView()
624 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
625 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
626 PositionUsesPivotPoint = true,
631 // The parameter, index, is for the index of either oddArray or evenArray.
632 private void CreateIndicator(int index)
634 if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null)
639 ImageVisual indicator = new ImageVisual
641 URL = circularPaginationStyle.IndicatorImageURL?.Normal,
642 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
648 indicator.Position = oddArray[index];
652 indicator.Position = evenArray[index];
655 container.AddVisual("Indicator" + indicatorList.Count, indicator);
656 indicatorList.Add(indicator);
659 private void CheckCenterIndicator(int index)
661 if (isCenterImageSet &&
662 (isSymmetrical && (index == indicatorCount / 2)) ||
663 (!isSymmetrical && (index == leftIndicatorCount)) )
665 isCurrentIndicatorCentered = true;
669 isCurrentIndicatorCentered = false;
673 private void UpdateContainer()
675 if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null || container == null)
679 if (indicatorList.Count > 0)
681 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
685 container.SizeWidth = 0;
687 container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
690 private void UpdateVisual()
692 if (null == circularPaginationStyle.IndicatorSize) return;
693 if (null == circularPaginationStyle.IndicatorImageURL) return;
694 if (indicatorCount <= 0) return;
696 for (int i = 0; i < indicatorList.Count; i++)
698 ImageVisual indicator = indicatorList[i];
699 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
701 CheckCenterIndicator(i);
703 if (i == selectedIndex)
705 // If the center image is set before, then should update the center visual separately.
706 if (isCurrentIndicatorCentered)
708 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
712 indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
714 indicator.Opacity = 1.0f;
718 // If the center image is set before, then should update the center visual separately.
719 if (isCurrentIndicatorCentered)
721 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
725 indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
727 indicator.Opacity = 0.5f;
734 indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
738 indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
744 indicator.Position = evenArray[middleIndex - (indicatorCount / 2) + i];
749 private void UpdateAsymmetry()
751 if (null == circularPaginationStyle.IndicatorSize) return;
752 if (null == circularPaginationStyle.IndicatorImageURL) return;
754 int listCount = indicatorList.Count;
756 for (int i = 0; i < listCount; i++)
758 container.RemoveVisual("Indicator" + i);
760 container.RemoveAll();
761 indicatorList.Clear();
763 for (int i = 0; i < listCount; i++)
765 ImageVisual newOne = new ImageVisual
767 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
768 Position = oddArray[i + (middleIndex - leftIndicatorCount)]
771 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
773 newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
777 newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
779 newOne.Opacity = 0.5f;
780 container.AddVisual("Indicator" + i, newOne);
781 indicatorList.Add(newOne);
784 // If selectedIndex is not set yet, the default value is middle index.
785 if (selectedIndex == -1)
787 selectedIndex = leftIndicatorCount;
790 if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
792 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
793 indicatorList[selectedIndex].Opacity = 1.0f;
797 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
798 indicatorList[selectedIndex].Opacity = 1.0f;