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 /// <exception cref="ArgumentNullException">This exception can occur by the position is null.</exception>
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 if (position == null)
493 throw new ArgumentNullException(nameof(position));
495 // Update odd / even Array and List by each converted index.
500 oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
502 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
504 oddArray[(middleIndex - leftIndicatorCount) + index] = position;
506 indicatorList[index].Position = new Vector2(position.X, position.Y);
508 else // Only symmetry circular pagination can be even number.
510 evenArray[(middleIndex - (indicatorCount / 2) + index)] = position;
511 indicatorList[index].Position = new Vector2(position.X, position.Y);
516 private void CreateSelectAnimation()
518 if (selectAnimation == null)
520 selectAnimation = new Animation(250);
525 /// You can override it to do your select out operation.
527 /// <param name="selectOutIndicator">The indicator will be selected out</param>
528 /// <since_tizen> 8 </since_tizen>
529 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
530 [EditorBrowsable(EditorBrowsableState.Never)]
531 protected virtual void SelectOut(VisualMap selectOutIndicator)
533 if (!(selectOutIndicator is ImageVisual visual)) return;
534 if (isCurrentIndicatorCentered)
536 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Normal;
540 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Normal;
542 visual.Opacity = 0.5f;
546 /// You can override it to do your select in operation.
548 /// <param name="selectInIndicator">The indicator will be selected in</param>
549 /// <since_tizen> 8 </since_tizen>
550 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
551 [EditorBrowsable(EditorBrowsableState.Never)]
552 protected virtual void SelectIn(VisualMap selectInIndicator)
554 if (!(selectInIndicator is ImageVisual visual)) return;
555 if (isCurrentIndicatorCentered)
557 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Selected;
561 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Selected;
563 visual.Opacity = 1.0f;
567 /// you can override it to create your own default style.
569 /// <since_tizen> 8 </since_tizen>
570 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
571 [EditorBrowsable(EditorBrowsableState.Never)]
572 protected override ViewStyle CreateViewStyle()
574 return new CircularPaginationStyle();
578 /// you can override it to clean-up your own resources.
580 /// <param name="type">DisposeTypes</param>
581 /// <since_tizen> 8 </since_tizen>
582 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
583 [EditorBrowsable(EditorBrowsableState.Never)]
584 protected override void Dispose(DisposeTypes type)
591 if (type == DisposeTypes.Explicit)
593 if (selectAnimation != null)
595 if (selectAnimation.State == Animation.States.Playing)
597 selectAnimation.Stop();
599 selectAnimation.Dispose();
600 selectAnimation = null;
603 container.RemoveAll();
604 indicatorList.Clear();
606 this.Remove(container);
614 private void Initialize()
616 circularPaginationStyle = Style as CircularPaginationStyle;
617 if (circularPaginationStyle == null)
619 throw new Exception("CircularPagination style is null.");
622 container = new VisualView()
625 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
626 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
627 PositionUsesPivotPoint = true,
632 // The parameter, index, is for the index of either oddArray or evenArray.
633 private void CreateIndicator(int index)
635 if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null)
640 ImageVisual indicator = new ImageVisual
642 URL = circularPaginationStyle.IndicatorImageURL?.Normal,
643 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
649 indicator.Position = oddArray[index];
653 indicator.Position = evenArray[index];
656 container.AddVisual("Indicator" + indicatorList.Count, indicator);
657 indicatorList.Add(indicator);
660 private void CheckCenterIndicator(int index)
662 if (isCenterImageSet &&
663 (isSymmetrical && (index == indicatorCount / 2)) ||
664 (!isSymmetrical && (index == leftIndicatorCount)) )
666 isCurrentIndicatorCentered = true;
670 isCurrentIndicatorCentered = false;
674 private void UpdateContainer()
676 if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null || container == null)
680 if (indicatorList.Count > 0)
682 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
686 container.SizeWidth = 0;
688 container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
691 private void UpdateVisual()
693 if (null == circularPaginationStyle.IndicatorSize) return;
694 if (null == circularPaginationStyle.IndicatorImageURL) return;
695 if (indicatorCount <= 0) return;
697 for (int i = 0; i < indicatorList.Count; i++)
699 ImageVisual indicator = indicatorList[i];
700 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
702 CheckCenterIndicator(i);
704 if (i == selectedIndex)
706 // If the center image is set before, then should update the center visual separately.
707 if (isCurrentIndicatorCentered)
709 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
713 indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
715 indicator.Opacity = 1.0f;
719 // If the center image is set before, then should update the center visual separately.
720 if (isCurrentIndicatorCentered)
722 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
726 indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
728 indicator.Opacity = 0.5f;
735 indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
739 indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
745 indicator.Position = evenArray[middleIndex - (indicatorCount / 2) + i];
750 private void UpdateAsymmetry()
752 if (null == circularPaginationStyle.IndicatorSize) return;
753 if (null == circularPaginationStyle.IndicatorImageURL) return;
755 int listCount = indicatorList.Count;
757 for (int i = 0; i < listCount; i++)
759 container.RemoveVisual("Indicator" + i);
761 container.RemoveAll();
762 indicatorList.Clear();
764 for (int i = 0; i < listCount; i++)
766 ImageVisual newOne = new ImageVisual
768 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
769 Position = oddArray[i + (middleIndex - leftIndicatorCount)]
772 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
774 newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
778 newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
780 newOne.Opacity = 0.5f;
781 container.AddVisual("Indicator" + i, newOne);
782 indicatorList.Add(newOne);
785 // If selectedIndex is not set yet, the default value is middle index.
786 if (selectedIndex == -1)
788 selectedIndex = leftIndicatorCount;
791 if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
793 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
794 indicatorList[selectedIndex].Opacity = 1.0f;
798 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
799 indicatorList[selectedIndex].Opacity = 1.0f;