1use super::{PipsPagerSelectedIndexChangedEventArgs, PipsPagerTemplateSettings};
2use crate::automation::AutomationProperties;
3use crate::primitives::{TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl, TemplatedControlImplExt};
4use crate::{Button, Control, ControlImpl, ListBox, ScrollViewer, StackPanel};
5use ferroui_base::data::BindingMode;
6use ferroui_base::input::{InputElementImpl, InputElementImplExt, Key, KeyEventArgs};
7use ferroui_base::interactivity::{Interactive, InteractiveImpl, RoutedEvent, RoutedEventHandlerToken, RoutingStrategies};
8use ferroui_base::layout::{LayoutableImpl, Orientation};
9use ferroui_base::reactive::IDisposable;
10use ferroui_base::styling::ControlTheme;
11use ferroui_base::threading::{CancellationToken, CancellationTokenSource, Dispatcher, DispatcherPriority, DispatcherTimer};
12use ferroui_base::{
13 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, ferro_routed_event, instantiate,
14 DirectProperty, DirectPropertyMetadata, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable,
15 Ref, StyledElementImpl, StyledProperty, StyledPropertyOptions, Vector, VisualImpl,
16};
17use std::cell::{Cell, RefCell};
18use std::rc::Rc;
19use std::time::Duration;
20
21const PART_PREVIOUS_BUTTON: &str = "PART_PreviousButton";
22const PART_NEXT_BUTTON: &str = "PART_NextButton";
23const PART_PIPS_PAGER_LIST: &str = "PART_PipsPagerList";
24
25const PC_FIRST_PAGE: &str = ":first-page";
26const PC_LAST_PAGE: &str = ":last-page";
27const PC_VERTICAL: &str = ":vertical";
28const PC_HORIZONTAL: &str = ":horizontal";
29
30#[repr(C)]
33pub struct PipsPager {
34 base: TemplatedControl,
35 previous_button: RefCell<Option<(Ref<Button>, RoutedEventHandlerToken)>>,
36 next_button: RefCell<Option<(Ref<Button>, RoutedEventHandlerToken)>>,
37 pips_pager_list: RefCell<Option<(Ref<ListBox>, RoutedEventHandlerToken)>>,
38 pips_pager_list_container_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
40 scroll_pending: Cell<bool>,
41 updating_pager_size: Cell<bool>,
42 is_initial_load: Cell<bool>,
43 last_selected_page_index: Cell<i32>,
44 scroll_animation_cts: RefCell<Option<CancellationTokenSource>>,
45 template_settings: RefCell<Ref<PipsPagerTemplateSettings>>,
46}
47
48ferro_class!(PipsPager: TemplatedControl);
49
50ferro_class_info!(PipsPager {
51 new: PipsPager::new,
52 markup: {
53 attributes: [
54 TemplatePart("PART_PreviousButton", type(Ref<Button>)),
55 TemplatePart("PART_NextButton", type(Ref<Button>)),
56 TemplatePart("PART_PipsPagerList", type(Ref<ListBox>)),
57 PseudoClasses(":first-page", ":last-page", ":vertical", ":horizontal"),
58 ],
59 },
60});
61
62ferro_impl_classes!(PipsPager: StyledElementImpl, VisualImpl, LayoutableImpl, InteractiveImpl);
63
64impl FerroObjectImpl for PipsPager {
65 fn constructed(this: &Self) {
66 Self::parent_constructed(this);
67 this.update_pseudo_classes();
68 }
69}
70
71impl TemplatedControlImpl for PipsPager {
72 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
73 Self::parent_on_apply_template(this, e);
74
75 if let Some(cts) = this.scroll_animation_cts.take() {
76 cts.cancel();
77 }
78 this.is_initial_load.set(true);
79
80 if let Some((button, token)) = this.previous_button.take() {
82 button.remove_handler(Button::click_event(), token);
83 }
84
85 if let Some((button, token)) = this.next_button.take() {
86 button.remove_handler(Button::click_event(), token);
87 }
88
89 if let Some((list, token)) = this.pips_pager_list.take() {
91 list.remove_handler(Control::size_changed_event(), token);
92 for subscription in this.pips_pager_list_container_subscriptions.take() {
93 subscription.dispose();
94 }
95 }
96
97 let previous_button = e.name_scope().find_as::<Button>(PART_PREVIOUS_BUTTON);
99 let next_button = e.name_scope().find_as::<Button>(PART_NEXT_BUTTON);
100 let pips_pager_list = e.name_scope().find_as::<ListBox>(PART_PIPS_PAGER_LIST);
101
102 let weak = this.to_ref().downgrade();
103
104 if let Some(button) = previous_button {
106 let weak = weak.clone();
107 let token = button.click(move |_, _| {
108 if let Some(this) = weak.upgrade() {
109 this.previous_button_click();
110 }
111 });
112 AutomationProperties::set_name(&button, Some("Previous page"));
113 *this.previous_button.borrow_mut() = Some((button, token));
114 }
115
116 if let Some(button) = next_button {
118 let weak = weak.clone();
119 let token = button.click(move |_, _| {
120 if let Some(this) = weak.upgrade() {
121 this.next_button_click();
122 }
123 });
124 AutomationProperties::set_name(&button, Some("Next page"));
125 *this.next_button.borrow_mut() = Some((button, token));
126 }
127
128 if let Some(list) = pips_pager_list {
130 let size_changed_weak = weak.clone();
131 let token = list.size_changed(move |_, _| {
132 if let Some(this) = size_changed_weak.upgrade() {
133 this.on_pips_pager_list_size_changed();
134 }
135 });
136 let prepared_weak = weak.clone();
137 let prepared = list.container_prepared(move |e| {
138 if let Some(this) = prepared_weak.upgrade() {
139 this.update_container_automation_properties(e.container(), e.index());
140 }
141 });
142 let index_changed = list.container_index_changed(move |e| {
143 if let Some(this) = weak.upgrade() {
144 this.update_container_automation_properties(e.container(), e.new_index());
145 }
146 });
147 *this.pips_pager_list_container_subscriptions.borrow_mut() = vec![prepared, index_changed];
148 *this.pips_pager_list.borrow_mut() = Some((list, token));
149 }
150
151 this.update_buttons_state();
152 this.update_pseudo_classes();
153 this.update_pager_size();
154 }
155}
156
157impl ControlImpl for PipsPager {
158 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
159 crate::automation::peers::PipsPagerAutomationPeer::new(this).upcast()
160 }
161}
162
163impl InputElementImpl for PipsPager {
164 fn on_key_down(this: &Self, e: &KeyEventArgs) {
165 Self::parent_on_key_down(this, e);
166
167 if e.handled() {
168 return;
169 }
170
171 let is_horizontal = this.orientation() == Orientation::Horizontal;
172
173 match e.key {
174 Key::Left | Key::Up if (e.key == Key::Left) == is_horizontal => {
175 if this.selected_page_index() > 0 {
176 this.set_current_value(Self::selected_page_index_property(), this.selected_page_index() - 1);
177 e.set_handled(true);
178 }
179 }
180 Key::Right | Key::Down if (e.key == Key::Right) == is_horizontal => {
181 if this.selected_page_index() < this.number_of_pages() - 1 {
182 this.set_current_value(Self::selected_page_index_property(), this.selected_page_index() + 1);
183 e.set_handled(true);
184 }
185 }
186 Key::Home => {
187 this.set_current_value(Self::selected_page_index_property(), 0);
188 e.set_handled(true);
189 }
190 Key::End => {
191 if this.number_of_pages() > 0 {
192 this.set_current_value(Self::selected_page_index_property(), this.number_of_pages() - 1);
193 e.set_handled(true);
194 }
195 }
196 _ => {}
197 }
198 }
199}
200
201ferro_properties! {
202 impl PipsPager {
203 pub fn max_visible_pips_property() -> StyledProperty<i32> {
205 FerroProperty::register::<PipsPager, _>("MaxVisiblePips", 5)
206 }
207
208 pub fn is_next_button_visible_property() -> StyledProperty<bool> {
210 FerroProperty::register::<PipsPager, _>("IsNextButtonVisible", true)
211 }
212
213 pub fn number_of_pages_property() -> StyledProperty<i32> {
215 FerroProperty::register::<PipsPager, _>("NumberOfPages", 0)
216 }
217
218 pub fn orientation_property() -> StyledProperty<Orientation> {
220 FerroProperty::register::<PipsPager, _>("Orientation", Orientation::Horizontal)
221 }
222
223 pub fn is_previous_button_visible_property() -> StyledProperty<bool> {
225 FerroProperty::register::<PipsPager, _>("IsPreviousButtonVisible", true)
226 }
227
228 pub fn selected_page_index_property() -> StyledProperty<i32> {
230 FerroProperty::register_with::<PipsPager, _>(
231 "SelectedPageIndex",
232 StyledPropertyOptions::new(0).default_binding_mode(BindingMode::TwoWay),
233 )
234 }
235
236 pub fn template_settings_property() -> DirectProperty<PipsPager, Ref<PipsPagerTemplateSettings>> {
238 FerroProperty::register_direct_with::<PipsPager, _>(
239 "TemplateSettings",
240 |x| x.template_settings(),
241 None,
242 DirectPropertyMetadata::new(None),
243 )
244 }
245
246 pub fn previous_button_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
248 FerroProperty::register::<PipsPager, _>("PreviousButtonTheme", None)
249 }
250
251 pub fn next_button_theme_property() -> StyledProperty<Option<Ref<ControlTheme>>> {
253 FerroProperty::register::<PipsPager, _>("NextButtonTheme", None)
254 }
255 }
256}
257
258impl PipsPager {
259 ferro_routed_event!(
260 pub fn selected_index_changed_event() -> RoutedEvent<PipsPagerSelectedIndexChangedEventArgs> {
262 RoutedEvent::register::<PipsPager, _>("SelectedIndexChanged", RoutingStrategies::BUBBLE)
263 }
264 );
265
266 fn static_constructor() {
267 crate::markup_types::lists::PipsList::register();
270
271 Self::selected_page_index_property()
272 .changed()
273 .add_class_handler::<PipsPager>(|x, e| x.on_selected_page_index_changed(e));
274 Self::number_of_pages_property().changed().add_class_handler::<PipsPager>(|x, e| x.on_number_of_pages_changed(e));
275 Self::is_previous_button_visible_property()
276 .changed()
277 .add_class_handler::<PipsPager>(|x, _| x.on_is_previous_button_visible_changed());
278 Self::is_next_button_visible_property()
279 .changed()
280 .add_class_handler::<PipsPager>(|x, _| x.on_is_next_button_visible_changed());
281 Self::orientation_property().changed().add_class_handler::<PipsPager>(|x, _| x.on_orientation_changed());
282 Self::max_visible_pips_property()
283 .changed()
284 .add_class_handler::<PipsPager>(|x, e| x.on_max_visible_pips_changed(e));
285 }
286
287 pub fn construct() -> Self {
289 Self {
290 base: TemplatedControl::construct(),
291 previous_button: RefCell::new(None),
292 next_button: RefCell::new(None),
293 pips_pager_list: RefCell::new(None),
294 pips_pager_list_container_subscriptions: RefCell::new(Vec::new()),
295 scroll_pending: Cell::new(false),
296 updating_pager_size: Cell::new(false),
297 is_initial_load: Cell::new(false),
298 last_selected_page_index: Cell::new(0),
299 scroll_animation_cts: RefCell::new(None),
300 template_settings: RefCell::new(PipsPagerTemplateSettings::new()),
301 }
302 }
303
304 pub fn new() -> Ref<Self> {
306 instantiate(Self::construct())
307 }
308
309 pub fn selected_index_changed(
311 &self,
312 handler: impl Fn(&Interactive, &PipsPagerSelectedIndexChangedEventArgs) + 'static,
313 ) -> RoutedEventHandlerToken {
314 self.add_handler(Self::selected_index_changed_event(), handler)
315 }
316
317 pub fn max_visible_pips(&self) -> i32 {
319 self.get_value(Self::max_visible_pips_property())
320 }
321
322 pub fn set_max_visible_pips(&self, value: i32) {
323 self.set_value(Self::max_visible_pips_property(), value)
324 }
325
326 pub fn is_next_button_visible(&self) -> bool {
328 self.get_value(Self::is_next_button_visible_property())
329 }
330
331 pub fn set_is_next_button_visible(&self, value: bool) {
332 self.set_value(Self::is_next_button_visible_property(), value)
333 }
334
335 fn update_container_automation_properties(&self, container: &Ref<Control>, index: i32) {
336 AutomationProperties::set_name(container, Some(&format!("Page {}", index + 1)));
337 AutomationProperties::set_position_in_set(container, index + 1);
338 AutomationProperties::set_size_of_set(container, self.number_of_pages());
339 }
340
341 pub fn number_of_pages(&self) -> i32 {
343 self.get_value(Self::number_of_pages_property())
344 }
345
346 pub fn set_number_of_pages(&self, value: i32) {
347 self.set_value(Self::number_of_pages_property(), value)
348 }
349
350 pub fn orientation(&self) -> Orientation {
352 self.get_value(Self::orientation_property())
353 }
354
355 pub fn set_orientation(&self, value: Orientation) {
356 self.set_value(Self::orientation_property(), value)
357 }
358
359 pub fn is_previous_button_visible(&self) -> bool {
361 self.get_value(Self::is_previous_button_visible_property())
362 }
363
364 pub fn set_is_previous_button_visible(&self, value: bool) {
365 self.set_value(Self::is_previous_button_visible_property(), value)
366 }
367
368 pub fn selected_page_index(&self) -> i32 {
370 self.get_value(Self::selected_page_index_property())
371 }
372
373 pub fn set_selected_page_index(&self, value: i32) {
374 self.set_value(Self::selected_page_index_property(), value)
375 }
376
377 pub fn template_settings(&self) -> Ref<PipsPagerTemplateSettings> {
379 self.template_settings.borrow().clone()
380 }
381
382 pub fn previous_button_theme(&self) -> Option<Ref<ControlTheme>> {
387 self.get_value(Self::previous_button_theme_property())
388 }
389
390 pub fn set_previous_button_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
391 self.set_value(Self::previous_button_theme_property(), value.into().0)
392 }
393
394 pub fn next_button_theme(&self) -> Option<Ref<ControlTheme>> {
396 self.get_value(Self::next_button_theme_property())
397 }
398
399 pub fn set_next_button_theme(&self, value: impl Into<Nullable<ControlTheme>>) {
400 self.set_value(Self::next_button_theme_property(), value.into().0)
401 }
402
403 fn previous_button_part(&self) -> Option<Ref<Button>> {
404 self.previous_button.borrow().as_ref().map(|(button, _)| button.clone())
405 }
406
407 fn next_button_part(&self) -> Option<Ref<Button>> {
408 self.next_button.borrow().as_ref().map(|(button, _)| button.clone())
409 }
410
411 fn pips_pager_list_part(&self) -> Option<Ref<ListBox>> {
412 self.pips_pager_list.borrow().as_ref().map(|(list, _)| list.clone())
413 }
414
415 fn on_selected_page_index_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
416 let (old_index, new_index) = e.get_old_and_new_value::<i32>();
417
418 if new_index < 0 {
419 self.set_current_value(Self::selected_page_index_property(), 0);
420 return;
421 }
422
423 if self.number_of_pages() > 0 {
424 if new_index >= self.number_of_pages() {
425 self.set_current_value(Self::selected_page_index_property(), self.number_of_pages() - 1);
426 return;
427 }
428 } else if new_index > 0 {
429 self.set_current_value(Self::selected_page_index_property(), 0);
430 return;
431 }
432
433 self.last_selected_page_index.set(old_index);
434
435 self.update_buttons_state();
436 self.update_pseudo_classes();
437 self.request_scroll_to_selected_pip();
438
439 self.raise_event(&PipsPagerSelectedIndexChangedEventArgs::new(old_index, new_index));
440 }
441
442 fn on_number_of_pages_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
443 let new_value = e.get_new_value::<i32>();
444
445 if new_value < 0 {
446 self.set_current_value(Self::number_of_pages_property(), 0);
447 return;
448 }
449
450 let pips = self.template_settings().pips();
451 let count = pips.count() as i32;
452
453 if count < new_value {
454 let start = count + 1;
455 pips.add_range(start..=new_value);
456 } else if count > new_value {
457 pips.remove_range(new_value as usize, (count - new_value) as usize);
458 }
459
460 let mut index_clamped = false;
461
462 if new_value > 0 && self.selected_page_index() >= new_value {
463 self.set_current_value(Self::selected_page_index_property(), new_value - 1);
464 index_clamped = true;
465 } else if new_value == 0 && self.selected_page_index() > 0 {
466 self.set_current_value(Self::selected_page_index_property(), 0);
467 index_clamped = true;
468 }
469
470 if !index_clamped {
471 self.update_buttons_state();
472 self.update_pseudo_classes();
473 }
474
475 self.update_pager_size();
476 }
477
478 fn on_is_previous_button_visible_changed(&self) {
479 self.update_buttons_state();
480 }
481
482 fn on_is_next_button_visible_changed(&self) {
483 self.update_buttons_state();
484 }
485
486 fn on_orientation_changed(&self) {
487 self.update_pseudo_classes();
488 self.update_pager_size();
489 }
490
491 fn on_max_visible_pips_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
492 let new_value = e.get_new_value::<i32>();
493
494 if new_value < 1 {
495 self.set_current_value(Self::max_visible_pips_property(), 1);
496 return;
497 }
498
499 self.update_pager_size();
500 }
501
502 fn previous_button_click(&self) {
503 if self.selected_page_index() > 0 {
504 self.set_current_value(Self::selected_page_index_property(), self.selected_page_index() - 1);
505 }
506 }
507
508 fn next_button_click(&self) {
509 if self.selected_page_index() < self.number_of_pages() - 1 {
510 self.set_current_value(Self::selected_page_index_property(), self.selected_page_index() + 1);
511 }
512 }
513
514 fn update_buttons_state(&self) {
515 if let Some(previous_button) = self.previous_button_part() {
516 previous_button.set_is_enabled(self.selected_page_index() > 0);
517 }
518
519 if let Some(next_button) = self.next_button_part() {
520 next_button.set_is_enabled(self.selected_page_index() < self.number_of_pages() - 1);
521 }
522 }
523
524 fn update_pseudo_classes(&self) {
525 let pseudo_classes = self.pseudo_classes();
526 pseudo_classes.set(PC_FIRST_PAGE, self.selected_page_index() == 0);
527 pseudo_classes
528 .set(PC_LAST_PAGE, self.number_of_pages() > 0 && self.selected_page_index() >= self.number_of_pages() - 1);
529 pseudo_classes.set(PC_VERTICAL, self.orientation() == Orientation::Vertical);
530 pseudo_classes.set(PC_HORIZONTAL, self.orientation() == Orientation::Horizontal);
531 }
532
533 fn on_pips_pager_list_size_changed(&self) {
534 if !self.updating_pager_size.get() {
535 self.update_pager_size();
536 }
537 }
538
539 fn request_scroll_to_selected_pip(&self) {
540 if self.scroll_pending.get() {
541 return;
542 }
543
544 self.scroll_pending.set(true);
545 let this = self.to_ref();
546 Dispatcher::ui_thread().post_local(
547 move || {
548 this.scroll_pending.set(false);
549 this.scroll_to_selected_pip();
550 },
551 DispatcherPriority::INPUT,
552 );
553 }
554
555 fn scroll_to_selected_pip(&self) {
556 let Some(list) = self.pips_pager_list_part() else {
557 return;
558 };
559
560 if self.number_of_pages() <= self.max_visible_pips() {
561 return;
562 }
563
564 let Some(scroll_viewer) = list.scroll().and_then(|scroll| scroll.cast::<ScrollViewer>()) else {
565 return;
566 };
567
568 let Some(container) = list.container_from_index(self.selected_page_index()) else {
569 return;
570 };
571
572 let is_horizontal = self.orientation() == Orientation::Horizontal;
573 let margin = container.margin();
574 let pip_size = if is_horizontal {
575 container.bounds().width + margin.left + margin.right
576 } else {
577 container.bounds().height + margin.top + margin.bottom
578 };
579
580 if pip_size <= 0.0 {
581 return;
582 }
583
584 let max_visible_pips = self.max_visible_pips();
585 let even_offset =
586 if max_visible_pips % 2 == 0 && self.selected_page_index() > self.last_selected_page_index.get() {
587 1
588 } else {
589 0
590 };
591 let offset_elements = self.selected_page_index() + even_offset - max_visible_pips / 2;
592 let mut target_offset = f64::max(0.0, f64::from(offset_elements) * pip_size);
593 let max_offset = if is_horizontal {
594 scroll_viewer.extent().width - scroll_viewer.viewport().width
595 } else {
596 scroll_viewer.extent().height - scroll_viewer.viewport().height
597 };
598 target_offset = f64::min(target_offset, f64::max(0.0, max_offset));
599
600 if self.is_initial_load.get() {
601 self.is_initial_load.set(false);
602 Self::set_scroll_offset(&scroll_viewer, target_offset, is_horizontal);
603 return;
604 }
605
606 self.animate_scroll_offset(&scroll_viewer, target_offset, is_horizontal);
607 }
608
609 fn set_scroll_offset(scroll_viewer: &Ref<ScrollViewer>, offset: f64, is_horizontal: bool) {
610 let current = scroll_viewer.offset();
611 scroll_viewer.set_offset(if is_horizontal {
612 Vector::new(offset, current.y)
613 } else {
614 Vector::new(current.x, offset)
615 });
616 }
617
618 fn animate_scroll_offset(&self, scroll_viewer: &Ref<ScrollViewer>, target_offset: f64, is_horizontal: bool) {
619 if let Some(cts) = self.scroll_animation_cts.take() {
620 cts.cancel();
621 }
622 let cts = CancellationTokenSource::new();
623 let token = cts.token();
624 *self.scroll_animation_cts.borrow_mut() = Some(cts);
625
626 let start_offset = if is_horizontal { scroll_viewer.offset().x } else { scroll_viewer.offset().y };
627 let delta = target_offset - start_offset;
628
629 if delta.abs() < 0.5 {
630 Self::set_scroll_offset(scroll_viewer, target_offset, is_horizontal);
631 return;
632 }
633
634 const DURATION_MS: i32 = 200;
635 const FRAME_MS: u64 = 16;
636 let start_time = Dispatcher::ui_thread().now();
638
639 let scroll_viewer = scroll_viewer.clone();
640 DispatcherTimer::run_once(
641 move || {
642 Self::animate_step(&scroll_viewer, start_offset, delta, start_time, DURATION_MS, is_horizontal, &token)
643 },
644 Duration::from_millis(FRAME_MS),
645 DispatcherPriority::RENDER,
646 );
647 }
648
649 fn animate_step(
650 scroll_viewer: &Ref<ScrollViewer>,
651 start_offset: f64,
652 delta: f64,
653 start_time: i64,
654 duration_ms: i32,
655 is_horizontal: bool,
656 token: &CancellationToken,
657 ) {
658 if token.is_cancellation_requested() {
659 return;
660 }
661
662 let elapsed = Dispatcher::ui_thread().now() - start_time;
663 let t = f64::min(1.0, elapsed as f64 / f64::from(duration_ms));
664 let eased = 1.0 - (1.0 - t).powi(3);
665 let current = start_offset + (delta * eased);
666
667 Self::set_scroll_offset(scroll_viewer, current, is_horizontal);
668
669 if t < 1.0 {
670 let scroll_viewer = scroll_viewer.clone();
671 let token = token.clone();
672 DispatcherTimer::run_once(
673 move || {
674 Self::animate_step(
675 &scroll_viewer,
676 start_offset,
677 delta,
678 start_time,
679 duration_ms,
680 is_horizontal,
681 &token,
682 )
683 },
684 Duration::from_millis(16),
685 DispatcherPriority::RENDER,
686 );
687 }
688 }
689
690 fn update_pager_size(&self) {
691 let Some(list) = self.pips_pager_list_part() else {
692 return;
693 };
694
695 struct Reset<'a>(&'a Cell<bool>);
697 impl Drop for Reset<'_> {
698 fn drop(&mut self) {
699 self.0.set(false);
700 }
701 }
702
703 self.updating_pager_size.set(true);
704 let _reset = Reset(&self.updating_pager_size);
705 self.update_pager_size_core(&list);
706 }
707
708 fn update_pager_size_core(&self, list: &Ref<ListBox>) {
709 let mut pip_size = 12.0;
710
711 let mut container = list.container_from_index(self.selected_page_index());
712
713 if container.is_none() && list.items().count() > 0 {
714 container = list.container_from_index(0);
715 }
716
717 if let Some(container) = container {
718 let margin = container.margin();
719 let size = if self.orientation() == Orientation::Horizontal {
720 container.bounds().width + margin.left + margin.right
721 } else {
722 container.bounds().height + margin.top + margin.bottom
723 };
724
725 if size > 0.0 {
726 pip_size = size;
727 }
728 }
729
730 let mut spacing = 0.0;
731
732 if let Some(items_panel) = list.items_panel_root().and_then(|panel| panel.cast::<StackPanel>()) {
733 spacing = items_panel.spacing();
734 }
735
736 let visible_count = i32::min(self.number_of_pages(), self.max_visible_pips());
737
738 if visible_count <= 0 {
739 return;
740 }
741
742 let extent = (f64::from(visible_count) * pip_size) + (f64::from(visible_count - 1) * spacing);
743
744 if self.orientation() == Orientation::Horizontal {
745 list.set_width(extent);
746 list.set_height(f64::NAN);
747 } else {
748 list.set_height(extent);
749 list.set_width(f64::NAN);
750 }
751
752 self.request_scroll_to_selected_pip();
753 }
754}