2 * Copyright(c) 2021 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.
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Diagnostics;
22 using Tizen.NUI.Accessibility;
23 using Tizen.NUI.BaseComponents;
24 using Tizen.NUI.Binding;
26 namespace Tizen.NUI.Components
29 /// Pagination shows the number of pages available and the currently active page.
31 /// <since_tizen> 8 </since_tizen>
32 public partial class Pagination : Control, IAtspiValue
34 /// <summary>The IndicatorSize bindable property.</summary>
35 [EditorBrowsable(EditorBrowsableState.Never)]
36 public static readonly BindableProperty IndicatorSizeProperty = BindableProperty.Create(nameof(IndicatorSize), typeof(Size), typeof(Pagination), null, propertyChanged: (bindable, oldValue, newValue) =>
40 var pagination = (Pagination)bindable;
41 pagination.indicatorSize = new Size((Size)newValue);
42 pagination.UpdateVisual();
43 pagination.UpdateContainer();
46 defaultValueCreator: (bindable) =>
48 return ((Pagination)bindable).indicatorSize;
51 /// <summary>The IndicatorImageUrlSelector bindable property.</summary>
52 [EditorBrowsable(EditorBrowsableState.Never)]
53 public static readonly BindableProperty IndicatorImageUrlProperty = BindableProperty.Create(nameof(IndicatorImageUrl), typeof(Selector<string>), typeof(Pagination), null, propertyChanged: (bindable, oldValue, newValue) =>
55 var pagination = (Pagination)bindable;
56 pagination.indicatorImageUrl = ((Selector<string>)newValue)?.Clone();
57 pagination.UpdateVisual();
59 defaultValueCreator: (bindable) =>
61 return ((Pagination)bindable).indicatorImageUrl;
64 /// <summary>The IndicatorSpacing bindable property.</summary>
65 [EditorBrowsable(EditorBrowsableState.Never)]
66 public static readonly BindableProperty IndicatorSpacingProperty = BindableProperty.Create(nameof(IndicatorSpacing), typeof(int), typeof(Pagination), default(int), propertyChanged: (bindable, oldValue, newValue) =>
68 var pagination = (Pagination)bindable;
69 pagination.indicatorSpacing = (int)newValue;
70 pagination.UpdateVisual();
72 defaultValueCreator: (bindable) =>
74 return ((Pagination)bindable).indicatorSpacing;
77 // Depending on Tizen 7.0 Pagination UX guide
78 private const int DefaultIndicatorWidth = 64;
79 private const int DefaultIndicatorHeight = 8;
80 private const int DefaultIndicatorSpacing = 16;
81 private const string DefaultIndicatorColor = "#FAFAFA";
82 private const string DefaultSelectedIndicatorColor = "#FFA166";
84 private View container;
85 private Size indicatorSize = new Size(DefaultIndicatorWidth, DefaultIndicatorHeight);
86 private Selector<string> indicatorImageUrl;
87 private int indicatorSpacing = DefaultIndicatorSpacing;
88 private List<ImageView> indicatorList = new List<ImageView>();
90 private int indicatorCount = 0;
91 private int selectedIndex = 0;
93 private Color indicatorColor;
94 private Color selectedIndicatorColor;
95 private Selector<string> lastIndicatorImageUrl;
97 static Pagination() { }
100 /// Creates a new instance of a Pagination.
102 /// <since_tizen> 8 </since_tizen>
103 public Pagination() : base()
108 /// Creates a new instance of a Pagination using style.
110 /// <param name="style">The string to initialize the Pagination</param>
111 /// <since_tizen> 8 </since_tizen>
112 public Pagination(string style) : base(style)
117 /// Creates a new instance of a Pagination using style.
119 /// <param name="paginationStyle">The style object to initialize the Pagination</param>
120 /// <since_tizen> 8 </since_tizen>
121 public Pagination(PaginationStyle paginationStyle) : base(paginationStyle)
126 /// Return currently applied style.
129 /// Modifying contents in style may cause unexpected behaviour.
131 /// <since_tizen> 8 </since_tizen>
132 public PaginationStyle Style => (PaginationStyle)(ViewStyle as PaginationStyle)?.Clone();
135 /// Gets or sets the size of the indicator.
137 /// <since_tizen> 8 </since_tizen>
138 public Size IndicatorSize
140 get => (Size)GetValue(IndicatorSizeProperty);
141 set => SetValue(IndicatorSizeProperty, value);
145 /// Gets or sets the background resource of indicator.
147 /// <since_tizen> 8 </since_tizen>
148 public Selector<string> IndicatorImageUrl
150 get => (Selector<string>)GetValue(IndicatorImageUrlProperty);
151 set => SetValue(IndicatorImageUrlProperty, value);
155 /// This is experimental API.
156 /// Make the last indicator has exceptional image, not common image in the Pagination.
158 [EditorBrowsable(EditorBrowsableState.Never)]
159 public Selector<string> LastIndicatorImageUrl
163 return GetValue(LastIndicatorImageUrlProperty) as Selector<string>;
167 SetValue(LastIndicatorImageUrlProperty, value);
168 NotifyPropertyChanged();
171 private Selector<string> InternalLastIndicatorImageUrl
173 get => lastIndicatorImageUrl;
176 lastIndicatorImageUrl = value;
177 if (value != null && indicatorCount > 0)
179 indicatorList[LastIndicatorIndex].ResourceUrl = IsLastSelected ? value.Selected : value.Normal;
185 /// Gets or sets the space of the indicator.
187 /// <since_tizen> 8 </since_tizen>
188 public int IndicatorSpacing
190 get => (int)GetValue(IndicatorSpacingProperty);
191 set => SetValue(IndicatorSpacingProperty, value);
196 /// Gets or sets the count of the pages/indicators.
198 /// <since_tizen> 8 </since_tizen>
199 /// <exception cref="ArgumentException">Thrown when the given value is negative.</exception>
200 public int IndicatorCount
204 return (int)GetValue(IndicatorCountProperty);
208 SetValue(IndicatorCountProperty, value);
209 NotifyPropertyChanged();
212 private int InternalIndicatorCount
216 return indicatorCount;
222 throw new ArgumentException($"Setting {nameof(IndicatorCount)} to negative is not allowed.");
225 if (indicatorCount == value)
230 int prevLastIndex = -1;
232 if (indicatorCount < value)
234 prevLastIndex = LastIndicatorIndex;
235 for (int i = indicatorCount; i < value; i++)
242 for (int i = value; i < indicatorCount; i++)
244 container.Remove(indicatorList[i]);
246 indicatorList.RemoveRange(value, indicatorCount - value);
248 if (selectedIndex >= value)
250 selectedIndex = Math.Max(0, value - 1);
254 UpdateSelectedIndicator(indicatorList[selectedIndex]);
258 indicatorCount = value;
260 if (lastIndicatorImageUrl != null && indicatorImageUrl != null && indicatorCount > 0)
262 if (prevLastIndex >= 0)
264 indicatorList[prevLastIndex].ResourceUrl = prevLastIndex == selectedIndex ? indicatorImageUrl.Selected : indicatorImageUrl.Normal;
266 indicatorList[LastIndicatorIndex].ResourceUrl = IsLastSelected ? lastIndicatorImageUrl.Selected : lastIndicatorImageUrl.Normal;
273 private void OnIndicatorColorChanged(float r, float g, float b, float a)
275 IndicatorColor = new Color(r, g, b, a);
279 /// Color of the indicator.
281 /// <since_tizen> 8 </since_tizen>
282 public Color IndicatorColor
286 return GetValue(IndicatorColorProperty) as Color;
290 SetValue(IndicatorColorProperty, value);
291 NotifyPropertyChanged();
294 private Color InternalIndicatorColor
298 return new Color(OnIndicatorColorChanged, indicatorColor);
307 if (indicatorColor == null)
309 indicatorColor = new Color((Color)value);
313 if (indicatorColor == value)
318 indicatorColor = value;
321 if (indicatorCount == 0)
326 for (int i = 0; i < indicatorCount; i++)
328 if (i == selectedIndex)
333 indicatorList[i].Color = indicatorColor;
338 private void OnSelectedIndicatorColorChanged(float r, float g, float b, float a)
340 SelectedIndicatorColor = new Color(r, g, b, a);
344 /// Color of the selected indicator.
346 /// <since_tizen> 8 </since_tizen>
347 public Color SelectedIndicatorColor
351 return GetValue(SelectedIndicatorColorProperty) as Color;
355 SetValue(SelectedIndicatorColorProperty, value);
356 NotifyPropertyChanged();
359 private Color InternalSelectedIndicatorColor
363 return new Color(OnSelectedIndicatorColorChanged, selectedIndicatorColor);
372 if (selectedIndicatorColor == null)
374 selectedIndicatorColor = new Color((Color)value);
378 if (selectedIndicatorColor == value)
383 selectedIndicatorColor = value;
386 if (indicatorList.Count > selectedIndex)
388 indicatorList[selectedIndex].Color = selectedIndicatorColor;
394 /// Gets or sets the index of the select indicator.
396 /// <since_tizen> 8 </since_tizen>
397 public int SelectedIndex
401 return (int)GetValue(SelectedIndexProperty);
405 SetValue(SelectedIndexProperty, value);
406 NotifyPropertyChanged();
409 private int InternalSelectedIndex
413 return selectedIndex;
417 var refinedValue = Math.Max(0, Math.Min(value, indicatorCount - 1));
419 if (selectedIndex == refinedValue)
424 Debug.Assert(refinedValue >= 0 && refinedValue < indicatorCount);
425 Debug.Assert(selectedIndex >= 0 && selectedIndex < indicatorCount);
427 UpdateUnselectedIndicator(indicatorList[selectedIndex]);
429 selectedIndex = refinedValue;
431 UpdateSelectedIndicator(indicatorList[selectedIndex]);
433 if (Accessibility.Accessibility.IsEnabled && IsHighlighted)
435 EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value);
441 /// Retrieves the position of a indicator by index.
443 /// <param name="index">Indicator index</param>
444 /// <returns>The position of a indicator by index.</returns>
445 /// <since_tizen> 8 </since_tizen>
446 public Position GetIndicatorPosition(int index)
448 if (index < 0 || index >= indicatorList.Count)
452 return new Position(indicatorList[index].Position.X + container.PositionX, indicatorList[index].Position.Y + container.PositionY);
458 [EditorBrowsable(EditorBrowsableState.Never)]
459 double IAtspiValue.AccessibilityGetMinimum()
467 [EditorBrowsable(EditorBrowsableState.Never)]
468 double IAtspiValue.AccessibilityGetCurrent()
470 return (double)SelectedIndex;
476 [EditorBrowsable(EditorBrowsableState.Never)]
477 double IAtspiValue.AccessibilityGetMaximum()
479 return (double)IndicatorCount;
485 [EditorBrowsable(EditorBrowsableState.Never)]
486 bool IAtspiValue.AccessibilitySetCurrent(double value)
488 int integerValue = (int)value;
490 if (integerValue >= 0 && integerValue <= IndicatorCount)
492 SelectedIndex = integerValue;
500 /// Minimum increment.
502 [EditorBrowsable(EditorBrowsableState.Never)]
503 double IAtspiValue.AccessibilityGetMinimumIncrement()
509 [EditorBrowsable(EditorBrowsableState.Never)]
510 public override void OnInitialize()
513 AccessibilityRole = Role.ScrollBar;
514 AccessibilityHighlightable = true;
515 AccessibilityAttributes["style"] = "pagecontrolbyvalue";
517 container = new View()
520 ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft,
521 PivotPoint = Tizen.NUI.PivotPoint.CenterLeft,
522 PositionUsesPivotPoint = true,
523 Layout = new LinearLayout()
525 LinearOrientation = LinearLayout.Orientation.Horizontal,
526 CellPadding = new Size2D(indicatorSpacing, 0),
528 Padding = new Extents(8, 8, 0, 0),
532 //TODO: Apply color properties from PaginationStyle class.
533 indicatorColor = new Color(DefaultIndicatorColor);
534 selectedIndicatorColor = new Color(DefaultSelectedIndicatorColor);
538 /// You can override it to do your select out operation.
540 /// <param name="selectOutIndicator">The indicator will be selected out</param>
541 /// <since_tizen> 8 </since_tizen>
542 protected virtual void SelectOut(VisualMap selectOutIndicator)
544 // Currently, this method is not used in this file anymore.
545 // However, the implementation inside should remain because someone could be used by overriding it.
546 if (!(selectOutIndicator is ImageVisual visual)) return;
547 visual.URL = ((IsLastSelected && lastIndicatorImageUrl != null) ? lastIndicatorImageUrl : indicatorImageUrl)?.Normal;
549 if (indicatorColor == null)
551 visual.MixColor = new Color(DefaultIndicatorColor);
552 visual.Opacity = 1.0f;
556 visual.MixColor = indicatorColor;
557 visual.Opacity = indicatorColor.A;
562 /// You can override it to do your select in operation.
564 /// <param name="selectInIndicator">The indicator will be selected in</param>
565 /// <since_tizen> 8 </since_tizen>
566 protected virtual void SelectIn(VisualMap selectInIndicator)
568 // Currently, this method is not used in this file anymore.
569 // However, the implementation inside should remain because someone could be used by overriding it.
570 if (!(selectInIndicator is ImageVisual visual)) return;
571 visual.URL = ((IsLastSelected && lastIndicatorImageUrl != null) ? lastIndicatorImageUrl : indicatorImageUrl)?.Selected;
573 if (selectedIndicatorColor == null)
575 visual.MixColor = new Color(DefaultSelectedIndicatorColor);
576 visual.Opacity = 1.0f;
580 visual.MixColor = selectedIndicatorColor;
581 visual.Opacity = selectedIndicatorColor.A;
586 /// you can override it to create your own default style.
588 /// <returns>The default pagination style.</returns>
589 /// <since_tizen> 8 </since_tizen>
590 protected override ViewStyle CreateViewStyle()
592 return new PaginationStyle();
596 /// you can override it to clean-up your own resources.
598 /// <param name="type">DisposeTypes</param>
599 /// <since_tizen> 8 </since_tizen>
600 protected override void Dispose(DisposeTypes type)
607 if (type == DisposeTypes.Explicit)
609 for (int i = 0; i < indicatorCount; i++)
611 container.Remove(indicatorList[i]);
613 indicatorList.Clear();
615 this.Remove(container);
623 private void UpdateUnselectedIndicator(ImageView indicator)
625 if (indicator == null) return;
627 if (IsLastSelected && lastIndicatorImageUrl != null)
629 indicator.ResourceUrl = lastIndicatorImageUrl.Normal;
633 indicator.ResourceUrl = indicatorImageUrl.Normal;
636 if (indicatorColor == null)
638 indicator.Color = new Color(DefaultIndicatorColor);
642 indicator.Color = indicatorColor;
646 private void UpdateSelectedIndicator(ImageView indicator)
648 if (indicator == null) return;
650 if (IsLastSelected && lastIndicatorImageUrl != null)
652 indicator.ResourceUrl = lastIndicatorImageUrl.Selected;
656 indicator.ResourceUrl = indicatorImageUrl.Selected;
659 if (indicatorColor == null)
661 indicator.Color = new Color(DefaultSelectedIndicatorColor);
665 indicator.Color = indicatorColor;
669 private void CreateIndicator(int index)
671 Debug.Assert(indicatorSize != null);
673 ImageView indicator = new ImageView
675 ResourceUrl = indicatorImageUrl?.Normal,
676 Size = indicatorSize,
677 Color = (indicatorColor == null) ? new Color(DefaultIndicatorColor) : indicatorColor,
679 indicatorList.Add(indicator);
680 container.Add(indicator);
682 if (index == selectedIndex)
684 UpdateSelectedIndicator(indicatorList[selectedIndex]);
688 private void UpdateContainer()
690 Debug.Assert(indicatorSize != null);
692 if (indicatorList.Count > 0)
694 container.SizeWidth = (indicatorSize.Width + indicatorSpacing) * indicatorList.Count - indicatorSpacing;
698 container.SizeWidth = 0;
700 container.SizeHeight = indicatorSize.Height;
701 container.PositionX = (int)((this.SizeWidth - container.SizeWidth) / 2);
704 private void UpdateVisual()
706 Debug.Assert(indicatorSize != null);
708 if (indicatorImageUrl == null)
713 if (container != null && (container.Layout is LinearLayout linearLayout))
715 linearLayout.CellPadding = new Size2D(indicatorSpacing, 0);
718 for (int i = 0; i < indicatorList.Count; i++)
720 ImageView indicator = indicatorList[i];
721 indicator.ResourceUrl = indicatorImageUrl?.Normal;
722 indicator.Size = indicatorSize;
725 if (lastIndicatorImageUrl != null && indicatorCount > 0)
727 indicatorList[LastIndicatorIndex].ResourceUrl = lastIndicatorImageUrl.Normal;
731 private int LastIndicatorIndex => IndicatorCount - 1;
732 private bool IsLastSelected => LastIndicatorIndex == selectedIndex;