ferroui_controls/calendar/
selected_dates_collection.rs1use super::{Calendar, CalendarSelectionMode, DateTimeHelper};
10use crate::primitives::SelectingItemsControl;
11use crate::SelectionChangedEventArgs;
12use ferroui_base::collections::{CollectionChangedHandler, FerroList};
13use ferroui_base::threading::Dispatcher;
14use ferroui_base::utilities::DateTime;
15use ferroui_base::{BoxedValue, Ref, WeakRef};
16use std::cell::{Cell, RefCell};
17use std::rc::Rc;
18
19#[derive(Clone)]
26pub struct SelectedDatesCollection(Rc<SelectedDatesCollectionData>);
27
28struct SelectedDatesCollectionData {
29 list: FerroList<DateTime>,
30 added_items: RefCell<Vec<DateTime>>,
32 is_cleared: Cell<bool>,
34 is_range_added: Cell<bool>,
36 owner: WeakRef<Calendar>,
38}
39
40impl PartialEq for SelectedDatesCollection {
41 fn eq(&self, other: &Self) -> bool {
42 Rc::ptr_eq(&self.0, &other.0)
43 }
44}
45
46fn boxed_dates(dates: &[DateTime]) -> Vec<Option<BoxedValue>> {
48 dates.iter().map(|date| Some(Rc::new(*date) as BoxedValue)).collect()
49}
50
51impl SelectedDatesCollection {
52 pub fn new(owner: &Ref<Calendar>) -> Self {
56 Self(Rc::new(SelectedDatesCollectionData {
57 list: FerroList::new(),
58 added_items: RefCell::new(Vec::new()),
59 is_cleared: Cell::new(false),
60 is_range_added: Cell::new(false),
61 owner: owner.downgrade(),
62 }))
63 }
64
65 fn owner(&self) -> Ref<Calendar> {
66 self.0.owner.upgrade().expect("the calendar of the selected dates is alive")
67 }
68
69 fn invoke_collection_changed(&self, removed_items: &[DateTime], added_items: &[DateTime]) {
70 self.owner().on_selected_dates_collection_changed(&SelectionChangedEventArgs::new(
71 Some(SelectingItemsControl::selection_changed_event()),
72 boxed_dates(removed_items),
73 boxed_dates(added_items),
74 ));
75 }
76
77 pub fn add_range(&self, start: DateTime, end: DateTime) {
83 let owner = self.owner();
84
85 let increment = if DateTime::compare(end, start) >= 0 { 1 } else { -1 };
90
91 self.0.added_items.borrow_mut().clear();
92
93 let mut range_start = Some(start);
94 self.0.is_range_added.set(true);
95
96 if owner.is_mouse_selection() {
97 while let Some(current) = range_start {
101 if DateTime::compare(end, current) == -increment {
102 break;
103 }
104
105 if Calendar::is_valid_date_selection(&owner, Some(current)) {
106 self.add(current);
107 } else if owner.selection_mode() == CalendarSelectionMode::SingleRange {
108 owner.set_hover_end(Some(current.add_days(-increment as f64)));
109 break;
110 }
111
112 range_start = DateTimeHelper::add_days(current, increment);
113 }
114 } else {
115 if owner.selection_mode() == CalendarSelectionMode::SingleRange && self.count() > 0 {
121 owner.add_removed_items(&self.to_vec());
122 self.clear_internal();
123 }
124
125 while let Some(current) = range_start {
126 if DateTime::compare(end, current) == -increment {
127 break;
128 }
129
130 self.add(current);
131 range_start = DateTimeHelper::add_days(current, increment);
132 }
133 }
134
135 let added_items = self.0.added_items.borrow().clone();
140 owner.on_selected_dates_collection_changed(&SelectionChangedEventArgs::new(
141 Some(SelectingItemsControl::selection_changed_event()),
142 boxed_dates(&owner.removed_items()),
143 boxed_dates(&added_items),
144 ));
145 owner.clear_removed_items();
146 owner.update_months();
147 self.0.is_range_added.set(false);
148 }
149
150 pub fn count(&self) -> usize {
154 self.0.list.count()
155 }
156
157 pub fn is_empty(&self) -> bool {
159 self.0.list.is_empty()
160 }
161
162 pub fn get(&self, index: usize) -> DateTime {
164 self.0.list.get(index)
165 }
166
167 pub fn to_vec(&self) -> Vec<DateTime> {
169 self.0.list.to_vec()
170 }
171
172 pub fn contains(&self, item: DateTime) -> bool {
174 self.0.list.contains(&item)
175 }
176
177 pub fn index_of(&self, item: DateTime) -> Option<usize> {
179 self.0.list.index_of(&item)
180 }
181
182 pub fn add(&self, item: DateTime) {
187 self.insert_item(self.count(), item);
188 }
189
190 pub fn insert(&self, index: usize, item: DateTime) {
195 assert!(index <= self.count(), "Index must be within the bounds of the List.");
196 self.insert_item(index, item);
197 }
198
199 pub fn remove(&self, item: DateTime) -> bool {
201 match self.index_of(item) {
202 Some(index) => {
203 self.remove_item(index);
204 true
205 }
206 None => false,
207 }
208 }
209
210 pub fn remove_at(&self, index: usize) {
212 assert!(index < self.count(), "Index was out of range.");
213 self.remove_item(index);
214 }
215
216 pub fn set(&self, index: usize, item: DateTime) {
218 assert!(index < self.count(), "Index was out of range.");
219 self.set_item(index, item);
220 }
221
222 pub fn clear(&self) {
224 self.clear_items();
225 }
226
227 pub fn move_item(&self, old_index: usize, new_index: usize) {
229 self.0.list.move_item(old_index, new_index);
230 }
231
232 pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<DateTime>>) -> u64 {
235 self.0.list.add_collection_changed(handler)
236 }
237
238 pub fn remove_collection_changed(&self, token: u64) -> bool {
240 self.0.list.remove_collection_changed(token)
241 }
242
243 fn clear_items(&self) {
249 Self::ensure_valid_thread();
250 let owner = self.owner();
251
252 let added_items: Vec<DateTime> = Vec::new();
253 let removed_items = self.to_vec();
254
255 self.0.list.clear();
256
257 if owner.selection_mode() != CalendarSelectionMode::None && owner.selected_date().is_some() {
259 owner.set_selected_date(None);
260 }
261
262 if !removed_items.is_empty() {
263 self.invoke_collection_changed(&removed_items, &added_items);
264 }
265 owner.update_months();
266 }
267
268 fn insert_item(&self, index: usize, item: DateTime) {
272 Self::ensure_valid_thread();
273 let owner = self.owner();
274
275 if !self.contains(item) {
276 if self.check_selection_mode() {
277 if Calendar::is_valid_date_selection(&owner, Some(item)) {
278 let mut index = index;
279
280 if self.0.is_cleared.get() {
283 index = 0;
284 self.0.is_cleared.set(false);
285 }
286
287 self.0.list.insert(index, item);
288
289 if index == 0
291 && !owner.selected_date().is_some_and(|selected| DateTime::compare(selected, item) == 0)
292 {
293 owner.set_selected_date(Some(item));
294 }
295
296 if !self.0.is_range_added.get() {
297 self.invoke_collection_changed(&owner.removed_items(), &[item]);
300 owner.clear_removed_items();
301 let month_difference =
302 DateTimeHelper::compare_year_month(item, owner.display_date_internal());
303
304 if month_difference < 2 && month_difference > -2 {
305 owner.update_months();
306 }
307 } else {
308 self.0.added_items.borrow_mut().push(item);
309 }
310 } else {
311 panic!("Specified argument was out of the range of valid values. (Parameter 'SelectedDate value is not valid.')");
312 }
313 }
314 }
315 }
316
317 fn remove_item(&self, index: usize) {
321 Self::ensure_valid_thread();
322 let owner = self.owner();
323
324 if index >= self.count() {
325 self.0.list.remove_at(index);
326 } else {
327 let added_items: Vec<DateTime> = Vec::new();
328 let removed = self.get(index);
329 let month_difference = DateTimeHelper::compare_year_month(removed, owner.display_date_internal());
330
331 self.0.list.remove_at(index);
332
333 if index == 0 {
335 if self.count() > 0 {
336 owner.set_selected_date(Some(self.get(0)));
337 } else {
338 owner.set_selected_date(None);
339 }
340 }
341
342 self.invoke_collection_changed(&[removed], &added_items);
343
344 if month_difference < 2 && month_difference > -2 {
345 owner.update_months();
346 }
347 }
348 }
349
350 fn set_item(&self, index: usize, item: DateTime) {
354 Self::ensure_valid_thread();
355 let owner = self.owner();
356
357 if !self.contains(item) {
358 if index >= self.count() {
359 self.0.list.set(index, item);
360 } else {
361 let current = self.get(index);
362 if DateTime::compare(current, item) != 0 && Calendar::is_valid_date_selection(&owner, Some(item)) {
363 self.0.list.set(index, item);
364
365 if index == 0
367 && !owner.selected_date().is_some_and(|selected| DateTime::compare(selected, item) == 0)
368 {
369 owner.set_selected_date(Some(item));
370 }
371
372 self.invoke_collection_changed(&[current], &[item]);
373
374 let month_difference = DateTimeHelper::compare_year_month(item, owner.display_date_internal());
375
376 if month_difference < 2 && month_difference > -2 {
377 owner.update_months();
378 }
379 }
380 }
381 }
382 }
383
384 pub(crate) fn clear_internal(&self) {
387 self.0.list.clear();
388 }
389
390 fn check_selection_mode(&self) -> bool {
391 let owner = self.owner();
392
393 if owner.selection_mode() == CalendarSelectionMode::None {
394 panic!("The SelectedDate property cannot be set when the selection mode is None.");
395 }
396 if owner.selection_mode() == CalendarSelectionMode::SingleDate && self.count() > 0 {
397 panic!("The SelectedDates collection can be changed only in a multiple selection mode. Use the SelectedDate in a single selection mode.");
398 }
399
400 if owner.selection_mode() == CalendarSelectionMode::SingleRange
405 && !self.0.is_range_added.get()
406 && self.count() > 0
407 {
408 owner.add_removed_items(&self.to_vec());
409 self.clear_internal();
410 self.0.is_cleared.set(true);
411 }
412 true
413 }
414
415 fn ensure_valid_thread() {
416 Dispatcher::ui_thread().verify_access();
417 }
418}