Skip to main content

ferroui_controls/markup_types/
plain.rs

1//! Markup metadata of the plain (non object-model) classes and of the
2//! collections of this crate that markup can name, construct or add to.
3//!
4//! A collection is a shared handle: the type itself is the handle.
5
6use crate::documents::{Inline, InlineCollection};
7use crate::primitives::popup_positioning::{
8    CustomPopupPlacement, PopupAnchor, PopupGravity, PopupPositionerConstraintAdjustment,
9};
10use crate::primitives::CustomPopupPlacementCallbackValue;
11use crate::templates::{DataTemplates, IDataTemplate};
12use crate::{ItemsSourceView, NativeMenuItemBase, Page, StateTriggerBase, TableViewColumn, VisualState, VisualStateGroup};
13use ferroui_base::collections::FerroList;
14use crate::{
15    ColumnDefinition, ColumnDefinitions, Control, Controls, ItemCollection, RowDefinition, RowDefinitions, TrayIcon,
16    TrayIcons, WindowIcon,
17};
18use super::classes::RoutedEventOf;
19use crate::ScrollChangedEventArgs;
20use ferroui_base::data::core::ValueTypes;
21use ferroui_base::interactivity::{IRoutedEventArgs, RoutedEvent};
22use ferroui_base::animation::TimeSpan;
23use ferroui_base::Vector;
24use ferroui_base::ferro_markup_type;
25use ferroui_base::metadata::{MarkupDelegate, MarkupType, MarkupTyped};
26use ferroui_base::{BoxedValue, Point, Rect, Ref, Size, Thickness, Visual};
27use std::cell::RefCell;
28use std::rc::Rc;
29
30/// `FerroList<T>.Capacity`.
31fn list_capacity<T: Clone>(list: &FerroList<T>) -> i32 {
32    i32::try_from(list.capacity()).unwrap_or(i32::MAX)
33}
34
35/// The setter of `FerroList<T>.Capacity`: a capacity below the number of
36/// items is an error, as in the managed original.
37fn set_list_capacity<T: Clone>(list: &FerroList<T>, capacity: i32) -> Result<(), &'static str> {
38    match usize::try_from(capacity) {
39        Ok(capacity) if capacity >= list.count() => Ok(list.set_capacity(capacity)),
40        _ => Err("capacity was less than the current size."),
41    }
42}
43
44// FerroUI.Controls
45
46ferro_markup_type!(class Controls {
47    namespace: "FerroUI.Controls",
48    handles: [Controls, Option<Controls>],
49    base: FerroList<Ref<Control>>,
50    constructors: [() => Controls::new],
51    methods: [fn Add(Ref<Control>) => |controls: &Controls, item: Ref<Control>| controls.add(item)],
52});
53
54ferro_markup_type!(class RowDefinitions {
55    namespace: "FerroUI.Controls",
56    handles: [RowDefinitions, Option<RowDefinitions>],
57    base: FerroList<Ref<RowDefinition>>,
58    parse: RowDefinitions::parse,
59    constructors: [
60        () => RowDefinitions::new,
61        try (String) => |s: String| RowDefinitions::parse(&s),
62    ],
63    methods: [fn Add(Ref<RowDefinition>) => |definitions: &RowDefinitions, item: Ref<RowDefinition>| definitions.add(item)],
64    // Declared on the base class of the definition lists in the managed original.
65    attributes: [FerroList(Separators = [",", " "])],
66});
67
68ferro_markup_type!(class ColumnDefinitions {
69    namespace: "FerroUI.Controls",
70    handles: [ColumnDefinitions, Option<ColumnDefinitions>],
71    base: FerroList<Ref<ColumnDefinition>>,
72    parse: ColumnDefinitions::parse,
73    constructors: [
74        () => ColumnDefinitions::new,
75        try (String) => |s: String| ColumnDefinitions::parse(&s),
76    ],
77    methods: [
78        fn Add(Ref<ColumnDefinition>) =>
79            |definitions: &ColumnDefinitions, item: Ref<ColumnDefinition>| definitions.add(item),
80    ],
81    // Declared on the base class of the definition lists in the managed original.
82    attributes: [FerroList(Separators = [",", " "])],
83});
84
85ferro_markup_type!(class ItemCollection {
86    namespace: "FerroUI.Controls",
87    handles: [ItemCollection, Option<ItemCollection>],
88    methods: [fn Add(Option<BoxedValue>) -> i32 => |items: &ItemCollection, item: Option<BoxedValue>| items.add(item)],
89});
90
91// The tray icons of an application (the value of `TrayIcon.Icons`).
92ferro_markup_type!(class TrayIcons {
93    namespace: "FerroUI.Controls",
94    handles: [Rc<TrayIcons>, Option<Rc<TrayIcons>>],
95    this: Rc<TrayIcons>,
96    base: FerroList<Ref<TrayIcon>>,
97    constructors: [() => TrayIcons::new],
98    methods: [fn Add(Ref<TrayIcon>) => |icons: &Rc<TrayIcons>, item: Ref<TrayIcon>| icons.add(item)],
99});
100
101ferro_markup_type!(class WindowIcon {
102    namespace: "FerroUI.Controls",
103    handles: [Rc<WindowIcon>, Option<Rc<WindowIcon>>],
104});
105
106/// `ItemsSourceView.Count`: the count does not fit the `int` of markup.
107fn items_source_view_count(view: &Rc<ItemsSourceView>) -> Result<i32, &'static str> {
108    i32::try_from(view.count()).map_err(|_| "The number of items exceeds the range of Int32.")
109}
110
111/// `ItemsSourceView.GetAt`: an index that is not the index of an item is an error.
112fn items_source_view_get_at(view: &Rc<ItemsSourceView>, index: i32) -> Result<Option<BoxedValue>, &'static str> {
113    match usize::try_from(index) {
114        Ok(index) if index < view.count() => Ok(view.get_at(index)),
115        _ => Err("Index was out of range. Must be non-negative and less than the size of the collection."),
116    }
117}
118
119/// `ItemsSourceView.KeyFromIndex`: a source without unique keys, or an index that is not the
120/// index of an item, is an error.
121fn items_source_view_key_from_index(view: &Rc<ItemsSourceView>, index: i32) -> Result<String, &'static str> {
122    if !view.has_key_index_mapping() {
123        return Err("The items source does not implement IKeyIndexMapping.");
124    }
125    match usize::try_from(index) {
126        Ok(item) if item < view.count() => Ok(view.key_from_index(index)),
127        _ => Err("Argument index is invalid."),
128    }
129}
130
131/// `ItemsSourceView.IndexFromKey`: a source without unique keys is an error.
132fn items_source_view_index_from_key(view: &Rc<ItemsSourceView>, key: String) -> Result<i32, &'static str> {
133    if !view.has_key_index_mapping() {
134        return Err("The items source does not implement IKeyIndexMapping.");
135    }
136    Ok(view.index_from_key(&key))
137}
138
139// The view of an items source: upstream's public members, with the public constructor and the
140// unique keys of WinUI's `ItemsSourceView` (DEVIATIONS.md, Items sources). A view is an
141// enumerable itself, so it is assignable to an items source.
142ferro_markup_type!(class ItemsSourceView {
143    namespace: "FerroUI.Controls",
144    handles: [Rc<ItemsSourceView>, Option<Rc<ItemsSourceView>>],
145    this: Rc<ItemsSourceView>,
146    interfaces: [crate::ItemsSource],
147    constructors: [(crate::ItemsSource) => ItemsSourceView::new],
148    properties: [
149        Count: i32 { try_get: items_source_view_count },
150        HasKeyIndexMapping: bool { get: |view: &Rc<ItemsSourceView>| view.has_key_index_mapping() },
151    ],
152    methods: [
153        try fn GetAt(i32) -> Option<BoxedValue> => items_source_view_get_at,
154        fn IndexOf(Option<BoxedValue>) -> i32 => |view: &Rc<ItemsSourceView>, item: Option<BoxedValue>| view.index_of(&item),
155        try fn KeyFromIndex(i32) -> String => items_source_view_key_from_index,
156        try fn IndexFromKey(String) -> i32 => items_source_view_index_from_key,
157    ],
158});
159
160// FerroUI.Controls.Notifications
161
162/// The setter of `Notification.Expiration`: a negative time span is an error, as a negative
163/// delay is upstream when the notification is shown.
164fn set_notification_expiration(
165    notification: &Rc<crate::notifications::Notification>,
166    value: TimeSpan,
167) -> Result<(), &'static str> {
168    match value.to_duration() {
169        Some(expiration) => Ok(notification.set_expiration(expiration)),
170        None => Err("The expiration of a notification cannot be negative."),
171    }
172}
173
174// A notification created in markup (`<Notification Title=".." Message=".."/>`, the content of
175// an action that shows it). `OnClick` and `OnClose` are actions, which markup cannot state, and
176// are not declared.
177ferro_markup_type!(class crate::notifications::Notification as "Notification" {
178    namespace: "FerroUI.Controls.Notifications",
179    handles: [
180        crate::notifications::Notification,
181        Rc<crate::notifications::Notification>,
182        Option<Rc<crate::notifications::Notification>>
183    ],
184    this: Rc<crate::notifications::Notification>,
185    interfaces: [Rc<dyn crate::notifications::INotification>],
186    constructors: [() => crate::notifications::Notification::empty],
187    properties: [
188        Title: Option<String> {
189            get: |n: &Rc<crate::notifications::Notification>| n.title(),
190            set: |n: &Rc<crate::notifications::Notification>, value: Option<String>| n.set_title(value)
191        },
192        Message: Option<String> {
193            get: |n: &Rc<crate::notifications::Notification>| n.message(),
194            set: |n: &Rc<crate::notifications::Notification>, value: Option<String>| n.set_message(value)
195        },
196        Type: crate::notifications::NotificationType {
197            get: |n: &Rc<crate::notifications::Notification>| n.type_(),
198            set: |n: &Rc<crate::notifications::Notification>, value: crate::notifications::NotificationType| n.set_type(value)
199        },
200        Expiration: TimeSpan {
201            get: |n: &Rc<crate::notifications::Notification>| TimeSpan::from(n.expiration()),
202            try_set: set_notification_expiration
203        },
204    ],
205    notify_property_changed: crate::notifications::Notification,
206});
207
208// FerroUI.Controls.Documents
209
210ferro_markup_type!(class InlineCollection {
211    namespace: "FerroUI.Controls.Documents",
212    handles: [InlineCollection, Option<InlineCollection>],
213    base: FerroList<Ref<Inline>>,
214    constructors: [() => InlineCollection::new],
215    methods: [
216        fn Add(Ref<Inline>) => |inlines: &InlineCollection, inline: Ref<Inline>| inlines.add(inline),
217        fn Add(String) => |inlines: &InlineCollection, text: String| inlines.add_text(&text),
218        fn Add(Ref<Control>) => |inlines: &InlineCollection, control: Ref<Control>| inlines.add_control(control),
219    ],
220    attributes: [WhitespaceSignificantCollection],
221});
222
223// FerroUI.Controls.Templates
224
225ferro_markup_type!(class DataTemplates {
226    namespace: "FerroUI.Controls.Templates",
227    handles: [DataTemplates, Option<DataTemplates>],
228    base: FerroList<Rc<dyn IDataTemplate>>,
229    constructors: [() => DataTemplates::new],
230    methods: [
231        // A typed template without a data type is an error, as in the managed original.
232        try fn Add(Rc<dyn IDataTemplate>) => |templates: &DataTemplates, item: Rc<dyn IDataTemplate>| {
233            if item.as_typed_data_template().is_some_and(|typed| typed.data_type().is_none()) {
234                return Err(
235                    "DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use \
236                     ItemTemplate with single template instead.",
237                );
238            }
239            Ok(templates.add(item))
240        },
241    ],
242});
243
244// FerroUI.Controls.Primitives.PopupPositioning
245
246// The parameters of a custom placement as a method named in markup receives them: one shared
247// object, which the method changes (the record of the managed original is a reference type).
248ferro_markup_type!(class CustomPopupPlacement {
249    namespace: "FerroUI.Controls.Primitives.PopupPositioning",
250    handles: [Rc<RefCell<CustomPopupPlacement>>, Option<Rc<RefCell<CustomPopupPlacement>>>],
251    this: Rc<RefCell<CustomPopupPlacement>>,
252    properties: [
253        PopupSize: Size { get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().popup_size() },
254        Deflate: Thickness { get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().deflate() },
255        Target: Ref<Visual> { get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().target() },
256        AnchorRectangle: Rect {
257            get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().anchor_rectangle,
258            set: |placement: &Rc<RefCell<CustomPopupPlacement>>, value: Rect| placement.borrow_mut().anchor_rectangle = value
259        },
260        Anchor: PopupAnchor {
261            get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().anchor(),
262            set: |placement: &Rc<RefCell<CustomPopupPlacement>>, value: PopupAnchor| placement.borrow_mut().set_anchor(value)
263        },
264        Gravity: PopupGravity {
265            get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().gravity(),
266            set: |placement: &Rc<RefCell<CustomPopupPlacement>>, value: PopupGravity| placement.borrow_mut().set_gravity(value)
267        },
268        ConstraintAdjustment: PopupPositionerConstraintAdjustment {
269            get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().constraint_adjustment,
270            set: |placement: &Rc<RefCell<CustomPopupPlacement>>, value: PopupPositionerConstraintAdjustment| {
271                placement.borrow_mut().constraint_adjustment = value
272            }
273        },
274        Offset: Point {
275            get: |placement: &Rc<RefCell<CustomPopupPlacement>>| placement.borrow().offset,
276            set: |placement: &Rc<RefCell<CustomPopupPlacement>>, value: Point| placement.borrow_mut().offset = value
277        },
278    ],
279});
280
281// The delegate type of the `CustomPopupPlacementCallback` properties: a delegate with one
282// `Invoke` method, so that markup accepts the name of a method of the root object with that
283// signature as the value of such a property.
284ferro_markup_type!(class CustomPopupPlacementCallbackValue as "CustomPopupPlacementCallback" {
285    namespace: "FerroUI.Controls.Primitives.PopupPositioning",
286    handles: [CustomPopupPlacementCallbackValue, Option<CustomPopupPlacementCallbackValue>],
287    base: MarkupDelegate,
288    methods: [
289        fn Invoke(Rc<RefCell<CustomPopupPlacement>>) =>
290            |callback: &CustomPopupPlacementCallbackValue, placement: Rc<RefCell<CustomPopupPlacement>>| {
291                (callback.0)(&mut placement.borrow_mut())
292            },
293    ],
294});
295
296/// The callback that calls a method named in markup: the method receives the parameters as
297/// one shared object, and what it leaves in them is the placement.
298fn custom_popup_placement_callback(method: &MarkupDelegate) -> CustomPopupPlacementCallbackValue {
299    let method = method.clone();
300    CustomPopupPlacementCallbackValue(Rc::new(move |placement: &mut CustomPopupPlacement| {
301        let shared = Rc::new(RefCell::new(placement.clone()));
302        method.invoke(&[Some(Rc::new(shared.clone()) as BoxedValue)]);
303        *placement = shared.borrow().clone();
304    }))
305}
306
307// FerroUI.Collections
308
309/// Carries the metadata of the notifying list of items of this crate (the
310/// list type belongs to the base crate).
311#[doc(hidden)]
312pub struct FerroListOf<T>(std::marker::PhantomData<T>);
313
314// The lists the named collections of this crate derive from.
315
316ferro_markup_type!(class FerroListOf<Ref<Control>> as "FerroList`1" {
317    namespace: "FerroUI.Collections",
318    handles: [FerroList<Ref<Control>>, Option<FerroList<Ref<Control>>>],
319    this: FerroList<Ref<Control>>,
320    generic: "FerroList`1" [Ref<Control>],
321    constructors: [() => FerroList::<Ref<Control>>::new],
322    properties: [
323        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
324        Count: i32 { get: |list: &FerroList<Ref<Control>>| list.count() as i32 },
325    ],
326    methods: [fn Add(Ref<Control>) => |list: &FerroList<Ref<Control>>, item: Ref<Control>| list.add(item)],
327});
328
329ferro_markup_type!(class FerroListOf<Ref<Inline>> as "FerroList`1" {
330    namespace: "FerroUI.Collections",
331    handles: [FerroList<Ref<Inline>>, Option<FerroList<Ref<Inline>>>],
332    this: FerroList<Ref<Inline>>,
333    generic: "FerroList`1" [Ref<Inline>],
334    constructors: [() => FerroList::<Ref<Inline>>::new],
335    properties: [
336        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
337        Count: i32 { get: |list: &FerroList<Ref<Inline>>| list.count() as i32 },
338    ],
339    methods: [fn Add(Ref<Inline>) => |list: &FerroList<Ref<Inline>>, item: Ref<Inline>| list.add(item)],
340});
341
342ferro_markup_type!(class FerroListOf<Rc<dyn IDataTemplate>> as "FerroList`1" {
343    namespace: "FerroUI.Collections",
344    handles: [FerroList<Rc<dyn IDataTemplate>>, Option<FerroList<Rc<dyn IDataTemplate>>>],
345    this: FerroList<Rc<dyn IDataTemplate>>,
346    generic: "FerroList`1" [Rc<dyn IDataTemplate>],
347    constructors: [() => FerroList::<Rc<dyn IDataTemplate>>::new],
348    properties: [
349        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
350        Count: i32 { get: |list: &FerroList<Rc<dyn IDataTemplate>>| list.count() as i32 },
351    ],
352    methods: [fn Add(Rc<dyn IDataTemplate>) => |list: &FerroList<Rc<dyn IDataTemplate>>, item: Rc<dyn IDataTemplate>| list.add(item)],
353});
354
355ferro_markup_type!(class FerroListOf<Ref<RowDefinition>> as "FerroList`1" {
356    namespace: "FerroUI.Collections",
357    handles: [FerroList<Ref<RowDefinition>>, Option<FerroList<Ref<RowDefinition>>>],
358    this: FerroList<Ref<RowDefinition>>,
359    generic: "FerroList`1" [Ref<RowDefinition>],
360    constructors: [() => FerroList::<Ref<RowDefinition>>::new],
361    properties: [
362        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
363        Count: i32 { get: |list: &FerroList<Ref<RowDefinition>>| list.count() as i32 },
364    ],
365    methods: [fn Add(Ref<RowDefinition>) => |list: &FerroList<Ref<RowDefinition>>, item: Ref<RowDefinition>| list.add(item)],
366});
367
368ferro_markup_type!(class FerroListOf<Ref<ColumnDefinition>> as "FerroList`1" {
369    namespace: "FerroUI.Collections",
370    handles: [FerroList<Ref<ColumnDefinition>>, Option<FerroList<Ref<ColumnDefinition>>>],
371    this: FerroList<Ref<ColumnDefinition>>,
372    generic: "FerroList`1" [Ref<ColumnDefinition>],
373    constructors: [() => FerroList::<Ref<ColumnDefinition>>::new],
374    properties: [
375        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
376        Count: i32 { get: |list: &FerroList<Ref<ColumnDefinition>>| list.count() as i32 },
377    ],
378    methods: [fn Add(Ref<ColumnDefinition>) => |list: &FerroList<Ref<ColumnDefinition>>, item: Ref<ColumnDefinition>| list.add(item)],
379});
380
381ferro_markup_type!(class FerroListOf<Ref<TrayIcon>> as "FerroList`1" {
382    namespace: "FerroUI.Collections",
383    handles: [FerroList<Ref<TrayIcon>>, Option<FerroList<Ref<TrayIcon>>>],
384    this: FerroList<Ref<TrayIcon>>,
385    generic: "FerroList`1" [Ref<TrayIcon>],
386    constructors: [() => FerroList::<Ref<TrayIcon>>::new],
387    properties: [
388        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
389        Count: i32 { get: |list: &FerroList<Ref<TrayIcon>>| list.count() as i32 },
390    ],
391    methods: [fn Add(Ref<TrayIcon>) => |list: &FerroList<Ref<TrayIcon>>, item: Ref<TrayIcon>| list.add(item)],
392});
393
394// The commands of a command bar.
395ferro_markup_type!(interface dyn crate::ICommandBarElement as "ICommandBarElement" {
396    namespace: "FerroUI.Controls",
397    handles: [Rc<dyn crate::ICommandBarElement>, Option<Rc<dyn crate::ICommandBarElement>>],
398});
399
400ferro_markup_type!(class FerroListOf<Rc<dyn crate::ICommandBarElement>> as "FerroList`1" {
401    namespace: "FerroUI.Collections",
402    handles: [FerroList<Rc<dyn crate::ICommandBarElement>>, Option<FerroList<Rc<dyn crate::ICommandBarElement>>>],
403    this: FerroList<Rc<dyn crate::ICommandBarElement>>,
404    generic: "FerroList`1" [Rc<dyn crate::ICommandBarElement>],
405    constructors: [() => FerroList::<Rc<dyn crate::ICommandBarElement>>::new],
406    properties: [
407        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
408        Count: i32 { get: |list: &FerroList<Rc<dyn crate::ICommandBarElement>>| list.count() as i32 },
409    ],
410    methods: [
411        fn Add(Rc<dyn crate::ICommandBarElement>) =>
412            |list: &FerroList<Rc<dyn crate::ICommandBarElement>>, item: Rc<dyn crate::ICommandBarElement>| list.add(item),
413    ],
414});
415
416// The items of a native menu.
417ferro_markup_type!(class FerroListOf<Ref<NativeMenuItemBase>> as "FerroList`1" {
418    namespace: "FerroUI.Collections",
419    handles: [FerroList<Ref<NativeMenuItemBase>>, Option<FerroList<Ref<NativeMenuItemBase>>>],
420    this: FerroList<Ref<NativeMenuItemBase>>,
421    generic: "FerroList`1" [Ref<NativeMenuItemBase>],
422    constructors: [() => FerroList::<Ref<NativeMenuItemBase>>::new],
423    properties: [
424        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
425        Count: i32 { get: |list: &FerroList<Ref<NativeMenuItemBase>>| list.count() as i32 },
426    ],
427    methods: [
428        fn Add(Ref<NativeMenuItemBase>) =>
429            |list: &FerroList<Ref<NativeMenuItemBase>>, item: Ref<NativeMenuItemBase>| list.add(item),
430    ],
431});
432
433// The pages of a multi page (`MultiPage.Pages`).
434ferro_markup_type!(class FerroListOf<Ref<Page>> as "FerroList`1" {
435    namespace: "FerroUI.Collections",
436    handles: [FerroList<Ref<Page>>, Option<FerroList<Ref<Page>>>],
437    this: FerroList<Ref<Page>>,
438    generic: "FerroList`1" [Ref<Page>],
439    constructors: [() => FerroList::<Ref<Page>>::new],
440    properties: [
441        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
442        Count: i32 { get: |list: &FerroList<Ref<Page>>| list.count() as i32 },
443    ],
444    methods: [fn Add(Ref<Page>) => |list: &FerroList<Ref<Page>>, item: Ref<Page>| list.add(item)],
445});
446
447// The columns of a table view (`TableView.Columns`).
448ferro_markup_type!(class FerroListOf<Ref<TableViewColumn>> as "FerroList`1" {
449    namespace: "FerroUI.Collections",
450    handles: [FerroList<Ref<TableViewColumn>>, Option<FerroList<Ref<TableViewColumn>>>],
451    this: FerroList<Ref<TableViewColumn>>,
452    generic: "FerroList`1" [Ref<TableViewColumn>],
453    constructors: [() => FerroList::<Ref<TableViewColumn>>::new],
454    properties: [
455        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
456        Count: i32 { get: |list: &FerroList<Ref<TableViewColumn>>| list.count() as i32 },
457    ],
458    methods: [fn Add(Ref<TableViewColumn>) => |list: &FerroList<Ref<TableViewColumn>>, item: Ref<TableViewColumn>| list.add(item)],
459});
460
461// The visual state groups of an element (`VisualStateManager.VisualStateGroups`).
462ferro_markup_type!(class FerroListOf<Ref<VisualStateGroup>> as "FerroList`1" {
463    namespace: "FerroUI.Collections",
464    handles: [FerroList<Ref<VisualStateGroup>>, Option<FerroList<Ref<VisualStateGroup>>>],
465    this: FerroList<Ref<VisualStateGroup>>,
466    generic: "FerroList`1" [Ref<VisualStateGroup>],
467    constructors: [() => FerroList::<Ref<VisualStateGroup>>::new],
468    properties: [
469        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
470        Count: i32 { get: |list: &FerroList<Ref<VisualStateGroup>>| list.count() as i32 },
471    ],
472    methods: [fn Add(Ref<VisualStateGroup>) => |list: &FerroList<Ref<VisualStateGroup>>, item: Ref<VisualStateGroup>| list.add(item)],
473});
474
475// The states of a visual state group (`VisualStateGroup.States`).
476ferro_markup_type!(class FerroListOf<Ref<VisualState>> as "FerroList`1" {
477    namespace: "FerroUI.Collections",
478    handles: [FerroList<Ref<VisualState>>, Option<FerroList<Ref<VisualState>>>],
479    this: FerroList<Ref<VisualState>>,
480    generic: "FerroList`1" [Ref<VisualState>],
481    constructors: [() => FerroList::<Ref<VisualState>>::new],
482    properties: [
483        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
484        Count: i32 { get: |list: &FerroList<Ref<VisualState>>| list.count() as i32 },
485    ],
486    methods: [fn Add(Ref<VisualState>) => |list: &FerroList<Ref<VisualState>>, item: Ref<VisualState>| list.add(item)],
487});
488
489// The triggers of a visual state (`VisualState.StateTriggers`).
490ferro_markup_type!(class FerroListOf<Ref<StateTriggerBase>> as "FerroList`1" {
491    namespace: "FerroUI.Collections",
492    handles: [FerroList<Ref<StateTriggerBase>>, Option<FerroList<Ref<StateTriggerBase>>>],
493    this: FerroList<Ref<StateTriggerBase>>,
494    generic: "FerroList`1" [Ref<StateTriggerBase>],
495    constructors: [() => FerroList::<Ref<StateTriggerBase>>::new],
496    properties: [
497        Capacity: i32 { get: list_capacity, try_set: set_list_capacity },
498        Count: i32 { get: |list: &FerroList<Ref<StateTriggerBase>>| list.count() as i32 },
499    ],
500    methods: [fn Add(Ref<StateTriggerBase>) => |list: &FerroList<Ref<StateTriggerBase>>, item: Ref<StateTriggerBase>| list.add(item)],
501});
502
503// The arguments of `ScrollViewer.ScrollChanged` and the type of its routed event. `classes.rs`
504// declares the arguments of the routed events of the classes whose `markup:` part it generates;
505// the scroll viewer declares its part in its own file (with its public methods), so the two
506// declarations its event needs are written here.
507
508ferro_markup_type!(class RoutedEventOf<ScrollChangedEventArgs> as "RoutedEvent`1" {
509    namespace: "FerroUI.Interactivity",
510    handles: [RoutedEvent<ScrollChangedEventArgs>, Option<RoutedEvent<ScrollChangedEventArgs>>],
511    base: RoutedEvent,
512    generic: "RoutedEvent`1" [ScrollChangedEventArgs],
513});
514
515/// The arguments of a routed event as the arguments of `ScrollChanged`.
516fn scroll_changed_event_args<R>(
517    e: &Rc<dyn IRoutedEventArgs>,
518    f: impl FnOnce(&ScrollChangedEventArgs) -> R,
519) -> Result<R, String> {
520    match e
521        .query_args(std::any::TypeId::of::<ScrollChangedEventArgs>())
522        .and_then(|args| args.downcast_ref::<ScrollChangedEventArgs>())
523    {
524        Some(args) => Ok(f(args)),
525        None => Err(format!("The arguments are not {}.", std::any::type_name::<ScrollChangedEventArgs>())),
526    }
527}
528
529ferro_markup_type!(class ScrollChangedEventArgs {
530    namespace: "FerroUI.Controls",
531    handles: [ScrollChangedEventArgs],
532    this: Rc<dyn IRoutedEventArgs>,
533    base: Rc<dyn IRoutedEventArgs>,
534    properties: [
535        ExtentDelta: Vector {
536            try_get: |e: &Rc<dyn IRoutedEventArgs>| scroll_changed_event_args(e, |e| e.extent_delta())
537        },
538        OffsetDelta: Vector {
539            try_get: |e: &Rc<dyn IRoutedEventArgs>| scroll_changed_event_args(e, |e| e.offset_delta())
540        },
541        ViewportDelta: Vector {
542            try_get: |e: &Rc<dyn IRoutedEventArgs>| scroll_changed_event_args(e, |e| e.viewport_delta())
543        },
544    ],
545});
546
547/// The types declared in this file.
548pub(super) const TYPES: &[&MarkupType] = &[
549    <crate::notifications::Notification as MarkupTyped>::MARKUP,
550    <RoutedEventOf<ScrollChangedEventArgs> as MarkupTyped>::MARKUP,
551    <ScrollChangedEventArgs as MarkupTyped>::MARKUP,
552    <dyn crate::ICommandBarElement as MarkupTyped>::MARKUP,
553    <FerroListOf<Rc<dyn crate::ICommandBarElement>> as MarkupTyped>::MARKUP,
554    <Controls as MarkupTyped>::MARKUP,
555    <RowDefinitions as MarkupTyped>::MARKUP,
556    <ColumnDefinitions as MarkupTyped>::MARKUP,
557    <ItemCollection as MarkupTyped>::MARKUP,
558    <TrayIcons as MarkupTyped>::MARKUP,
559    <FerroListOf<Ref<TrayIcon>> as MarkupTyped>::MARKUP,
560    <WindowIcon as MarkupTyped>::MARKUP,
561    <ItemsSourceView as MarkupTyped>::MARKUP,
562    <InlineCollection as MarkupTyped>::MARKUP,
563    <DataTemplates as MarkupTyped>::MARKUP,
564    <CustomPopupPlacement as MarkupTyped>::MARKUP,
565    <CustomPopupPlacementCallbackValue as MarkupTyped>::MARKUP,
566    <FerroListOf<Ref<Control>> as MarkupTyped>::MARKUP,
567    <FerroListOf<Ref<Inline>> as MarkupTyped>::MARKUP,
568    <FerroListOf<Rc<dyn IDataTemplate>> as MarkupTyped>::MARKUP,
569    <FerroListOf<Ref<RowDefinition>> as MarkupTyped>::MARKUP,
570    <FerroListOf<Ref<ColumnDefinition>> as MarkupTyped>::MARKUP,
571    <FerroListOf<Ref<NativeMenuItemBase>> as MarkupTyped>::MARKUP,
572    <FerroListOf<Ref<Page>> as MarkupTyped>::MARKUP,
573    <FerroListOf<Ref<TableViewColumn>> as MarkupTyped>::MARKUP,
574    <FerroListOf<Ref<VisualStateGroup>> as MarkupTyped>::MARKUP,
575    <FerroListOf<Ref<VisualState>> as MarkupTyped>::MARKUP,
576    <FerroListOf<Ref<StateTriggerBase>> as MarkupTyped>::MARKUP,
577];
578
579/// Registers the nullable forms of the types that can be held in untyped
580/// values with the untyped value conversions of the current thread.
581pub(super) fn register_value_types() {
582    ValueTypes::register_cast::<RoutedEvent<ScrollChangedEventArgs>, RoutedEvent>(|event| event.as_routed_event());
583    ValueTypes::register_nullable::<Rc<dyn crate::ICommandBarElement>>();
584    ValueTypes::register_nullable::<FerroList<Rc<dyn crate::ICommandBarElement>>>();
585    ValueTypes::register_nullable::<Controls>();
586    ValueTypes::register_nullable::<RowDefinitions>();
587    ValueTypes::register_nullable::<ColumnDefinitions>();
588    ValueTypes::register_nullable::<ItemCollection>();
589    ValueTypes::register_nullable::<InlineCollection>();
590    ValueTypes::register_nullable::<DataTemplates>();
591    ValueTypes::register_nullable::<FerroList<Ref<Control>>>();
592    ValueTypes::register_nullable::<FerroList<Ref<Inline>>>();
593    ValueTypes::register_nullable::<FerroList<Rc<dyn IDataTemplate>>>();
594    ValueTypes::register_nullable::<FerroList<Ref<RowDefinition>>>();
595    ValueTypes::register_nullable::<FerroList<Ref<ColumnDefinition>>>();
596    ValueTypes::register_nullable::<FerroList<Ref<NativeMenuItemBase>>>();
597    ValueTypes::register_nullable::<FerroList<Ref<Page>>>();
598    ValueTypes::register_nullable::<FerroList<Ref<TableViewColumn>>>();
599    ValueTypes::register_nullable::<FerroList<Ref<VisualStateGroup>>>();
600    ValueTypes::register_nullable::<FerroList<Ref<VisualState>>>();
601    ValueTypes::register_nullable::<FerroList<Ref<StateTriggerBase>>>();
602    ValueTypes::register_nullable::<Rc<WindowIcon>>();
603    ValueTypes::register_nullable::<Rc<RefCell<CustomPopupPlacement>>>();
604    ValueTypes::register_nullable::<CustomPopupPlacementCallbackValue>();
605    // The delegate of a method named in markup is a callback: the value of a
606    // `CustomPopupPlacementCallback` property.
607    ValueTypes::register_cast::<MarkupDelegate, CustomPopupPlacementCallbackValue>(custom_popup_placement_callback);
608    // A named collection is the list it derives from: the same list, so that the members
609    // of the list (`Capacity`) are reached through the collection.
610    // The collection handles of items controls: a property that holds one takes the handle
611    // (and null) from an untyped value, and a binding delivers a handle to it.
612    ValueTypes::register_nullable::<crate::ItemsSource>();
613    ValueTypes::register_nullable::<crate::primitives::SelectedItemsList>();
614    // A notification is a reference type, boxed as itself, and a notification of the contract.
615    crate::notifications::Notification::ensure_registered();
616    crate::ItemsSource::register_binding_conversion::<crate::ItemsSource>();
617    // The selected items of a selecting control are a list (upstream `IList`), so an
618    // enumerable: the handle shares the list, and a binding delivers it to an items source
619    // (`{Binding #list.SelectedItems}` as the items of an action).
620    ValueTypes::register_cast::<crate::primitives::SelectedItemsList, crate::ItemsSource>(|list| {
621        crate::ItemsSource::from(list.0.clone())
622    });
623    ValueTypes::register_conversion::<crate::primitives::SelectedItemsList, Option<crate::ItemsSource>>(|list| {
624        Some(Some(crate::ItemsSource::from(list.0.clone())))
625    });
626    // A font collection is a read-only list of font families (`IReadOnlyList<FontFamily>` in
627    // the managed original), which a binding delivers to an items source
628    // (`{Binding SystemFonts, Source={x:Static FontManager.Current}}`): the items are the
629    // font families of the collection.
630    crate::ItemsSource::register_binding_conversion_with::<Rc<dyn ferroui_base::media::fonts::IFontCollection>>(|fonts| {
631        crate::ItemsSource::from_values(fonts.font_families())
632    });
633    // A list of untyped items is an enumerable (a list of the runtime library the run-time
634    // loader creates for markup, `List<T>` or `ArrayList`): the handle shares the list.
635    ValueTypes::register_cast::<Rc<FerroList<Option<BoxedValue>>>, crate::ItemsSource>(|list| {
636        crate::ItemsSource::from(list.clone())
637    });
638    // A vector of untyped values is an enumerable too (the errors of a control, an
639    // `IEnumerable<object>` in the managed original, which the template of the errors binds
640    // to the items of an items control): the handle holds the items of the vector. The
641    // conversions are what a binding delivers the vector with, to the handle and to its
642    // nullable form.
643    ValueTypes::register_cast::<Vec<BoxedValue>, crate::ItemsSource>(|items| crate::ItemsSource::from(items.clone()));
644    ValueTypes::register_conversion::<Vec<BoxedValue>, Option<crate::ItemsSource>>(|items| {
645        Some(Some(crate::ItemsSource::from(items.clone())))
646    });
647    ValueTypes::register_cast::<Vec<Option<BoxedValue>>, crate::ItemsSource>(|items| {
648        crate::ItemsSource::from(items.clone())
649    });
650    ValueTypes::register_conversion::<Vec<Option<BoxedValue>>, Option<crate::ItemsSource>>(|items| {
651        Some(Some(crate::ItemsSource::from(items.clone())))
652    });
653    // A view of an items source is an enumerable too (upstream `ItemsSourceView :
654    // IReadOnlyList<object?>`): the handle is the view itself, which an items control then
655    // reads through (`ItemsSourceView::get_or_create` returns it as it is).
656    ValueTypes::register_nullable::<Rc<ItemsSourceView>>();
657    ValueTypes::register_cast::<Rc<ItemsSourceView>, crate::ItemsSource>(|view| crate::ItemsSource::from(view.clone()));
658    ValueTypes::register_conversion::<Rc<ItemsSourceView>, Option<crate::ItemsSource>>(|view| {
659        Some(Some(crate::ItemsSource::from(view.clone())))
660    });
661    ValueTypes::register_nullable::<Rc<TrayIcons>>();
662    ValueTypes::register_nullable::<FerroList<Ref<TrayIcon>>>();
663    ValueTypes::register_cast::<Rc<TrayIcons>, FerroList<Ref<TrayIcon>>>(|c| (***c).clone());
664    ValueTypes::register_cast::<Controls, FerroList<Ref<Control>>>(|c| (**c).clone());
665    ValueTypes::register_cast::<InlineCollection, FerroList<Ref<Inline>>>(|c| (**c).clone());
666    ValueTypes::register_cast::<DataTemplates, FerroList<Rc<dyn IDataTemplate>>>(|c| (**c).clone());
667    ValueTypes::register_cast::<RowDefinitions, FerroList<Ref<RowDefinition>>>(|c| (***c).clone());
668    ValueTypes::register_cast::<ColumnDefinitions, FerroList<Ref<ColumnDefinition>>>(|c| (***c).clone());
669    // The named collections whose Rust type dereferences to the list: generated code
670    // passes a reference to such a collection where a member of the list takes the list.
671    ValueTypes::register_deref::<Controls, FerroList<Ref<Control>>>(|c| c);
672    ValueTypes::register_deref::<InlineCollection, FerroList<Ref<Inline>>>(|c| c);
673    ValueTypes::register_deref::<DataTemplates, FerroList<Rc<dyn IDataTemplate>>>(|c| c);
674    ValueTypes::register_deref::<RowDefinitions, FerroList<Ref<RowDefinition>>>(|c| c);
675    ValueTypes::register_deref::<ColumnDefinitions, FerroList<Ref<ColumnDefinition>>>(|c| c);
676}