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 // Update odd / even Array and List by each converted index.
495 oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
497 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
499 oddArray[(middleIndex - leftIndicatorCount) + index] = position;
501 indicatorList[index].Position.X = position.X;
502 indicatorList[index].Position.Y = position.Y;
504 else // Only symmetry circular pagination can be even number.
506 evenArray[(middleIndex - (indicatorCount / 2) + index)] = position;
507 indicatorList[index].Position.X = position.X;
508 indicatorList[index].Position.Y = position.Y;
513 private void CreateSelectAnimation()
515 if (selectAnimation == null)
517 selectAnimation = new Animation(250);
522 /// You can override it to do your select out operation.
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)
530 if (!(selectOutIndicator is ImageVisual visual)) return;
531 if (isCurrentIndicatorCentered)
533 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
537 visual.URL = circularPaginationStyle.IndicatorImageURL.Normal;
539 visual.Opacity = 0.5f;
543 /// You can override it to do your select in operation.
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)
551 if (!(selectInIndicator is ImageVisual visual)) return;
552 if (isCurrentIndicatorCentered)
554 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
558 visual.URL = circularPaginationStyle.IndicatorImageURL.Selected;
560 visual.Opacity = 1.0f;
564 /// you can override it to create your own default style.
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()
571 return new CircularPaginationStyle();
575 /// you can override it to clean-up your own resources.
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)
588 if (type == DisposeTypes.Explicit)
590 if (selectAnimation != null)
592 if (selectAnimation.State == Animation.States.Playing)
594 selectAnimation.Stop();
596 selectAnimation.Dispose();
597 selectAnimation = null;
600 container.RemoveAll();
601 indicatorList.Clear();
603 this.Remove(container);
611 private void Initialize()
613 circularPaginationStyle = Style as CircularPaginationStyle;
614 if (circularPaginationStyle == null)
616 throw new Exception("CircularPagination style is null.");
619 container = new VisualView()
622 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
623 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
624 PositionUsesPivotPoint = true,
629 // The parameter, index, is for the index of either oddArray or evenArray.
630 private void CreateIndicator(int index)
632 if (circularPaginationStyle == null)
637 ImageVisual indicator = new ImageVisual
639 URL = circularPaginationStyle.IndicatorImageURL.Normal,
640 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
646 indicator.Position = oddArray[index];
650 indicator.Position = evenArray[index];
653 container.AddVisual("Indicator" + indicatorList.Count, indicator);
654 indicatorList.Add(indicator);
657 private void CheckCenterIndicator(int index)
659 if (isCenterImageSet &&
660 (isSymmetrical && (index == indicatorCount / 2)) ||
661 (!isSymmetrical && (index == leftIndicatorCount)) )
663 isCurrentIndicatorCentered = true;
667 isCurrentIndicatorCentered = false;
671 private void UpdateContainer()
673 if (circularPaginationStyle == null)
677 if (indicatorList.Count > 0)
679 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
683 container.SizeWidth = 0;
685 container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
688 private void UpdateVisual()
690 if (null == circularPaginationStyle.IndicatorSize) return;
691 if (null == circularPaginationStyle.IndicatorImageURL) return;
692 if (indicatorCount <= 0) return;
694 for (int i = 0; i < indicatorList.Count; i++)
696 ImageVisual indicator = indicatorList[i];
697 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
699 CheckCenterIndicator(i);
701 if (i == selectedIndex)
703 // If the center image is set before, then should update the center visual separately.
704 if (isCurrentIndicatorCentered)
706 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
710 indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
712 indicator.Opacity = 1.0f;
716 // If the center image is set before, then should update the center visual separately.
717 if (isCurrentIndicatorCentered)
719 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
723 indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
725 indicator.Opacity = 0.5f;
732 indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
736 indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
742 indicator.Position = evenArray[middleIndex - (indicatorCount / 2) + i];
747 private void UpdateAsymmetry()
749 if (null == circularPaginationStyle.IndicatorSize) return;
750 if (null == circularPaginationStyle.IndicatorImageURL) return;
752 int listCount = indicatorList.Count;
754 for (int i = 0; i < listCount; i++)
756 container.RemoveVisual("Indicator" + i);
758 container.RemoveAll();
759 indicatorList.Clear();
761 for (int i = 0; i < listCount; i++)
763 ImageVisual newOne = new ImageVisual
765 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
766 Position = oddArray[i + (middleIndex - leftIndicatorCount)]
769 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
771 newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
775 newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
777 newOne.Opacity = 0.5f;
778 container.AddVisual("Indicator" + i, newOne);
779 indicatorList.Add(newOne);
782 // If selectedIndex is not set yet, the default value is middle index.
783 if (selectedIndex == -1)
785 selectedIndex = leftIndicatorCount;
788 if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
790 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
791 indicatorList[selectedIndex].Opacity = 1.0f;
795 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
796 indicatorList[selectedIndex].Opacity = 1.0f;