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(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)
193 CreateIndicator(middleIndex);
196 isSymmetrical = value;
204 /// Gets or sets the number of the pages/indicators.
206 /// This value is for symmetrical indicators.
208 /// <since_tizen> 8 </since_tizen>
209 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
210 [EditorBrowsable(EditorBrowsableState.Never)]
211 public int IndicatorCount
215 return indicatorCount;
219 if (indicatorCount == value || indicatorCount < 0)
223 if (value % 2 == 1) // Odd number
232 if (indicatorCount < value)
237 arrayIndex = (19 - value) / 2;
241 arrayIndex = (18 - value) / 2;
244 for (int i = indicatorCount; i <= value; i++)
246 CreateIndicator( arrayIndex );
250 // If selectedIndex is not set yet, the default value is middle index.
251 if (selectedIndex == -1)
253 selectedIndex = value / 2;
254 SelectIn(indicatorList[selectedIndex]);
259 for (int i = value; i < indicatorCount; i++)
261 ImageVisual indicator = indicatorList[i];
262 container.RemoveVisual("Indicator" + i);
264 indicatorList.RemoveRange(value, indicatorCount - value);
266 if (selectedIndex >= value)
269 SelectIn(indicatorList[selectedIndex]);
272 indicatorCount = value;
279 /// Gets or sets the number of the left pages/indicators.
281 /// This value can be set when IsSymmetrical API is false.
283 /// <since_tizen> 8 </since_tizen>
284 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
285 [EditorBrowsable(EditorBrowsableState.Never)]
286 public int LeftIndicatorCount
290 return leftIndicatorCount;
294 if (isSymmetrical == true)
296 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
297 isSymmetrical = false;
300 if (leftIndicatorCount == value || leftIndicatorCount < 0 || leftIndicatorCount > 9)
307 if (leftIndicatorCount < value)
309 for (int i = (middleIndex - value); i < (middleIndex - leftIndicatorCount); i++)
311 CreateIndicator( i );
316 for (int i = 0; i < (leftIndicatorCount - value); i++)
318 ImageVisual indicator = indicatorList[i];
319 container.RemoveVisual("Indicator" + i);
321 indicatorList.RemoveRange(0, (leftIndicatorCount - value)); // LeftIndicator starts from index 0.
323 if (selectedIndex >= ( value + rightIndicatorCount ))
326 SelectIn(indicatorList[selectedIndex]);
328 else if (selectedIndex == 0)
331 SelectIn(indicatorList[selectedIndex]);
334 leftIndicatorCount = value;
335 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
337 // When RightIndicatorCount is set before, then selectedIndex should be updated using the current LeftIndicatorCount.
338 if (uninitializedLeftIndicator && selectedIndex == 0)
340 selectedIndex = leftIndicatorCount;
342 uninitializedLeftIndicator = false;
350 /// Gets or sets the number of the right pages/indicators.
352 /// This value can be set when IsSymmetrical API is false.
354 /// <since_tizen> 8 </since_tizen>
355 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
356 [EditorBrowsable(EditorBrowsableState.Never)]
357 public int RightIndicatorCount
361 return rightIndicatorCount;
365 if (isSymmetrical == true)
367 Log.Info("NUI", "This variable is not for symmetric pagination. \n");
368 isSymmetrical = false;
371 if (rightIndicatorCount == value || rightIndicatorCount < 0 || rightIndicatorCount > 9)
378 if (rightIndicatorCount < value)
380 for (int i = (middleIndex + rightIndicatorCount + 1); i <= (middleIndex + value); i++)
382 CreateIndicator( i );
387 for (int i = (leftIndicatorCount + value + 1); i < (leftIndicatorCount + rightIndicatorCount); i++)
389 ImageVisual indicator = indicatorList[i];
390 container.RemoveVisual("Indicator" + i);
392 indicatorList.RemoveRange((leftIndicatorCount + value), (rightIndicatorCount - value));
394 if (selectedIndex >= (leftIndicatorCount + rightIndicatorCount))
397 SelectIn(indicatorList[selectedIndex]);
400 rightIndicatorCount = value;
401 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
409 /// Gets or sets the index of the select indicator.
411 /// If no value is set, the default value is the center indicator.
413 /// <since_tizen> 8 </since_tizen>
414 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
415 [EditorBrowsable(EditorBrowsableState.Never)]
416 public int SelectedIndex
420 return selectedIndex;
424 if (selectedIndex == value)
429 // TODO : Here it needs to add virtual function for Animation.
431 if (selectedIndex >= 0)
433 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
434 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
436 CheckCenterIndicator(selectedIndex);
437 SelectOut(indicatorList[selectedIndex]);
440 selectedIndex = value;
441 if (selectedIndex >= 0)
443 if ( (isSymmetrical && selectedIndex < indicatorCount) ||
444 (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
446 CheckCenterIndicator(selectedIndex);
447 SelectIn(indicatorList[selectedIndex]);
454 /// Retrieves the position of a indicator by index.
456 /// <param name="index">Indicator index</param>
457 /// <returns>The position of a indicator by index</returns>
458 /// <since_tizen> 8 </since_tizen>
459 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
460 [EditorBrowsable(EditorBrowsableState.Never)]
461 public Position GetIndicatorPosition(int index)
463 if (index < 0 || index >= indicatorList.Count)
467 return new Position(indicatorList[index].Position.X, indicatorList[index].Position.Y);
471 /// Sets the position of a indicator by index.
473 /// <param name="index">Indicator index</param>
474 /// <param name="position">The position of a indicator by index</param>
475 /// <since_tizen> 8 </since_tizen>
476 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
477 [EditorBrowsable(EditorBrowsableState.Never)]
478 public virtual void SetIndicatorPosition(int index, Position position)
480 // Update odd / even Array and List by each converted index.
485 oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
487 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
489 oddArray[(middleIndex - leftIndicatorCount) + index] = position;
491 indicatorList[index].Position.X = position.X;
492 indicatorList[index].Position.Y = position.Y;
494 else // Only symmetry circular pagination can be even number.
496 evenArray[IndicatorCount/2 + index - 1] = position;
497 indicatorList[index].Position.X = position.X;
498 indicatorList[index].Position.Y = position.Y;
503 private void CreateSelectAnimation()
505 if (selectAnimation == null)
507 selectAnimation = new Animation(250);
512 /// You can override it to do your select out operation.
514 /// <param name="selectOutIndicator">The indicator will be selected out</param>
515 /// <since_tizen> 8 </since_tizen>
516 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
517 [EditorBrowsable(EditorBrowsableState.Never)]
518 protected virtual void SelectOut(VisualMap selectOutIndicator)
520 if (!(selectOutIndicator is ImageVisual visual)) return;
521 if (isCurrentIndicatorCentered)
523 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
527 visual.URL = circularPaginationStyle.IndicatorImageURL.Normal;
529 visual.Opacity = 0.5f;
533 /// You can override it to do your select in operation.
535 /// <param name="selectInIndicator">The indicator will be selected in</param>
536 /// <since_tizen> 8 </since_tizen>
537 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
538 [EditorBrowsable(EditorBrowsableState.Never)]
539 protected virtual void SelectIn(VisualMap selectInIndicator)
541 if (!(selectInIndicator is ImageVisual visual)) return;
542 if (isCurrentIndicatorCentered)
544 visual.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
548 visual.URL = circularPaginationStyle.IndicatorImageURL.Selected;
550 visual.Opacity = 1.0f;
554 /// you can override it to create your own default style.
556 /// <since_tizen> 8 </since_tizen>
557 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
558 [EditorBrowsable(EditorBrowsableState.Never)]
559 protected override ViewStyle CreateViewStyle()
561 return new CircularPaginationStyle();
565 /// you can override it to clean-up your own resources.
567 /// <param name="type">DisposeTypes</param>
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 void Dispose(DisposeTypes type)
578 if (type == DisposeTypes.Explicit)
580 if (selectAnimation != null)
582 if (selectAnimation.State == Animation.States.Playing)
584 selectAnimation.Stop();
586 selectAnimation.Dispose();
587 selectAnimation = null;
590 container.RemoveAll();
591 indicatorList.Clear();
593 this.Remove(container);
601 private void Initialize()
603 circularPaginationStyle = Style as CircularPaginationStyle;
604 if (circularPaginationStyle == null)
606 throw new Exception("CircularPagination style is null.");
609 container = new VisualView()
612 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
613 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
614 PositionUsesPivotPoint = true,
619 // The parameter, index, is for the index of either oddArray or evenArray.
620 private void CreateIndicator(int index)
622 if (circularPaginationStyle == null)
627 ImageVisual indicator = new ImageVisual
629 URL = circularPaginationStyle.IndicatorImageURL.Normal,
630 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
636 indicator.Position = oddArray[index];
640 indicator.Position = evenArray[index];
643 container.AddVisual("Indicator" + indicatorList.Count, indicator);
644 indicatorList.Add(indicator);
647 private void CheckCenterIndicator(int index)
649 if (isCenterImageSet &&
650 (isSymmetrical && (index == indicatorCount / 2)) ||
651 (!isSymmetrical && (index == leftIndicatorCount)) )
653 isCurrentIndicatorCentered = true;
657 isCurrentIndicatorCentered = false;
661 private void UpdateContainer()
663 if (circularPaginationStyle == null)
667 if (indicatorList.Count > 0)
669 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
673 container.SizeWidth = 0;
675 container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
678 private void UpdateVisual()
680 if (null == circularPaginationStyle.IndicatorSize) return;
681 if (null == circularPaginationStyle.IndicatorImageURL) return;
682 if (indicatorCount < 0) return;
684 for (int i = 0; i < indicatorList.Count; i++)
686 ImageVisual indicator = indicatorList[i];
687 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
689 CheckCenterIndicator(i);
691 if (i == selectedIndex)
693 // If the center image is set before, then should update the center visual separately.
694 if (isCurrentIndicatorCentered)
696 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
700 indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
702 indicator.Opacity = 1.0f;
706 // If the center image is set before, then should update the center visual separately.
707 if (isCurrentIndicatorCentered)
709 indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
713 indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
715 indicator.Opacity = 0.5f;
722 indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
726 indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
732 indicator.Position = evenArray[IndicatorCount/2 + i - 1];
737 private void UpdateAsymmetry()
739 if (null == circularPaginationStyle.IndicatorSize) return;
740 if (null == circularPaginationStyle.IndicatorImageURL) return;
742 int listCount = indicatorList.Count;
744 for (int i = 0; i < listCount; i++)
746 container.RemoveVisual("Indicator" + i);
748 container.RemoveAll();
749 indicatorList.Clear();
751 for (int i = 0; i < listCount; i++)
753 ImageVisual newOne = new ImageVisual
755 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
756 Position = oddArray[i + (middleIndex - leftIndicatorCount)]
759 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
761 newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
765 newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
767 newOne.Opacity = 0.5f;
768 container.AddVisual("Indicator" + i, newOne);
769 indicatorList.Add(newOne);
772 // If selectedIndex is not set yet, the default value is middle index.
773 if (selectedIndex == -1)
775 selectedIndex = leftIndicatorCount;
778 if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
780 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
781 indicatorList[selectedIndex].Opacity = 1.0f;
785 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
786 indicatorList[selectedIndex].Opacity = 1.0f;