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.BaseComponents;
23 using Tizen.NUI.Binding;
25 namespace Tizen.NUI.Components
28 /// Pagination shows the number of pages available and the currently active page.
30 /// <since_tizen> 8 </since_tizen>
31 public partial class Pagination : Control
33 /// <summary>The IndicatorSize bindable property.</summary>
34 [EditorBrowsable(EditorBrowsableState.Never)]
35 public static readonly BindableProperty IndicatorSizeProperty = BindableProperty.Create(nameof(IndicatorSize), typeof(Size), typeof(Pagination), null, propertyChanged: (bindable, oldValue, newValue) =>
39 var pagination = (Pagination)bindable;
40 pagination.indicatorSize = new Size((Size)newValue);
41 pagination.UpdateVisual();
42 pagination.UpdateContainer();
45 defaultValueCreator: (bindable) =>
47 return ((Pagination)bindable).indicatorSize;
50 /// <summary>The IndicatorImageUrlSelector bindable property.</summary>
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 public static readonly BindableProperty IndicatorImageUrlProperty = BindableProperty.Create("IndicatorImageUrl", typeof(Selector<string>), typeof(Pagination), null, propertyChanged: (bindable, oldValue, newValue) =>
54 var pagination = (Pagination)bindable;
55 pagination.indicatorImageUrl = ((Selector<string>)newValue)?.Clone();
56 pagination.UpdateVisual();
58 defaultValueCreator: (bindable) =>
60 return ((Pagination)bindable).indicatorImageUrl;
63 /// <summary>The IndicatorSpacing bindable property.</summary>
64 [EditorBrowsable(EditorBrowsableState.Never)]
65 public static readonly BindableProperty IndicatorSpacingProperty = BindableProperty.Create(nameof(IndicatorSpacing), typeof(int), typeof(Pagination), default(int), propertyChanged: (bindable, oldValue, newValue) =>
67 var pagination = (Pagination)bindable;
68 pagination.indicatorSpacing = (int)newValue;
69 pagination.UpdateVisual();
71 defaultValueCreator: (bindable) =>
73 return ((Pagination)bindable).indicatorSpacing;
76 private VisualView container;
77 private Size indicatorSize = new Size(10, 10);
78 private Selector<string> indicatorImageUrl;
79 private int indicatorSpacing;
80 private List<ImageVisual> indicatorList = new List<ImageVisual>();
82 private int indicatorCount = 0;
83 private int selectedIndex = 0;
85 private Color indicatorColor;
86 private Color selectedIndicatorColor;
87 private Selector<string> lastIndicatorImageUrl;
89 static Pagination() { }
92 /// Creates a new instance of a Pagination.
94 /// <since_tizen> 8 </since_tizen>
95 public Pagination() : base()
100 /// Creates a new instance of a Pagination using style.
102 /// <param name="style">The string to initialize the Pagination</param>
103 /// <since_tizen> 8 </since_tizen>
104 public Pagination(string style) : base(style)
109 /// Creates a new instance of a Pagination using style.
111 /// <param name="paginationStyle">The style object to initialize the Pagination</param>
112 /// <since_tizen> 8 </since_tizen>
113 public Pagination(PaginationStyle paginationStyle) : base(paginationStyle)
118 /// Return currently applied style.
121 /// Modifying contents in style may cause unexpected behaviour.
123 /// <since_tizen> 8 </since_tizen>
124 public PaginationStyle Style => (PaginationStyle)(ViewStyle as PaginationStyle)?.Clone();
127 /// Gets or sets the size of the indicator.
129 /// <since_tizen> 8 </since_tizen>
130 public Size IndicatorSize
132 get => (Size)GetValue(IndicatorSizeProperty);
133 set => SetValue(IndicatorSizeProperty, value);
137 /// Gets or sets the background resource of indicator.
139 /// <since_tizen> 8 </since_tizen>
140 public Selector<string> IndicatorImageUrl
142 get => (Selector<string>)GetValue(IndicatorImageUrlProperty);
143 set => SetValue(IndicatorImageUrlProperty, value);
147 /// This is experimental API.
148 /// Make the last indicator has exceptional image, not common image in the Pagination.
150 [EditorBrowsable(EditorBrowsableState.Never)]
151 public Selector<string> LastIndicatorImageUrl
155 return GetValue(LastIndicatorImageUrlProperty) as Selector<string>;
159 SetValue(LastIndicatorImageUrlProperty, value);
160 NotifyPropertyChanged();
163 private Selector<string> InternalLastIndicatorImageUrl
165 get => lastIndicatorImageUrl;
168 lastIndicatorImageUrl = value;
169 if (value != null && indicatorCount > 0)
171 indicatorList[LastIndicatorIndex].URL = IsLastSelected ? value.Selected : value.Normal;
177 /// Gets or sets the space of the indicator.
179 /// <since_tizen> 8 </since_tizen>
180 public int IndicatorSpacing
182 get => (int)GetValue(IndicatorSpacingProperty);
183 set => SetValue(IndicatorSpacingProperty, value);
188 /// Gets or sets the count of the pages/indicators.
190 /// <since_tizen> 8 </since_tizen>
191 /// <exception cref="ArgumentException">Thrown when the given value is negative.</exception>
192 public int IndicatorCount
196 return (int)GetValue(IndicatorCountProperty);
200 SetValue(IndicatorCountProperty, value);
201 NotifyPropertyChanged();
204 private int InternalIndicatorCount
208 return indicatorCount;
214 throw new ArgumentException($"Setting {nameof(IndicatorCount)} to negative is not allowed.");
217 if (indicatorCount == value)
222 int prevLastIndex = -1;
224 if (indicatorCount < value)
226 prevLastIndex = LastIndicatorIndex;
227 for (int i = indicatorCount; i < value; i++)
234 for (int i = value; i < indicatorCount; i++)
236 ImageVisual indicator = indicatorList[i];
237 container.RemoveVisual("Indicator" + i);
239 indicatorList.RemoveRange(value, indicatorCount - value);
241 if (selectedIndex >= value)
243 selectedIndex = Math.Max(0, value - 1);
247 SelectIn(indicatorList[selectedIndex]);
251 indicatorCount = value;
253 if (lastIndicatorImageUrl != null && indicatorImageUrl != null && indicatorCount > 0)
255 if (prevLastIndex >= 0)
257 indicatorList[prevLastIndex].URL = prevLastIndex == selectedIndex ? indicatorImageUrl.Selected : indicatorImageUrl.Normal;
259 indicatorList[LastIndicatorIndex].URL = IsLastSelected ? lastIndicatorImageUrl.Selected : lastIndicatorImageUrl.Normal;
266 private void OnIndicatorColorChanged(float r, float g, float b, float a)
268 IndicatorColor = new Color(r, g, b, a);
272 /// Color of the indicator.
274 /// <since_tizen> 8 </since_tizen>
275 public Color IndicatorColor
279 return GetValue(IndicatorColorProperty) as Color;
283 SetValue(IndicatorColorProperty, value);
284 NotifyPropertyChanged();
287 private Color InternalIndicatorColor
291 return new Color(OnIndicatorColorChanged, indicatorColor);
300 if (indicatorColor == null)
302 indicatorColor = new Color((Color)value);
306 if (indicatorColor == value)
311 indicatorColor = value;
314 if (indicatorCount == 0)
319 for (int i = 0; i < indicatorCount; i++)
321 if (i == selectedIndex)
326 ImageVisual indicator = indicatorList[i];
327 indicator.MixColor = indicatorColor;
328 indicator.Opacity = indicatorColor.A;
333 private void OnSelectedIndicatorColorChanged(float r, float g, float b, float a)
335 SelectedIndicatorColor = new Color(r, g, b, a);
339 /// Color of the selected indicator.
341 /// <since_tizen> 8 </since_tizen>
342 public Color SelectedIndicatorColor
346 return GetValue(SelectedIndicatorColorProperty) as Color;
350 SetValue(SelectedIndicatorColorProperty, value);
351 NotifyPropertyChanged();
354 private Color InternalSelectedIndicatorColor
358 return new Color(OnSelectedIndicatorColorChanged, selectedIndicatorColor);
367 if (selectedIndicatorColor == null)
369 selectedIndicatorColor = new Color((Color)value);
373 if (selectedIndicatorColor == value)
378 selectedIndicatorColor = value;
381 if (indicatorList.Count > selectedIndex)
383 var indicator = indicatorList[selectedIndex];
384 indicator.MixColor = selectedIndicatorColor;
385 indicator.Opacity = selectedIndicatorColor.A;
391 /// Gets or sets the index of the select indicator.
393 /// <since_tizen> 8 </since_tizen>
394 public int SelectedIndex
398 return (int)GetValue(SelectedIndexProperty);
402 SetValue(SelectedIndexProperty, value);
403 NotifyPropertyChanged();
406 private int InternalSelectedIndex
410 return selectedIndex;
414 var refinedValue = Math.Max(0, Math.Min(value, indicatorCount - 1));
416 if (selectedIndex == refinedValue)
421 Debug.Assert(refinedValue >= 0 && refinedValue < indicatorCount);
422 Debug.Assert(selectedIndex >= 0 && selectedIndex < indicatorCount);
424 SelectOut(indicatorList[selectedIndex]);
426 selectedIndex = refinedValue;
428 SelectIn(indicatorList[selectedIndex]);
430 if (Accessibility.Accessibility.IsEnabled && IsHighlighted)
432 EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value);
438 /// Retrieves the position of a indicator by index.
440 /// <param name="index">Indicator index</param>
441 /// <returns>The position of a indicator by index.</returns>
442 /// <since_tizen> 8 </since_tizen>
443 public Position GetIndicatorPosition(int index)
445 if (index < 0 || index >= indicatorList.Count)
449 return new Position(indicatorList[index].Position.X + container.PositionX, indicatorList[index].Position.Y + container.PositionY);
455 [EditorBrowsable(EditorBrowsableState.Never)]
456 protected override double AccessibilityGetMinimum()
464 [EditorBrowsable(EditorBrowsableState.Never)]
465 protected override double AccessibilityGetCurrent()
467 return (double)SelectedIndex;
473 [EditorBrowsable(EditorBrowsableState.Never)]
474 protected override double AccessibilityGetMaximum()
476 return (double)IndicatorCount;
482 [EditorBrowsable(EditorBrowsableState.Never)]
483 protected override bool AccessibilitySetCurrent(double value)
485 int integerValue = (int)value;
487 if (integerValue >= 0 && integerValue <= IndicatorCount)
489 SelectedIndex = integerValue;
497 /// Minimum increment.
499 [EditorBrowsable(EditorBrowsableState.Never)]
500 protected override double AccessibilityGetMinimumIncrement()
506 [EditorBrowsable(EditorBrowsableState.Never)]
507 public override void OnInitialize()
510 SetAccessibilityConstructor(Role.ScrollBar, AccessibilityInterface.Value);
511 AccessibilityHighlightable = true;
512 AppendAccessibilityAttribute("style", "pagecontrolbyvalue");
514 container = new VisualView()
517 ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft,
518 PivotPoint = Tizen.NUI.PivotPoint.CenterLeft,
519 PositionUsesPivotPoint = true,
523 //TODO: Apply color properties from PaginationStyle class.
524 indicatorColor = new Color(1.0f, 1.0f, 1.0f, 0.5f);
525 selectedIndicatorColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);
529 /// You can override it to do your select out operation.
531 /// <param name="selectOutIndicator">The indicator will be selected out</param>
532 /// <since_tizen> 8 </since_tizen>
533 protected virtual void SelectOut(VisualMap selectOutIndicator)
535 if (!(selectOutIndicator is ImageVisual visual)) return;
536 visual.URL = ((IsLastSelected && lastIndicatorImageUrl != null) ? lastIndicatorImageUrl : indicatorImageUrl)?.Normal;
538 if (indicatorColor == null)
540 //TODO: Apply color properties from PaginationStyle class.
541 visual.MixColor = new Color(1.0f, 1.0f, 1.0f, 0.5f);
542 visual.Opacity = 0.5f;
546 visual.MixColor = indicatorColor;
547 visual.Opacity = indicatorColor.A;
552 /// You can override it to do your select in operation.
554 /// <param name="selectInIndicator">The indicator will be selected in</param>
555 /// <since_tizen> 8 </since_tizen>
556 protected virtual void SelectIn(VisualMap selectInIndicator)
558 if (!(selectInIndicator is ImageVisual visual)) return;
559 visual.URL = ((IsLastSelected && lastIndicatorImageUrl != null) ? lastIndicatorImageUrl : indicatorImageUrl)?.Selected;
561 if (selectedIndicatorColor == null)
563 //TODO: Apply color properties from PaginationStyle class.
564 visual.MixColor = new Color(1.0f, 1.0f, 1.0f, 1.0f);
565 visual.Opacity = 1.0f;
569 visual.MixColor = selectedIndicatorColor;
570 visual.Opacity = selectedIndicatorColor.A;
575 /// you can override it to create your own default style.
577 /// <returns>The default pagination style.</returns>
578 /// <since_tizen> 8 </since_tizen>
579 protected override ViewStyle CreateViewStyle()
581 return new PaginationStyle();
585 /// you can override it to clean-up your own resources.
587 /// <param name="type">DisposeTypes</param>
588 /// <since_tizen> 8 </since_tizen>
589 protected override void Dispose(DisposeTypes type)
596 if (type == DisposeTypes.Explicit)
598 container.RemoveAll();
599 indicatorList.Clear();
601 this.Remove(container);
609 private void CreateIndicator(int index)
611 Debug.Assert(indicatorSize != null);
613 ImageVisual indicator = new ImageVisual
615 URL = indicatorImageUrl?.Normal,
616 Size = indicatorSize,
617 //TODO: Apply color properties from PaginationStyle class.
618 MixColor = (indicatorColor == null) ? new Color(1.0f, 1.0f, 1.0f, 0.5f) : indicatorColor,
619 Opacity = (indicatorColor == null) ? 0.5f : indicatorColor.A
621 indicator.Position = new Vector2((int)(indicatorSize.Width + indicatorSpacing) * indicatorList.Count, 0);
622 container.AddVisual("Indicator" + indicatorList.Count, indicator);
623 indicatorList.Add(indicator);
625 if (index == selectedIndex)
627 SelectIn(indicatorList[selectedIndex]);
631 private void UpdateContainer()
633 Debug.Assert(indicatorSize != null);
635 if (indicatorList.Count > 0)
637 container.SizeWidth = (indicatorSize.Width + indicatorSpacing) * indicatorList.Count - indicatorSpacing;
641 container.SizeWidth = 0;
643 container.SizeHeight = indicatorSize.Height;
644 container.PositionX = (int)((this.SizeWidth - container.SizeWidth) / 2);
647 private void UpdateVisual()
649 Debug.Assert(indicatorSize != null);
651 if (indicatorImageUrl == null)
656 for (int i = 0; i < indicatorList.Count; i++)
658 ImageVisual indicator = indicatorList[i];
659 indicator.URL = indicatorImageUrl?.Normal;
660 indicator.Size = indicatorSize;
661 indicator.Position = new Vector2((int)(indicatorSize.Width + indicatorSpacing) * i, 0);
664 if (lastIndicatorImageUrl != null && indicatorCount > 0)
666 indicatorList[LastIndicatorIndex].URL = lastIndicatorImageUrl.Normal;
670 private int LastIndicatorIndex => IndicatorCount - 1;
671 private bool IsLastSelected => LastIndicatorIndex == selectedIndex;