Skip to main content

azul_layout/widgets/
list_view.rs

1//! Native list view widget with column headers, row selection, and sorting indicators.
2
3use alloc::vec::Vec;
4
5use azul_core::{
6    callbacks::{CoreCallback, CoreCallbackData, Update},
7    dom::{
8        Dom, DomVec, EventFilter, HoverEventFilter, IdOrClass, IdOrClass::Class, IdOrClassVec,
9        TabIndex,
10    },
11    geom::{LogicalPosition, LogicalSize},
12    menu::{Menu, OptionMenu},
13    refany::{OptionRefAny, RefAny},
14};
15#[allow(clippy::wildcard_imports)] // widget/render module pulls in the css property/value types it builds with
16use azul_css::{
17    corety::OptionUsize,
18    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
19    props::{
20        basic::*,
21        layout::*,
22        property::{CssProperty, *},
23        style::*,
24    },
25    *,
26};
27use azul_css::css::BoxOrStatic;
28
29use crate::callbacks::{Callback, CallbackInfo};
30
31const STRING_16146701490593874959: AzString = AzString::from_const_str("system:ui");
32const STYLE_BACKGROUND_CONTENT_661302523448178568_ITEMS: &[StyleBackgroundContent] =
33    &[StyleBackgroundContent::Color(ColorU {
34        r: 209,
35        g: 232,
36        b: 255,
37        a: 255,
38    })];
39const STYLE_BACKGROUND_CONTENT_2444935983575427872_ITEMS: &[StyleBackgroundContent] =
40    &[StyleBackgroundContent::Color(ColorU {
41        r: 252,
42        g: 252,
43        b: 252,
44        a: 255,
45    })];
46const STYLE_BACKGROUND_CONTENT_3010057533077499049_ITEMS: &[StyleBackgroundContent] =
47    &[StyleBackgroundContent::Color(ColorU {
48        r: 229,
49        g: 243,
50        b: 251,
51        a: 255,
52    })];
53const STYLE_BACKGROUND_CONTENT_3839348353894170136_ITEMS: &[StyleBackgroundContent] =
54    &[StyleBackgroundContent::Color(ColorU {
55        r: 249,
56        g: 250,
57        b: 251,
58        a: 255,
59    })];
60const STYLE_BACKGROUND_CONTENT_6112684430356720596_ITEMS: &[StyleBackgroundContent] =
61    &[StyleBackgroundContent::LinearGradient(LinearGradient {
62        direction: Direction::FromTo(DirectionCorners {
63            dir_from: DirectionCorner::Top,
64            dir_to: DirectionCorner::Bottom,
65        }),
66        extend_mode: ExtendMode::Clamp,
67        stops: NormalizedLinearColorStopVec::from_const_slice(
68            LINEAR_COLOR_STOP_10827796861537038040_ITEMS,
69        ),
70    })];
71const STYLE_BACKGROUND_CONTENT_7422581697888665934_ITEMS: &[StyleBackgroundContent] =
72    &[StyleBackgroundContent::LinearGradient(LinearGradient {
73        direction: Direction::FromTo(DirectionCorners {
74            dir_from: DirectionCorner::Top,
75            dir_to: DirectionCorner::Bottom,
76        }),
77        extend_mode: ExtendMode::Clamp,
78        stops: NormalizedLinearColorStopVec::from_const_slice(
79            LINEAR_COLOR_STOP_513857305091467054_ITEMS,
80        ),
81    })];
82const STYLE_BACKGROUND_CONTENT_11062356617965867290_ITEMS: &[StyleBackgroundContent] =
83    &[StyleBackgroundContent::Color(ColorU {
84        r: 240,
85        g: 240,
86        b: 240,
87        a: 255,
88    })];
89const STYLE_BACKGROUND_CONTENT_11098930083828139815_ITEMS: &[StyleBackgroundContent] =
90    &[StyleBackgroundContent::Color(ColorU {
91        r: 184,
92        g: 224,
93        b: 243,
94        a: 255,
95    })];
96const STYLE_TRANSFORM_6162542744002865382_ITEMS: &[StyleTransform] =
97    &[StyleTransform::Translate(StyleTransformTranslate2D {
98        x: PixelValue::const_px(7),
99        y: PixelValue::const_px(0),
100    })];
101const STYLE_TRANSFORM_16978981723642914576_ITEMS: &[StyleTransform] =
102    &[StyleTransform::Rotate(AngleValue::const_deg(45))];
103const STYLE_TRANSFORM_17732691695785266054_ITEMS: &[StyleTransform] = &[
104    StyleTransform::Rotate(AngleValue::const_deg(315)),
105    StyleTransform::Translate(StyleTransformTranslate2D {
106        x: PixelValue::const_px(0),
107        y: PixelValue::const_px(2),
108    }),
109];
110const STYLE_FONT_FAMILY_8122988506401935406_ITEMS: &[StyleFontFamily] =
111    &[StyleFontFamily::System(STRING_16146701490593874959)];
112const LINEAR_COLOR_STOP_513857305091467054_ITEMS: &[NormalizedLinearColorStop] = &[
113    NormalizedLinearColorStop {
114        offset: PercentageValue::const_new(0),
115        color: ColorOrSystem::color(ColorU {
116            r: 255,
117            g: 255,
118            b: 255,
119            a: 255,
120        }),
121    },
122    NormalizedLinearColorStop {
123        offset: PercentageValue::const_new(50),
124        color: ColorOrSystem::color(ColorU {
125            r: 255,
126            g: 255,
127            b: 255,
128            a: 255,
129        }),
130    },
131    NormalizedLinearColorStop {
132        offset: PercentageValue::const_new(51),
133        color: ColorOrSystem::color(ColorU {
134            r: 247,
135            g: 248,
136            b: 250,
137            a: 255,
138        }),
139    },
140    NormalizedLinearColorStop {
141        offset: PercentageValue::const_new(100),
142        color: ColorOrSystem::color(ColorU {
143            r: 243,
144            g: 244,
145            b: 246,
146            a: 255,
147        }),
148    },
149];
150const LINEAR_COLOR_STOP_10827796861537038040_ITEMS: &[NormalizedLinearColorStop] = &[
151    NormalizedLinearColorStop {
152        offset: PercentageValue::const_new(0),
153        color: ColorOrSystem::color(ColorU {
154            r: 247,
155            g: 252,
156            b: 254,
157            a: 255,
158        }),
159    },
160    NormalizedLinearColorStop {
161        offset: PercentageValue::const_new(50),
162        color: ColorOrSystem::color(ColorU {
163            r: 247,
164            g: 252,
165            b: 254,
166            a: 255,
167        }),
168    },
169    NormalizedLinearColorStop {
170        offset: PercentageValue::const_new(51),
171        color: ColorOrSystem::color(ColorU {
172            r: 232,
173            g: 246,
174            b: 254,
175            a: 255,
176        }),
177    },
178    NormalizedLinearColorStop {
179        offset: PercentageValue::const_new(100),
180        color: ColorOrSystem::color(ColorU {
181            r: 206,
182            g: 231,
183            b: 244,
184            a: 255,
185        }),
186    },
187];
188
189const CSS_MATCH_1085706216385961159_PROPERTIES: &[CssPropertyWithConditions] = &[
190    // .__azul_native-list-header-arrow-down
191    CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
192        StyleTransformVec::from_const_slice(STYLE_TRANSFORM_6162542744002865382_ITEMS),
193    ))),
194    CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
195        LayoutPosition::Absolute,
196    ))),
197    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
198        LayoutPaddingRight {
199            inner: PixelValue::const_px(3),
200        },
201    ))),
202    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
203        LayoutPaddingLeft {
204            inner: PixelValue::const_px(3),
205        },
206    ))),
207    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
208        LayoutPaddingBottom {
209            inner: PixelValue::const_px(3),
210        },
211    ))),
212    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
213        LayoutPaddingTop {
214            inner: PixelValue::const_px(3),
215        },
216    ))),
217    CssPropertyWithConditions::simple(CssProperty::JustifyContent(
218        LayoutJustifyContentValue::Exact(LayoutJustifyContent::Center),
219    )),
220    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
221        LayoutFlexDirection::Row,
222    ))),
223];
224const CSS_MATCH_1085706216385961159: CssPropertyWithConditionsVec =
225    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1085706216385961159_PROPERTIES);
226
227const CSS_MATCH_12498280255863106397_PROPERTIES: &[CssPropertyWithConditions] = &[
228    // .__azul_native-list-header-item:hover
229    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
230        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
231            inner: PixelValue::const_px(1),
232        }),
233    )),
234    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
235        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
236            inner: BorderStyle::Solid,
237        }),
238    )),
239    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
240        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
241            inner: ColorU {
242                r: 154,
243                g: 223,
244                b: 254,
245                a: 255,
246            },
247        }),
248    )),
249    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
250        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
251            STYLE_BACKGROUND_CONTENT_6112684430356720596_ITEMS,
252        )),
253    )),
254    // .__azul_native-list-header-item:active
255    CssPropertyWithConditions::on_active(CssProperty::BoxShadowBottom(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
256        StyleBoxShadow {
257            offset_x: PixelValueNoPercent {
258                inner: PixelValue::const_px(0),
259            },
260            offset_y: PixelValueNoPercent {
261                inner: PixelValue::const_px(0),
262            },
263            color: ColorU {
264                r: 206,
265                g: 231,
266                b: 244,
267                a: 255,
268            },
269            blur_radius: PixelValueNoPercent {
270                inner: PixelValue::const_px(5),
271            },
272            spread_radius: PixelValueNoPercent {
273                inner: PixelValue::const_px(0),
274            },
275            clip_mode: BoxShadowClipMode::Inset,
276        },
277    )))),
278    CssPropertyWithConditions::on_active(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
279        StyleBoxShadow {
280            offset_x: PixelValueNoPercent {
281                inner: PixelValue::const_px(0),
282            },
283            offset_y: PixelValueNoPercent {
284                inner: PixelValue::const_px(0),
285            },
286            color: ColorU {
287                r: 206,
288                g: 231,
289                b: 244,
290                a: 255,
291            },
292            blur_radius: PixelValueNoPercent {
293                inner: PixelValue::const_px(5),
294            },
295            spread_radius: PixelValueNoPercent {
296                inner: PixelValue::const_px(0),
297            },
298            clip_mode: BoxShadowClipMode::Inset,
299        },
300    )))),
301    CssPropertyWithConditions::on_active(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
302        StyleBoxShadow {
303            offset_x: PixelValueNoPercent {
304                inner: PixelValue::const_px(0),
305            },
306            offset_y: PixelValueNoPercent {
307                inner: PixelValue::const_px(0),
308            },
309            color: ColorU {
310                r: 206,
311                g: 231,
312                b: 244,
313                a: 255,
314            },
315            blur_radius: PixelValueNoPercent {
316                inner: PixelValue::const_px(5),
317            },
318            spread_radius: PixelValueNoPercent {
319                inner: PixelValue::const_px(0),
320            },
321            clip_mode: BoxShadowClipMode::Inset,
322        },
323    )))),
324    CssPropertyWithConditions::on_active(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
325        StyleBoxShadow {
326            offset_x: PixelValueNoPercent {
327                inner: PixelValue::const_px(0),
328            },
329            offset_y: PixelValueNoPercent {
330                inner: PixelValue::const_px(0),
331            },
332            color: ColorU {
333                r: 206,
334                g: 231,
335                b: 244,
336                a: 255,
337            },
338            blur_radius: PixelValueNoPercent {
339                inner: PixelValue::const_px(5),
340            },
341            spread_radius: PixelValueNoPercent {
342                inner: PixelValue::const_px(0),
343            },
344            clip_mode: BoxShadowClipMode::Inset,
345        },
346    )))),
347    CssPropertyWithConditions::on_active(CssProperty::BorderBottomWidth(
348        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
349            inner: PixelValue::const_px(1),
350        }),
351    )),
352    CssPropertyWithConditions::on_active(CssProperty::BorderLeftWidth(
353        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
354            inner: PixelValue::const_px(1),
355        }),
356    )),
357    CssPropertyWithConditions::on_active(CssProperty::BorderRightWidth(
358        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
359            inner: PixelValue::const_px(1),
360        }),
361    )),
362    CssPropertyWithConditions::on_active(CssProperty::BorderTopWidth(
363        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
364            inner: PixelValue::const_px(1),
365        }),
366    )),
367    CssPropertyWithConditions::on_active(CssProperty::BorderBottomStyle(
368        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
369            inner: BorderStyle::Solid,
370        }),
371    )),
372    CssPropertyWithConditions::on_active(CssProperty::BorderLeftStyle(
373        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
374            inner: BorderStyle::Solid,
375        }),
376    )),
377    CssPropertyWithConditions::on_active(CssProperty::BorderRightStyle(
378        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
379            inner: BorderStyle::Solid,
380        }),
381    )),
382    CssPropertyWithConditions::on_active(CssProperty::BorderTopStyle(
383        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
384            inner: BorderStyle::Solid,
385        }),
386    )),
387    CssPropertyWithConditions::on_active(CssProperty::BorderBottomColor(
388        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
389            inner: ColorU {
390                r: 194,
391                g: 205,
392                b: 219,
393                a: 255,
394            },
395        }),
396    )),
397    CssPropertyWithConditions::on_active(CssProperty::BorderLeftColor(
398        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
399            inner: ColorU {
400                r: 194,
401                g: 205,
402                b: 219,
403                a: 255,
404            },
405        }),
406    )),
407    CssPropertyWithConditions::on_active(CssProperty::BorderRightColor(
408        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
409            inner: ColorU {
410                r: 194,
411                g: 205,
412                b: 219,
413                a: 255,
414            },
415        }),
416    )),
417    CssPropertyWithConditions::on_active(CssProperty::BorderTopColor(
418        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
419            inner: ColorU {
420                r: 194,
421                g: 205,
422                b: 219,
423                a: 255,
424            },
425        }),
426    )),
427    CssPropertyWithConditions::on_active(CssProperty::BackgroundContent(
428        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
429            STYLE_BACKGROUND_CONTENT_3839348353894170136_ITEMS,
430        )),
431    )),
432    // .__azul_native-list-header-item
433    CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
434        LayoutPosition::Relative,
435    ))),
436    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
437        LayoutPaddingLeft {
438            inner: PixelValue::const_px(7),
439        },
440    ))),
441    CssPropertyWithConditions::simple(CssProperty::MinWidth(LayoutMinWidthValue::Exact(
442        LayoutMinWidth {
443            inner: PixelValue::const_px(100),
444        },
445    ))),
446    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
447        LayoutFlexDirection::Column,
448    ))),
449    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
450        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
451            inner: PixelValue::const_px(1),
452        }),
453    )),
454    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
455        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
456            inner: BorderStyle::Solid,
457        }),
458    )),
459    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
460        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
461            inner: ColorU {
462                r: 243,
463                g: 244,
464                b: 246,
465                a: 255,
466            },
467        }),
468    )),
469];
470const CSS_MATCH_12498280255863106397: CssPropertyWithConditionsVec =
471    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12498280255863106397_PROPERTIES);
472
473const CSS_MATCH_12980082330151137475_PROPERTIES: &[CssPropertyWithConditions] = &[
474    // .__azul_native-list-rows-row-cell
475    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
476        LayoutPaddingLeft {
477            inner: PixelValue::const_px(7),
478        },
479    ))),
480    CssPropertyWithConditions::simple(CssProperty::MinWidth(LayoutMinWidthValue::Exact(
481        LayoutMinWidth {
482            inner: PixelValue::const_px(100),
483        },
484    ))),
485    CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
486        StyleFontSize {
487            inner: PixelValue::const_px(11),
488        },
489    ))),
490    CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
491        StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
492    ))),
493];
494const CSS_MATCH_12980082330151137475: CssPropertyWithConditionsVec =
495    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_12980082330151137475_PROPERTIES);
496
497const CSS_MATCH_13758717721055992976_PROPERTIES: &[CssPropertyWithConditions] = &[
498    // .__azul_native-list-header-arrow-down-inner
499    CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
500        LayoutWidth::Px(PixelValue::const_px(6)),
501    ))),
502    CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
503        StyleTransformVec::from_const_slice(STYLE_TRANSFORM_16978981723642914576_ITEMS),
504    ))),
505    CssPropertyWithConditions::simple(CssProperty::OverflowY(LayoutOverflowValue::Exact(
506        LayoutOverflow::Hidden,
507    ))),
508    CssPropertyWithConditions::simple(CssProperty::OverflowX(LayoutOverflowValue::Exact(
509        LayoutOverflow::Hidden,
510    ))),
511    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
512        LayoutHeight::Px(PixelValue::const_px(6)),
513    ))),
514];
515const CSS_MATCH_13758717721055992976: CssPropertyWithConditionsVec =
516    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_13758717721055992976_PROPERTIES);
517
518const CSS_MATCH_15295293133676720691_PROPERTIES: &[CssPropertyWithConditions] = &[
519    // .__azul_native-list-header-dragwidth-drag
520    CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
521        LayoutWidth::Px(PixelValue::const_px(2)),
522    ))),
523    CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
524        LayoutPosition::Absolute,
525    ))),
526];
527const CSS_MATCH_15295293133676720691: CssPropertyWithConditionsVec =
528    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_15295293133676720691_PROPERTIES);
529
530const CSS_MATCH_15315949193378715186_PROPERTIES: &[CssPropertyWithConditions] = &[
531    // .__azul_native-list-header
532    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
533        LayoutHeight::Px(PixelValue::const_px(25)),
534    ))),
535    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
536        LayoutFlexDirection::Row,
537    ))),
538    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
539        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
540            STYLE_BACKGROUND_CONTENT_7422581697888665934_ITEMS,
541        )),
542    )),
543];
544const CSS_MATCH_15315949193378715186: CssPropertyWithConditionsVec =
545    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_15315949193378715186_PROPERTIES);
546
547const CSS_MATCH_15673486787900743642_PROPERTIES: &[CssPropertyWithConditions] = &[
548    // .__azul_native-list-header .__azul_native-list-header-item p
549    CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
550        StyleFontSize {
551            inner: PixelValue::const_px(11),
552        },
553    ))),
554    CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
555        StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
556    ))),
557    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
558        LayoutFlexGrow {
559            inner: FloatValue::const_new(1),
560        },
561    ))),
562    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
563        LayoutFlexDirection::Column,
564    ))),
565    CssPropertyWithConditions::simple(CssProperty::TextColor(StyleTextColorValue::Exact(
566        StyleTextColor {
567            inner: ColorU {
568                r: 0,
569                g: 0,
570                b: 0,
571                a: 255,
572            },
573        },
574    ))),
575    CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
576        LayoutAlignItems::Center,
577    ))),
578];
579const CSS_MATCH_15673486787900743642: CssPropertyWithConditionsVec =
580    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_15673486787900743642_PROPERTIES);
581
582const CSS_MATCH_1574792189506859253_PROPERTIES: &[CssPropertyWithConditions] = &[
583    // .__azul_native-list-header-arrow-down-inner-deco
584    CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
585        LayoutWidth::Px(PixelValue::const_px(12)),
586    ))),
587    CssPropertyWithConditions::simple(CssProperty::Transform(StyleTransformVecValue::Exact(
588        StyleTransformVec::from_const_slice(STYLE_TRANSFORM_17732691695785266054_ITEMS),
589    ))),
590    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
591        LayoutHeight::Px(PixelValue::const_px(12)),
592    ))),
593    CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
594        StyleBoxShadow {
595            offset_x: PixelValueNoPercent {
596                inner: PixelValue::const_px(3),
597            },
598            offset_y: PixelValueNoPercent {
599                inner: PixelValue::const_px(3),
600            },
601            color: ColorU {
602                r: 60,
603                g: 94,
604                b: 114,
605                a: 255,
606            },
607            blur_radius: PixelValueNoPercent {
608                inner: PixelValue::const_px(10),
609            },
610            spread_radius: PixelValueNoPercent {
611                inner: PixelValue::const_px(0),
612            },
613            clip_mode: BoxShadowClipMode::Inset,
614        },
615    )))),
616    CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
617        StyleBoxShadow {
618            offset_x: PixelValueNoPercent {
619                inner: PixelValue::const_px(3),
620            },
621            offset_y: PixelValueNoPercent {
622                inner: PixelValue::const_px(3),
623            },
624            color: ColorU {
625                r: 60,
626                g: 94,
627                b: 114,
628                a: 255,
629            },
630            blur_radius: PixelValueNoPercent {
631                inner: PixelValue::const_px(10),
632            },
633            spread_radius: PixelValueNoPercent {
634                inner: PixelValue::const_px(0),
635            },
636            clip_mode: BoxShadowClipMode::Inset,
637        },
638    )))),
639    CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
640        StyleBoxShadow {
641            offset_x: PixelValueNoPercent {
642                inner: PixelValue::const_px(3),
643            },
644            offset_y: PixelValueNoPercent {
645                inner: PixelValue::const_px(3),
646            },
647            color: ColorU {
648                r: 60,
649                g: 94,
650                b: 114,
651                a: 255,
652            },
653            blur_radius: PixelValueNoPercent {
654                inner: PixelValue::const_px(10),
655            },
656            spread_radius: PixelValueNoPercent {
657                inner: PixelValue::const_px(0),
658            },
659            clip_mode: BoxShadowClipMode::Inset,
660        },
661    )))),
662    CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(StyleBoxShadowValue::Exact(BoxOrStatic::Static(&
663        StyleBoxShadow {
664            offset_x: PixelValueNoPercent {
665                inner: PixelValue::const_px(3),
666            },
667            offset_y: PixelValueNoPercent {
668                inner: PixelValue::const_px(3),
669            },
670            color: ColorU {
671                r: 60,
672                g: 94,
673                b: 114,
674                a: 255,
675            },
676            blur_radius: PixelValueNoPercent {
677                inner: PixelValue::const_px(10),
678            },
679            spread_radius: PixelValueNoPercent {
680                inner: PixelValue::const_px(0),
681            },
682            clip_mode: BoxShadowClipMode::Inset,
683        },
684    )))),
685];
686const CSS_MATCH_1574792189506859253: CssPropertyWithConditionsVec =
687    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_1574792189506859253_PROPERTIES);
688
689const CSS_MATCH_17553577885456905601_PROPERTIES: &[CssPropertyWithConditions] = &[
690    // .__azul_native_list-container
691    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
692        LayoutFlexGrow {
693            inner: FloatValue::const_new(1),
694        },
695    ))),
696    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
697        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
698            STYLE_BACKGROUND_CONTENT_2444935983575427872_ITEMS,
699        )),
700    )),
701];
702const CSS_MATCH_17553577885456905601: CssPropertyWithConditionsVec =
703    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_17553577885456905601_PROPERTIES);
704
705const CSS_MATCH_2883986488332352590_PROPERTIES: &[CssPropertyWithConditions] = &[
706    // body
707    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
708        LayoutPaddingRight {
709            inner: PixelValue::const_px(5),
710        },
711    ))),
712    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
713        LayoutPaddingLeft {
714            inner: PixelValue::const_px(5),
715        },
716    ))),
717    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
718        LayoutPaddingBottom {
719            inner: PixelValue::const_px(5),
720        },
721    ))),
722    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
723        LayoutPaddingTop {
724            inner: PixelValue::const_px(5),
725        },
726    ))),
727    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
728        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
729            STYLE_BACKGROUND_CONTENT_11062356617965867290_ITEMS,
730        )),
731    )),
732];
733const CSS_MATCH_2883986488332352590: CssPropertyWithConditionsVec =
734    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_2883986488332352590_PROPERTIES);
735
736const CSS_MATCH_4852927511892172364_PROPERTIES: &[CssPropertyWithConditions] = &[
737    // .__azul_native-list-rows
738    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
739        LayoutFlexDirection::Column,
740    ))),
741];
742const CSS_MATCH_4852927511892172364: CssPropertyWithConditionsVec =
743    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4852927511892172364_PROPERTIES);
744
745const CSS_MATCH_6002662151290653203_PROPERTIES: &[CssPropertyWithConditions] = &[
746    // .__azul_native-list-header-dragwidth
747    CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
748        LayoutWidth::Px(PixelValue::const_px(0)),
749    ))),
750    CssPropertyWithConditions::simple(CssProperty::Position(LayoutPositionValue::Exact(
751        LayoutPosition::Relative,
752    ))),
753    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
754        LayoutFlexGrow {
755            inner: FloatValue::const_new(1),
756        },
757    ))),
758];
759const CSS_MATCH_6002662151290653203: CssPropertyWithConditionsVec =
760    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_6002662151290653203_PROPERTIES);
761
762const CSS_MATCH_6827198030119836081_PROPERTIES: &[CssPropertyWithConditions] = &[
763    // .__azul_native-list-rows-row.selected
764    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
765        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
766            inner: PixelValue::const_px(1),
767        }),
768    )),
769    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
770        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
771            inner: PixelValue::const_px(1),
772        }),
773    )),
774    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
775        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
776            inner: PixelValue::const_px(1),
777        }),
778    )),
779    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
780        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
781            inner: PixelValue::const_px(1),
782        }),
783    )),
784    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
785        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
786            inner: BorderStyle::Solid,
787        }),
788    )),
789    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
790        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
791            inner: BorderStyle::Solid,
792        }),
793    )),
794    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
795        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
796            inner: BorderStyle::Solid,
797        }),
798    )),
799    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
800        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
801            inner: BorderStyle::Solid,
802        }),
803    )),
804    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
805        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
806            inner: ColorU {
807                r: 102,
808                g: 167,
809                b: 232,
810                a: 255,
811            },
812        }),
813    )),
814    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
815        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
816            inner: ColorU {
817                r: 102,
818                g: 167,
819                b: 232,
820                a: 255,
821            },
822        }),
823    )),
824    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
825        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
826            inner: ColorU {
827                r: 102,
828                g: 167,
829                b: 232,
830                a: 255,
831            },
832        }),
833    )),
834    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
835        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
836            inner: ColorU {
837                r: 102,
838                g: 167,
839                b: 232,
840                a: 255,
841            },
842        }),
843    )),
844    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
845        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
846            STYLE_BACKGROUND_CONTENT_661302523448178568_ITEMS,
847        )),
848    )),
849    // .__azul_native-list-rows-row:hover
850    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
851        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
852            inner: PixelValue::const_px(1),
853        }),
854    )),
855    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftWidth(
856        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
857            inner: PixelValue::const_px(1),
858        }),
859    )),
860    CssPropertyWithConditions::on_hover(CssProperty::BorderRightWidth(
861        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
862            inner: PixelValue::const_px(1),
863        }),
864    )),
865    CssPropertyWithConditions::on_hover(CssProperty::BorderTopWidth(
866        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
867            inner: PixelValue::const_px(1),
868        }),
869    )),
870    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
871        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
872            inner: BorderStyle::Solid,
873        }),
874    )),
875    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftStyle(
876        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
877            inner: BorderStyle::Solid,
878        }),
879    )),
880    CssPropertyWithConditions::on_hover(CssProperty::BorderRightStyle(
881        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
882            inner: BorderStyle::Solid,
883        }),
884    )),
885    CssPropertyWithConditions::on_hover(CssProperty::BorderTopStyle(
886        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
887            inner: BorderStyle::Solid,
888        }),
889    )),
890    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
891        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
892            inner: ColorU {
893                r: 101,
894                g: 181,
895                b: 220,
896                a: 255,
897            },
898        }),
899    )),
900    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftColor(
901        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
902            inner: ColorU {
903                r: 101,
904                g: 181,
905                b: 220,
906                a: 255,
907            },
908        }),
909    )),
910    CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
911        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
912            inner: ColorU {
913                r: 101,
914                g: 181,
915                b: 220,
916                a: 255,
917            },
918        }),
919    )),
920    CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
921        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
922            inner: ColorU {
923                r: 101,
924                g: 181,
925                b: 220,
926                a: 255,
927            },
928        }),
929    )),
930    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
931        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
932            STYLE_BACKGROUND_CONTENT_3010057533077499049_ITEMS,
933        )),
934    )),
935    // .__azul_native-list-rows-row
936    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
937        LayoutPaddingRight {
938            inner: PixelValue::const_px(0),
939        },
940    ))),
941    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
942        LayoutPaddingLeft {
943            inner: PixelValue::const_px(0),
944        },
945    ))),
946    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
947        LayoutPaddingBottom {
948            inner: PixelValue::const_px(2),
949        },
950    ))),
951    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
952        LayoutPaddingTop {
953            inner: PixelValue::const_px(2),
954        },
955    ))),
956    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
957        LayoutFlexGrow {
958            inner: FloatValue::const_new(1),
959        },
960    ))),
961    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
962        LayoutFlexDirection::Row,
963    ))),
964    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
965        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
966            inner: PixelValue::const_px(1),
967        }),
968    )),
969    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
970        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
971            inner: PixelValue::const_px(1),
972        }),
973    )),
974    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
975        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
976            inner: PixelValue::const_px(1),
977        }),
978    )),
979    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
980        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
981            inner: PixelValue::const_px(1),
982        }),
983    )),
984    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
985        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
986            inner: BorderStyle::Solid,
987        }),
988    )),
989    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
990        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
991            inner: BorderStyle::Solid,
992        }),
993    )),
994    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
995        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
996            inner: BorderStyle::Solid,
997        }),
998    )),
999    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1000        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1001            inner: BorderStyle::Solid,
1002        }),
1003    )),
1004    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1005        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1006            inner: ColorU {
1007                r: 255,
1008                g: 255,
1009                b: 255,
1010                a: 0,
1011            },
1012        }),
1013    )),
1014    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1015        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1016            inner: ColorU {
1017                r: 255,
1018                g: 255,
1019                b: 255,
1020                a: 0,
1021            },
1022        }),
1023    )),
1024    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1025        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1026            inner: ColorU {
1027                r: 255,
1028                g: 255,
1029                b: 255,
1030                a: 0,
1031            },
1032        }),
1033    )),
1034    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1035        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1036            inner: ColorU {
1037                r: 255,
1038                g: 255,
1039                b: 255,
1040                a: 0,
1041            },
1042        }),
1043    )),
1044];
1045const CSS_MATCH_6827198030119836081: CssPropertyWithConditionsVec =
1046    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_6827198030119836081_PROPERTIES);
1047
1048const CSS_MATCH_7894335449545988724_PROPERTIES: &[CssPropertyWithConditions] = &[
1049    // .__azul_native-list-rows-row.focused
1050    CssPropertyWithConditions::on_focus(CssProperty::BorderBottomWidth(
1051        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1052            inner: PixelValue::const_px(1),
1053        }),
1054    )),
1055    CssPropertyWithConditions::on_focus(CssProperty::BorderLeftWidth(
1056        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1057            inner: PixelValue::const_px(1),
1058        }),
1059    )),
1060    CssPropertyWithConditions::on_focus(CssProperty::BorderRightWidth(
1061        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1062            inner: PixelValue::const_px(1),
1063        }),
1064    )),
1065    CssPropertyWithConditions::on_focus(CssProperty::BorderTopWidth(
1066        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1067            inner: PixelValue::const_px(1),
1068        }),
1069    )),
1070    CssPropertyWithConditions::on_focus(CssProperty::BorderBottomStyle(
1071        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1072            inner: BorderStyle::Solid,
1073        }),
1074    )),
1075    CssPropertyWithConditions::on_focus(CssProperty::BorderLeftStyle(
1076        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1077            inner: BorderStyle::Solid,
1078        }),
1079    )),
1080    CssPropertyWithConditions::on_focus(CssProperty::BorderRightStyle(
1081        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1082            inner: BorderStyle::Solid,
1083        }),
1084    )),
1085    CssPropertyWithConditions::on_focus(CssProperty::BorderTopStyle(
1086        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1087            inner: BorderStyle::Solid,
1088        }),
1089    )),
1090    CssPropertyWithConditions::on_focus(CssProperty::BorderBottomColor(
1091        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1092            inner: ColorU {
1093                r: 38,
1094                g: 160,
1095                b: 218,
1096                a: 255,
1097            },
1098        }),
1099    )),
1100    CssPropertyWithConditions::on_focus(CssProperty::BorderLeftColor(
1101        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1102            inner: ColorU {
1103                r: 38,
1104                g: 160,
1105                b: 218,
1106                a: 255,
1107            },
1108        }),
1109    )),
1110    CssPropertyWithConditions::on_focus(CssProperty::BorderRightColor(
1111        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1112            inner: ColorU {
1113                r: 38,
1114                g: 160,
1115                b: 218,
1116                a: 255,
1117            },
1118        }),
1119    )),
1120    CssPropertyWithConditions::on_focus(CssProperty::BorderTopColor(
1121        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1122            inner: ColorU {
1123                r: 38,
1124                g: 160,
1125                b: 218,
1126                a: 255,
1127            },
1128        }),
1129    )),
1130    CssPropertyWithConditions::on_focus(CssProperty::BackgroundContent(
1131        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1132            STYLE_BACKGROUND_CONTENT_11098930083828139815_ITEMS,
1133        )),
1134    )),
1135    // .__azul_native-list-rows-row:hover
1136    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
1137        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1138            inner: PixelValue::const_px(1),
1139        }),
1140    )),
1141    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftWidth(
1142        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1143            inner: PixelValue::const_px(1),
1144        }),
1145    )),
1146    CssPropertyWithConditions::on_hover(CssProperty::BorderRightWidth(
1147        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1148            inner: PixelValue::const_px(1),
1149        }),
1150    )),
1151    CssPropertyWithConditions::on_hover(CssProperty::BorderTopWidth(
1152        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1153            inner: PixelValue::const_px(1),
1154        }),
1155    )),
1156    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
1157        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1158            inner: BorderStyle::Solid,
1159        }),
1160    )),
1161    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftStyle(
1162        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1163            inner: BorderStyle::Solid,
1164        }),
1165    )),
1166    CssPropertyWithConditions::on_hover(CssProperty::BorderRightStyle(
1167        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1168            inner: BorderStyle::Solid,
1169        }),
1170    )),
1171    CssPropertyWithConditions::on_hover(CssProperty::BorderTopStyle(
1172        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1173            inner: BorderStyle::Solid,
1174        }),
1175    )),
1176    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
1177        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1178            inner: ColorU {
1179                r: 101,
1180                g: 181,
1181                b: 220,
1182                a: 255,
1183            },
1184        }),
1185    )),
1186    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftColor(
1187        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1188            inner: ColorU {
1189                r: 101,
1190                g: 181,
1191                b: 220,
1192                a: 255,
1193            },
1194        }),
1195    )),
1196    CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
1197        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1198            inner: ColorU {
1199                r: 101,
1200                g: 181,
1201                b: 220,
1202                a: 255,
1203            },
1204        }),
1205    )),
1206    CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
1207        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1208            inner: ColorU {
1209                r: 101,
1210                g: 181,
1211                b: 220,
1212                a: 255,
1213            },
1214        }),
1215    )),
1216    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
1217        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1218            STYLE_BACKGROUND_CONTENT_3010057533077499049_ITEMS,
1219        )),
1220    )),
1221    // .__azul_native-list-rows-row
1222    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
1223        LayoutPaddingRight {
1224            inner: PixelValue::const_px(0),
1225        },
1226    ))),
1227    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1228        LayoutPaddingLeft {
1229            inner: PixelValue::const_px(0),
1230        },
1231    ))),
1232    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
1233        LayoutPaddingBottom {
1234            inner: PixelValue::const_px(2),
1235        },
1236    ))),
1237    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
1238        LayoutPaddingTop {
1239            inner: PixelValue::const_px(2),
1240        },
1241    ))),
1242    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
1243        LayoutFlexGrow {
1244            inner: FloatValue::const_new(1),
1245        },
1246    ))),
1247    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
1248        LayoutFlexDirection::Row,
1249    ))),
1250    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
1251        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1252            inner: PixelValue::const_px(1),
1253        }),
1254    )),
1255    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
1256        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1257            inner: PixelValue::const_px(1),
1258        }),
1259    )),
1260    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
1261        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1262            inner: PixelValue::const_px(1),
1263        }),
1264    )),
1265    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
1266        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1267            inner: PixelValue::const_px(1),
1268        }),
1269    )),
1270    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
1271        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1272            inner: BorderStyle::Solid,
1273        }),
1274    )),
1275    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
1276        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1277            inner: BorderStyle::Solid,
1278        }),
1279    )),
1280    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
1281        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1282            inner: BorderStyle::Solid,
1283        }),
1284    )),
1285    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1286        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1287            inner: BorderStyle::Solid,
1288        }),
1289    )),
1290    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1291        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1292            inner: ColorU {
1293                r: 255,
1294                g: 255,
1295                b: 255,
1296                a: 0,
1297            },
1298        }),
1299    )),
1300    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1301        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1302            inner: ColorU {
1303                r: 255,
1304                g: 255,
1305                b: 255,
1306                a: 0,
1307            },
1308        }),
1309    )),
1310    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1311        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1312            inner: ColorU {
1313                r: 255,
1314                g: 255,
1315                b: 255,
1316                a: 0,
1317            },
1318        }),
1319    )),
1320    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1321        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1322            inner: ColorU {
1323                r: 255,
1324                g: 255,
1325                b: 255,
1326                a: 0,
1327            },
1328        }),
1329    )),
1330];
1331const CSS_MATCH_7894335449545988724: CssPropertyWithConditionsVec =
1332    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_7894335449545988724_PROPERTIES);
1333
1334const IDS_AND_CLASSES_790316832563530605: &[IdOrClass] = &[Class(AzString::from_const_str(
1335    "__azul_native-list-rows-row",
1336))];
1337const ROW_CLASS: IdOrClassVec = IdOrClassVec::from_const_slice(IDS_AND_CLASSES_790316832563530605);
1338
1339const IDS_AND_CLASSES_3034181810805097699: &[IdOrClass] = &[Class(AzString::from_const_str(
1340    "__azul_native-list-rows-row-cell",
1341))];
1342const CELL_CLASS: IdOrClassVec =
1343    IdOrClassVec::from_const_slice(IDS_AND_CLASSES_3034181810805097699);
1344
1345const IDS_AND_CLASSES_6012478019077291002: &[IdOrClass] =
1346    &[Class(AzString::from_const_str("__azul_native-list-rows"))];
1347const ROW_CONTAINER_CLASS: IdOrClassVec =
1348    IdOrClassVec::from_const_slice(IDS_AND_CLASSES_6012478019077291002);
1349
1350const IDS_AND_CLASSES_10742579426112804392: &[IdOrClass] =
1351    &[Class(AzString::from_const_str("__azul_native-list-header"))];
1352const HEADER_CONTAINER_CLASS: IdOrClassVec =
1353    IdOrClassVec::from_const_slice(IDS_AND_CLASSES_10742579426112804392);
1354
1355const IDS_AND_CLASSES_9205819539370539587: &[IdOrClass] = &[Class(AzString::from_const_str(
1356    "__azul_native_list-container",
1357))];
1358const LIST_VIEW_CONTAINER_CLASS: IdOrClassVec =
1359    IdOrClassVec::from_const_slice(IDS_AND_CLASSES_9205819539370539587);
1360
1361const IDS_AND_CLASSES_18330792117162403422: &[IdOrClass] = &[Class(AzString::from_const_str(
1362    "__azul_native-list-header-item",
1363))];
1364const COLUMN_NAME_CLASS: IdOrClassVec =
1365    IdOrClassVec::from_const_slice(IDS_AND_CLASSES_18330792117162403422);
1366
1367pub type ListViewOnLazyLoadScrollCallbackType =
1368    extern "C" fn(RefAny, CallbackInfo, ListViewState) -> Update;
1369impl_widget_callback!(
1370    ListViewOnLazyLoadScroll,
1371    OptionListViewOnLazyLoadScroll,
1372    ListViewOnLazyLoadScrollCallback,
1373    ListViewOnLazyLoadScrollCallbackType
1374);
1375
1376azul_core::impl_managed_callback! {
1377    wrapper:        ListViewOnLazyLoadScrollCallback,
1378    info_ty:        CallbackInfo,
1379    return_ty:      Update,
1380    default_ret:    Update::DoNothing,
1381    invoker_static: LIST_VIEW_ON_LAZY_LOAD_SCROLL_INVOKER,
1382    invoker_ty:     AzListViewOnLazyLoadScrollCallbackInvoker,
1383    thunk_fn:       az_list_view_on_lazy_load_scroll_callback_thunk,
1384    setter_fn:      AzApp_setListViewOnLazyLoadScrollCallbackInvoker,
1385    from_handle_fn: AzListViewOnLazyLoadScrollCallback_createFromHostHandle,
1386    extra_args:     [ state: ListViewState ],
1387}
1388
1389pub type ListViewOnColumnClickCallbackType =
1390    extern "C" fn(RefAny, CallbackInfo, ListViewState, column_clicked: usize) -> Update;
1391impl_widget_callback!(
1392    ListViewOnColumnClick,
1393    OptionListViewOnColumnClick,
1394    ListViewOnColumnClickCallback,
1395    ListViewOnColumnClickCallbackType
1396);
1397
1398azul_core::impl_managed_callback! {
1399    wrapper:        ListViewOnColumnClickCallback,
1400    info_ty:        CallbackInfo,
1401    return_ty:      Update,
1402    default_ret:    Update::DoNothing,
1403    invoker_static: LIST_VIEW_ON_COLUMN_CLICK_INVOKER,
1404    invoker_ty:     AzListViewOnColumnClickCallbackInvoker,
1405    thunk_fn:       az_list_view_on_column_click_callback_thunk,
1406    setter_fn:      AzApp_setListViewOnColumnClickCallbackInvoker,
1407    from_handle_fn: AzListViewOnColumnClickCallback_createFromHostHandle,
1408    extra_args:     [ state: ListViewState, column_clicked: usize ],
1409}
1410
1411pub type ListViewOnRowClickCallbackType =
1412    extern "C" fn(RefAny, CallbackInfo, ListViewState, row_clicked: usize) -> Update;
1413impl_widget_callback!(
1414    ListViewOnRowClick,
1415    OptionListViewOnRowClick,
1416    ListViewOnRowClickCallback,
1417    ListViewOnRowClickCallbackType
1418);
1419
1420azul_core::impl_managed_callback! {
1421    wrapper:        ListViewOnRowClickCallback,
1422    info_ty:        CallbackInfo,
1423    return_ty:      Update,
1424    default_ret:    Update::DoNothing,
1425    invoker_static: LIST_VIEW_ON_ROW_CLICK_INVOKER,
1426    invoker_ty:     AzListViewOnRowClickCallbackInvoker,
1427    thunk_fn:       az_list_view_on_row_click_callback_thunk,
1428    setter_fn:      AzApp_setListViewOnRowClickCallbackInvoker,
1429    from_handle_fn: AzListViewOnRowClickCallback_createFromHostHandle,
1430    extra_args:     [ state: ListViewState, row_clicked: usize ],
1431}
1432
1433/// State of the `ListView`, but without row data
1434#[derive(Debug, Clone)]
1435#[repr(C)]
1436pub struct ListViewState {
1437    /// Copy of the current column names
1438    pub columns: StringVec,
1439    /// Which column the rows are currently sorted by
1440    pub sorted_by: OptionUsize,
1441    /// Row count of rows currently loaded in the DOM
1442    pub current_row_count: usize,
1443    /// Y-offset currently applied to the rows
1444    pub scroll_offset: PixelValueNoPercent,
1445    /// Current position where the user has scrolled the `ListView` to
1446    pub current_scroll_position: LogicalPosition,
1447    /// Current height of the row container
1448    pub current_content_height: LogicalSize,
1449}
1450
1451/// List view, optionally able to lazy-load data
1452#[derive(Debug, Clone)]
1453#[repr(C)]
1454pub struct ListView {
1455    /// Column names
1456    pub columns: StringVec,
1457    /// Currently rendered rows. Note that the `ListView` does not
1458    /// have to render all rows at once, usually you'd only render
1459    /// the top 100 rows
1460    pub rows: ListViewRowVec,
1461    /// Which column is the list view sorted by (default = None)?
1462    pub sorted_by: OptionUsize,
1463    /// Offset to add to the rows used when layouting row positions
1464    /// during lazy-loaded scrolling. Also affects the scroll position
1465    pub scroll_offset: PixelValueNoPercent,
1466    /// Height of the content, if not all rows are loaded
1467    pub content_height: OptionPixelValueNoPercent,
1468    /// Context menu for the columns (usually opens a context menu
1469    /// to select which columns to show)
1470    pub column_context_menu: OptionMenu,
1471    /// Indicates that this `ListView` is being lazily loaded, allows
1472    /// control over what happens when the user scrolls the `ListView`.
1473    pub on_lazy_load_scroll: OptionListViewOnLazyLoadScroll,
1474    /// What to do when the user left-clicks the column
1475    /// (usually used for storing which column to sort by)
1476    pub on_column_click: OptionListViewOnColumnClick,
1477    /// What to do when the user left-clicks a row
1478    /// (usually used for selecting the row depending on the state)
1479    pub on_row_click: OptionListViewOnRowClick,
1480}
1481
1482impl Default for ListView {
1483    fn default() -> Self {
1484        Self {
1485            columns: StringVec::from_const_slice(&[]),
1486            rows: ListViewRowVec::from_const_slice(&[]),
1487            sorted_by: None.into(),
1488            scroll_offset: PixelValueNoPercent {
1489                inner: PixelValue::const_px(0),
1490            },
1491            content_height: None.into(),
1492            column_context_menu: None.into(),
1493            on_lazy_load_scroll: None.into(),
1494            on_column_click: None.into(),
1495            on_row_click: None.into(),
1496        }
1497    }
1498}
1499
1500/// Row of the `ListView`
1501#[derive(Debug, Clone)]
1502#[repr(C)]
1503pub struct ListViewRow {
1504    /// Each cell is an opaque Dom object
1505    pub cells: DomVec,
1506    /// Height of the row, if known beforehand
1507    pub height: OptionPixelValueNoPercent,
1508}
1509
1510impl_option!(ListViewRow, OptionListViewRow, copy = false, [Debug, Clone]);
1511impl_vec!(ListViewRow, ListViewRowVec, ListViewRowVecDestructor, ListViewRowVecDestructorType, ListViewRowVecSlice, OptionListViewRow);
1512impl_vec_clone!(ListViewRow, ListViewRowVec, ListViewRowVecDestructor);
1513impl_vec_mut!(ListViewRow, ListViewRowVec);
1514impl_vec_debug!(ListViewRow, ListViewRowVec);
1515
1516impl ListView {
1517    #[must_use] pub fn create(columns: StringVec) -> Self {
1518        Self {
1519            columns,
1520            ..Default::default()
1521        }
1522    }
1523
1524    #[must_use]
1525    pub fn swap_with_default(&mut self) -> Self {
1526        let mut m = Self::default();
1527        core::mem::swap(&mut m, self);
1528        m
1529    }
1530
1531    #[must_use] pub fn with_columns(mut self, columns: StringVec) -> Self {
1532        self.set_columns(columns);
1533        self
1534    }
1535
1536    pub fn set_columns(&mut self, columns: StringVec) {
1537        self.columns = columns;
1538    }
1539
1540    #[must_use] pub fn with_rows(mut self, rows: ListViewRowVec) -> Self {
1541        self.set_rows(rows);
1542        self
1543    }
1544
1545    pub fn set_rows(&mut self, rows: ListViewRowVec) {
1546        self.rows = rows;
1547    }
1548
1549    /// The half-open range `[first, last)` of row indices visible in a
1550    /// vertically-scrolled, fixed-row-height list โ€” the windowing core for
1551    /// virtualizing a long `ListView` (render only these rows instead of all of
1552    /// them, the way the `MapWidget`'s `VirtualView` renders only visible tiles).
1553    /// `scroll_y` is pixels scrolled past the top, `viewport_height` the visible
1554    /// height; one extra row is included so a row straddling the bottom edge
1555    /// still renders. Returns `(0, 0)` for degenerate input (no rows, a
1556    /// non-positive/non-finite height, or non-finite scroll), and an empty range
1557    /// `(total, total)` once scrolled past the end.
1558    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] // bounded layout/render numeric cast
1559    #[must_use] pub fn visible_row_range(
1560        scroll_y: f32,
1561        viewport_height: f32,
1562        row_height: f32,
1563        total_rows: usize,
1564    ) -> (usize, usize) {
1565        if total_rows == 0
1566            || !row_height.is_finite()
1567            || row_height <= 0.0
1568            || !viewport_height.is_finite()
1569            || viewport_height <= 0.0
1570            || !scroll_y.is_finite()
1571        {
1572            return (0, 0);
1573        }
1574        let first = (scroll_y.max(0.0) / row_height).floor() as usize;
1575        if first >= total_rows {
1576            return (total_rows, total_rows);
1577        }
1578        // Saturating: a sub-pixel `row_height` makes `viewport_height / row_height`
1579        // astronomically large, whose `as usize` cast saturates to `usize::MAX`, so
1580        // `+ 1` (and `first + visible`) would overflow. The `.min(total_rows)` clamp
1581        // makes the saturated value harmless.
1582        let visible = ((viewport_height / row_height).ceil() as usize).saturating_add(1);
1583        let last = first.saturating_add(visible).min(total_rows);
1584        (first, last)
1585    }
1586
1587    #[must_use] pub const fn with_sorted_by(mut self, sorted_by: OptionUsize) -> Self {
1588        self.set_sorted_by(sorted_by);
1589        self
1590    }
1591
1592    pub const fn set_sorted_by(&mut self, sorted_by: OptionUsize) {
1593        self.sorted_by = sorted_by;
1594    }
1595
1596    #[must_use] pub const fn with_scroll_offset(mut self, scroll_offset: PixelValueNoPercent) -> Self {
1597        self.set_scroll_offset(scroll_offset);
1598        self
1599    }
1600
1601    pub const fn set_scroll_offset(&mut self, scroll_offset: PixelValueNoPercent) {
1602        self.scroll_offset = scroll_offset;
1603    }
1604
1605    #[must_use] pub fn with_content_height(mut self, content_height: PixelValueNoPercent) -> Self {
1606        self.set_content_height(content_height);
1607        self
1608    }
1609
1610    pub fn set_content_height(&mut self, content_height: PixelValueNoPercent) {
1611        self.content_height = Some(content_height).into();
1612    }
1613
1614    #[must_use] pub fn with_column_context_menu(mut self, context_menu: Menu) -> Self {
1615        self.set_column_context_menu(context_menu);
1616        self
1617    }
1618
1619    pub fn set_column_context_menu(&mut self, column_context_menu: Menu) {
1620        self.column_context_menu = Some(column_context_menu).into();
1621    }
1622
1623    #[must_use]
1624    pub fn with_on_column_click<C: Into<ListViewOnColumnClickCallback>>(
1625        mut self,
1626        refany: RefAny,
1627        on_column_click: C,
1628    ) -> Self {
1629        self.set_on_column_click(refany, on_column_click);
1630        self
1631    }
1632
1633    pub fn set_on_column_click<C: Into<ListViewOnColumnClickCallback>>(
1634        &mut self,
1635        refany: RefAny,
1636        on_column_click: C,
1637    ) {
1638        self.on_column_click = Some(ListViewOnColumnClick {
1639            refany,
1640            callback: on_column_click.into(),
1641        })
1642        .into();
1643    }
1644
1645    #[must_use]
1646    pub fn with_on_row_click<C: Into<ListViewOnRowClickCallback>>(
1647        mut self,
1648        refany: RefAny,
1649        on_row_click: C,
1650    ) -> Self {
1651        self.set_on_row_click(refany, on_row_click);
1652        self
1653    }
1654
1655    pub fn set_on_row_click<C: Into<ListViewOnRowClickCallback>>(
1656        &mut self,
1657        refany: RefAny,
1658        on_row_click: C,
1659    ) {
1660        self.on_row_click = Some(ListViewOnRowClick {
1661            refany,
1662            callback: on_row_click.into(),
1663        })
1664        .into();
1665    }
1666
1667    #[must_use] pub fn dom(self) -> Dom {
1668        // Snapshot the state handed to row/column click callbacks. Runtime-only
1669        // fields (scroll position / content height) aren't known at build time,
1670        // so they default to zero; columns/sorted_by/row-count/scroll-offset are.
1671        let state = ListViewState {
1672            columns: self.columns.clone(),
1673            sorted_by: self.sorted_by,
1674            current_row_count: self.rows.as_ref().len(),
1675            scroll_offset: self.scroll_offset,
1676            current_scroll_position: LogicalPosition::zero(),
1677            current_content_height: LogicalSize::zero(),
1678        };
1679        let on_column_click = self.on_column_click.clone();
1680        let on_row_click = self.on_row_click.clone();
1681
1682        Dom::create_div()
1683            .with_css_props(CSS_MATCH_17553577885456905601)
1684            .with_ids_and_classes(LIST_VIEW_CONTAINER_CLASS)
1685            .with_children(DomVec::from_vec(vec![
1686                // header
1687                Dom::create_div()
1688                    .with_css_props(CSS_MATCH_15315949193378715186)
1689                    .with_ids_and_classes(HEADER_CONTAINER_CLASS)
1690                    .with_children(
1691                        self.columns
1692                            .iter()
1693                            .enumerate()
1694                            .map(|(col_index, col)| {
1695                                let col_dom = Dom::create_div()
1696                                    .with_css_props(CSS_MATCH_12498280255863106397)
1697                                    .with_ids_and_classes(COLUMN_NAME_CLASS)
1698                                    .with_child({
1699                                        Dom::create_text(col.clone())
1700                                            .with_css_props(CSS_MATCH_15673486787900743642)
1701                                    });
1702                                // Wire the click only when the app set a handler.
1703                                match &on_column_click {
1704                                    OptionListViewOnColumnClick::Some(_) => col_dom.with_callbacks(
1705                                        vec![CoreCallbackData {
1706                                            event: EventFilter::Hover(HoverEventFilter::MouseUp),
1707                                            refany: RefAny::new(ColumnClickData {
1708                                                col_index,
1709                                                state: state.clone(),
1710                                                on_column_click: on_column_click.clone(),
1711                                            }),
1712                                            callback: CoreCallback {
1713                                                cb: on_list_view_column_click as usize,
1714                                                ctx: OptionRefAny::None,
1715                                            },
1716                                        }]
1717                                        .into(),
1718                                    ),
1719                                    OptionListViewOnColumnClick::None => col_dom,
1720                                }
1721                            })
1722                            .collect::<Vec<_>>()
1723                            .into(),
1724                    ),
1725                // rows
1726                Dom::create_div()
1727                    .with_css_props(CSS_MATCH_4852927511892172364)
1728                    .with_ids_and_classes(ROW_CONTAINER_CLASS)
1729                    .with_children(
1730                        self.rows
1731                            .into_iter()
1732                            .enumerate()
1733                            .map(|(row_index, row)| {
1734                                let row_dom = Dom::create_div()
1735                                    .with_css_props(CSS_MATCH_7894335449545988724)
1736                                    .with_ids_and_classes(ROW_CLASS)
1737                                    .with_tab_index(TabIndex::Auto)
1738                                    .with_children(
1739                                        row.cells
1740                                            .as_ref()
1741                                            .iter()
1742                                            .map(|cell| {
1743                                                Dom::create_div()
1744                                                    .with_css_props(CSS_MATCH_12980082330151137475)
1745                                                    .with_ids_and_classes(CELL_CLASS)
1746                                                    .with_child(cell.clone())
1747                                            })
1748                                            .collect::<Vec<_>>()
1749                                            .into(),
1750                                    );
1751                                match &on_row_click {
1752                                    OptionListViewOnRowClick::Some(_) => row_dom.with_callbacks(
1753                                        vec![CoreCallbackData {
1754                                            event: EventFilter::Hover(HoverEventFilter::MouseUp),
1755                                            refany: RefAny::new(RowClickData {
1756                                                row_index,
1757                                                state: state.clone(),
1758                                                on_row_click: on_row_click.clone(),
1759                                            }),
1760                                            callback: CoreCallback {
1761                                                cb: on_list_view_row_click as usize,
1762                                                ctx: OptionRefAny::None,
1763                                            },
1764                                        }]
1765                                        .into(),
1766                                    ),
1767                                    OptionListViewOnRowClick::None => row_dom,
1768                                }
1769                            })
1770                            .collect::<Vec<_>>()
1771                            .into(),
1772                    ),
1773            ]))
1774    }
1775}
1776
1777/// Per-row data carried to the internal `MouseUp` handler (the row index plus a
1778/// snapshot of the list state and the app's `on_row_click` hook).
1779struct RowClickData {
1780    row_index: usize,
1781    state: ListViewState,
1782    on_row_click: OptionListViewOnRowClick,
1783}
1784
1785/// Per-column equivalent of [`RowClickData`].
1786struct ColumnClickData {
1787    col_index: usize,
1788    state: ListViewState,
1789    on_column_click: OptionListViewOnColumnClick,
1790}
1791
1792/// `MouseUp` on a row โ†’ invoke the app's `on_row_click(state, row_index)`.
1793extern "C" fn on_list_view_row_click(mut refany: RefAny, info: CallbackInfo) -> Update {
1794    let Some(data) = refany.downcast_ref::<RowClickData>() else {
1795        return Update::DoNothing;
1796    };
1797    match data.on_row_click.as_ref() {
1798        Some(ListViewOnRowClick { refany: user_data, callback }) => {
1799            (callback.cb)(user_data.clone(), info, data.state.clone(), data.row_index)
1800        }
1801        None => Update::DoNothing,
1802    }
1803}
1804
1805/// `MouseUp` on a column header โ†’ invoke the app's `on_column_click(state, col_index)`.
1806extern "C" fn on_list_view_column_click(mut refany: RefAny, info: CallbackInfo) -> Update {
1807    let Some(data) = refany.downcast_ref::<ColumnClickData>() else {
1808        return Update::DoNothing;
1809    };
1810    match data.on_column_click.as_ref() {
1811        Some(ListViewOnColumnClick { refany: user_data, callback }) => {
1812            (callback.cb)(user_data.clone(), info, data.state.clone(), data.col_index)
1813        }
1814        None => Update::DoNothing,
1815    }
1816}
1817
1818#[cfg(test)]
1819mod list_view_click_tests {
1820    use super::*;
1821
1822    /// The windowing core for `ListView` virtualization: only the visible rows
1823    /// (+1 straddling the bottom) are in range, the range tracks scroll, clamps
1824    /// to the row count, and degenerate input yields an empty range.
1825    #[test]
1826    fn visible_row_range_windows_correctly() {
1827        // 100 rows x 20px, 200px viewport โ†’ 10 full rows + 1 partial.
1828        assert_eq!(ListView::visible_row_range(0.0, 200.0, 20.0, 100), (0, 11));
1829        // Scrolled 50px โ†’ first row = floor(50/20) = 2.
1830        assert_eq!(ListView::visible_row_range(50.0, 200.0, 20.0, 100), (2, 13));
1831        // Near the end โ†’ clamped to the row count.
1832        assert_eq!(ListView::visible_row_range(1900.0, 200.0, 20.0, 100), (95, 100));
1833        // Scrolled past the end โ†’ empty range at the tail.
1834        assert_eq!(ListView::visible_row_range(5000.0, 200.0, 20.0, 100), (100, 100));
1835        // Degenerate inputs โ†’ empty.
1836        assert_eq!(ListView::visible_row_range(0.0, 200.0, 20.0, 0), (0, 0));
1837        assert_eq!(ListView::visible_row_range(0.0, 200.0, 0.0, 100), (0, 0));
1838        assert_eq!(ListView::visible_row_range(f32::NAN, 200.0, 20.0, 100), (0, 0));
1839    }
1840
1841    extern "C" fn noop_row(_: RefAny, _: CallbackInfo, _: ListViewState, _: usize) -> Update {
1842        Update::DoNothing
1843    }
1844
1845    fn empty_row() -> ListViewRow {
1846        ListViewRow {
1847            cells: DomVec::from_const_slice(&[]),
1848            height: None.into(),
1849        }
1850    }
1851
1852    /// Rows must carry a click callback exactly when `on_row_click` is set โ€”
1853    /// previously `dom()` wired nothing, so the hook was dead.
1854    #[test]
1855    #[allow(clippy::field_reassign_with_default)] // struct built incrementally / test setup; a struct literal is not clearer here
1856    fn rows_get_a_click_callback_only_when_on_row_click_is_set() {
1857        let mut lv = ListView::default();
1858        lv.rows = ListViewRowVec::from_vec(vec![empty_row(), empty_row()]);
1859        let on_row_click: ListViewOnRowClickCallbackType = noop_row;
1860        lv.set_on_row_click(RefAny::new(()), on_row_click);
1861        let dom = lv.dom();
1862        // children = [header, rows]; each row div carries the MouseUp callback.
1863        let rows = dom.children.as_ref()[1].children.as_ref();
1864        assert_eq!(rows.len(), 2);
1865        for row in rows {
1866            assert_eq!(
1867                row.root.callbacks.as_ref().len(),
1868                1,
1869                "row must carry the click callback when on_row_click is set"
1870            );
1871        }
1872
1873        // Without the hook โ†’ no callbacks (opt-in, no wasted dispatch).
1874        let mut bare = ListView::default();
1875        bare.rows = ListViewRowVec::from_vec(vec![empty_row()]);
1876        let dom2 = bare.dom();
1877        let rows2 = dom2.children.as_ref()[1].children.as_ref();
1878        assert_eq!(rows2.len(), 1);
1879        assert!(
1880            rows2[0].root.callbacks.as_ref().is_empty(),
1881            "no callback when on_row_click is unset"
1882        );
1883    }
1884}
1885
1886#[cfg(test)]
1887#[allow(clippy::float_cmp)] // exact float compares are deliberate here (saturation / identity checks)
1888mod autotest_generated {
1889    use azul_core::{
1890        dom::NodeType,
1891        menu::{MenuItem, MenuItemVec},
1892    };
1893
1894    use super::*;
1895
1896    // ------------------------------------------------------------------
1897    // Helpers
1898    // ------------------------------------------------------------------
1899
1900    fn cols(names: &[&str]) -> StringVec {
1901        StringVec::from_vec(names.iter().map(|s| AzString::from(*s)).collect::<Vec<_>>())
1902    }
1903
1904    /// A row with `n` text cells (`n == 0` is allowed and deliberately used).
1905    fn row_with(n: usize) -> ListViewRow {
1906        ListViewRow {
1907            cells: DomVec::from_vec(
1908                (0..n)
1909                    .map(|i| Dom::create_text(format!("c{i}")))
1910                    .collect::<Vec<_>>(),
1911            ),
1912            height: None.into(),
1913        }
1914    }
1915
1916    fn px(v: f32) -> PixelValueNoPercent {
1917        PixelValueNoPercent {
1918            inner: PixelValue::px(v),
1919        }
1920    }
1921
1922    extern "C" fn noop_row_cb(_: RefAny, _: CallbackInfo, _: ListViewState, _: usize) -> Update {
1923        Update::DoNothing
1924    }
1925
1926    extern "C" fn noop_col_cb(_: RefAny, _: CallbackInfo, _: ListViewState, _: usize) -> Update {
1927        Update::DoNothing
1928    }
1929
1930    /// The `[header, rows]` container pair every `ListView` DOM is built from.
1931    fn header_and_rows(dom: &Dom) -> (&Dom, &Dom) {
1932        let ch = dom.children.as_ref();
1933        assert_eq!(ch.len(), 2, "list view DOM = [header, rows]");
1934        (&ch[0], &ch[1])
1935    }
1936
1937    fn text_of(dom: &Dom) -> &str {
1938        match &dom.root.node_type {
1939            NodeType::Text(s) => s.as_ref().as_str(),
1940            _ => panic!("expected a text node"),
1941        }
1942    }
1943
1944    // ------------------------------------------------------------------
1945    // `visible_row_range` โ€” numeric core (zero / negative / NaN / limits)
1946    // ------------------------------------------------------------------
1947
1948    /// Every degenerate input documented as "empty" really returns `(0, 0)`,
1949    /// including `-0.0` (which must count as non-positive, not as a valid height).
1950    #[test]
1951    fn visible_row_range_zero_and_degenerate_inputs_are_empty() {
1952        assert_eq!(ListView::visible_row_range(0.0, 100.0, 10.0, 0), (0, 0));
1953        assert_eq!(ListView::visible_row_range(0.0, 100.0, 0.0, 5), (0, 0));
1954        assert_eq!(ListView::visible_row_range(0.0, 100.0, -0.0, 5), (0, 0));
1955        assert_eq!(ListView::visible_row_range(0.0, 100.0, -10.0, 5), (0, 0));
1956        assert_eq!(ListView::visible_row_range(0.0, 0.0, 10.0, 5), (0, 0));
1957        assert_eq!(ListView::visible_row_range(0.0, -0.0, 10.0, 5), (0, 0));
1958        assert_eq!(ListView::visible_row_range(0.0, -100.0, 10.0, 5), (0, 0));
1959        assert_eq!(ListView::visible_row_range(0.0, 0.0, 0.0, 0), (0, 0));
1960    }
1961
1962    /// A negative scroll offset (rubber-band / over-scroll) must clamp to the
1963    /// top window rather than wrapping through the `as usize` cast.
1964    #[test]
1965    fn visible_row_range_negative_scroll_clamps_to_the_top_window() {
1966        let top = ListView::visible_row_range(0.0, 200.0, 20.0, 100);
1967        assert_eq!(top, (0, 11), "10 full rows + 1 straddling the bottom edge");
1968        for s in [-0.0_f32, -1.0, -0.5, -1e9, -f32::MAX, -f32::MIN_POSITIVE] {
1969            assert_eq!(
1970                ListView::visible_row_range(s, 200.0, 20.0, 100),
1971                top,
1972                "negative scroll {s} must clamp to the top window"
1973            );
1974        }
1975    }
1976
1977    /// NaN / ยฑinf in any float argument yields the documented empty range โ€” no
1978    /// panic, no garbage index out of the floatโ†’int cast.
1979    #[test]
1980    fn visible_row_range_nan_and_infinite_inputs_are_empty() {
1981        for b in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
1982            assert_eq!(
1983                ListView::visible_row_range(b, 200.0, 20.0, 100),
1984                (0, 0),
1985                "scroll_y = {b}"
1986            );
1987            assert_eq!(
1988                ListView::visible_row_range(0.0, b, 20.0, 100),
1989                (0, 0),
1990                "viewport_height = {b}"
1991            );
1992            assert_eq!(
1993                ListView::visible_row_range(0.0, 200.0, b, 100),
1994                (0, 0),
1995                "row_height = {b}"
1996            );
1997        }
1998        // All-NaN with the largest possible row count is still empty.
1999        assert_eq!(
2000            ListView::visible_row_range(f32::NAN, f32::NAN, f32::NAN, usize::MAX),
2001            (0, 0)
2002        );
2003    }
2004
2005    /// Scrolling past the end returns the empty tail range, and a `scroll_y`
2006    /// large enough to saturate the `as usize` cast takes the same path.
2007    #[test]
2008    fn visible_row_range_past_the_end_is_an_empty_tail_range() {
2009        // 10 rows x 20px = 200px of content; scrolling exactly to the end.
2010        assert_eq!(
2011            ListView::visible_row_range(200.0, 100.0, 20.0, 10),
2012            (10, 10)
2013        );
2014        assert_eq!(
2015            ListView::visible_row_range(1e9, 200.0, 20.0, 100),
2016            (100, 100)
2017        );
2018        // f32::MAX / 20 overflows usize; the saturating cast keeps it >= total.
2019        assert_eq!(
2020            ListView::visible_row_range(f32::MAX, 200.0, 20.0, 100),
2021            (100, 100)
2022        );
2023        // ... one float ULP before the end is still a live window.
2024        let (first, last) = ListView::visible_row_range(199.0, 100.0, 20.0, 10);
2025        assert!(first < last, "just before the end the range is non-empty");
2026        assert_eq!(last, 10, "and clamps to the row count");
2027    }
2028
2029    /// A huge `total_rows` must not make the window huge โ€” only the viewport
2030    /// decides how many rows are returned.
2031    #[test]
2032    fn visible_row_range_window_size_is_bounded_by_the_viewport_not_the_row_count() {
2033        for total in [1_usize, 2, 1000, u32::MAX as usize, usize::MAX] {
2034            let (first, last) = ListView::visible_row_range(0.0, 200.0, 20.0, total);
2035            assert_eq!(first, 0);
2036            assert_eq!(
2037                last,
2038                11.min(total),
2039                "window stays viewport-sized for total_rows = {total}"
2040            );
2041        }
2042    }
2043
2044    /// Property: whatever the inputs, the returned window is well-ordered,
2045    /// clamped to the row count, and actually covers the visible strip.
2046    #[test]
2047    fn visible_row_range_window_always_covers_the_viewport() {
2048        let total = 1000_usize;
2049        for &h in &[1.0_f32, 7.5, 20.0, 33.3] {
2050            for &vp in &[1.0_f32, 17.0, 200.0, 999.0] {
2051                for &s in &[0.0_f32, 0.1, 19.0, 123.456, 5000.0] {
2052                    let (first, last) = ListView::visible_row_range(s, vp, h, total);
2053                    assert!(first <= last, "well-ordered range for ({s}, {vp}, {h})");
2054                    assert!(last <= total, "range clamps to the row count");
2055                    if first == last {
2056                        continue; // empty tail range: nothing to cover
2057                    }
2058                    let top = first as f32 * h;
2059                    let bottom = last as f32 * h;
2060                    assert!(top <= s, "window starts at or above scroll {s} (top {top})");
2061                    let needed = (s + vp).min(total as f32 * h);
2062                    assert!(
2063                        bottom >= needed,
2064                        "window bottom {bottom} must reach {needed} for ({s}, {vp}, {h})"
2065                    );
2066                }
2067            }
2068        }
2069    }
2070
2071    /// KNOWN BUG โ€” pinned deliberately, do NOT weaken to make it pass.
2072    ///
2073    /// `visible_row_range` computes `(viewport_height / row_height).ceil() as
2074    /// usize + 1` and then `first + visible`. The floatโ†’int cast *saturates* to
2075    /// `usize::MAX`, so the `+ 1` (and the later addition) overflow and panic
2076    /// under the default dev/test `overflow-checks`. Both inputs below are
2077    /// finite, positive and pass every existing guard. Expected safe behaviour
2078    /// is to saturate and clamp to `total_rows`, as the doc comment promises.
2079    #[test]
2080    fn visible_row_range_does_not_overflow_on_extreme_but_finite_input() {
2081        // Sub-pixel row height (degenerate zoom): 1000 / 1e-30 = 1e33, which
2082        // saturates the cast to usize::MAX -> `+ 1` overflows.
2083        assert_eq!(ListView::visible_row_range(0.0, 1000.0, 1e-30, 10), (0, 10));
2084
2085        // `first + visible` overflows even when neither term alone saturates:
2086        // ~1e19 + ~1e19 > usize::MAX (~1.84e19).
2087        let (first, last) = ListView::visible_row_range(1.0e19, 1.0e19, 1.0, usize::MAX);
2088        assert!(first > 0, "a huge scroll lands deep in the list");
2089        assert_eq!(last, usize::MAX, "the window must clamp to the row count");
2090    }
2091
2092    // ------------------------------------------------------------------
2093    // Constructors / setters โ€” round-trip + invariants
2094    // ------------------------------------------------------------------
2095
2096    #[test]
2097    fn create_sets_columns_and_leaves_everything_else_default() {
2098        let lv = ListView::create(cols(&["a", "b", "c"]));
2099        assert_eq!(lv.columns.len(), 3);
2100        assert_eq!(lv.columns.as_ref()[1].as_str(), "b");
2101        assert!(lv.rows.is_empty());
2102        assert!(lv.sorted_by.is_none());
2103        assert!(lv.content_height.is_none());
2104        assert!(lv.column_context_menu.is_none());
2105        assert!(lv.on_lazy_load_scroll.is_none());
2106        assert!(lv.on_column_click.is_none());
2107        assert!(lv.on_row_click.is_none());
2108        assert_eq!(lv.scroll_offset, PixelValueNoPercent::zero());
2109
2110        // An empty column list is accepted, not rejected or defaulted.
2111        let empty = ListView::create(StringVec::from_const_slice(&[]));
2112        assert!(empty.columns.is_empty());
2113    }
2114
2115    #[test]
2116    fn with_and_set_columns_replace_rather_than_append() {
2117        let b = cols(&["x", "y"]);
2118        let lv = ListView::default()
2119            .with_columns(cols(&["one"]))
2120            .with_columns(b.clone());
2121        assert_eq!(lv.columns, b, "the last write wins");
2122
2123        let mut m = ListView::default();
2124        m.set_columns(b.clone());
2125        assert_eq!(m.columns, b);
2126        m.set_columns(StringVec::from_const_slice(&[]));
2127        assert!(m.columns.is_empty(), "columns can be cleared again");
2128    }
2129
2130    /// Column names are opaque payload: empty strings, interior NULs, astral /
2131    /// ZWJ / RTL / combining sequences and very long strings must survive the
2132    /// builder *and* the DOM build byte-for-byte.
2133    #[test]
2134    fn columns_round_trip_unicode_and_pathological_strings() {
2135        let names: Vec<String> = vec![
2136            String::new(),
2137            "\u{0}".to_string(),
2138            "๐Ÿฆ€๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".to_string(),
2139            "ู…ุฑุญุจุง ุจุงู„ุนุงู„ู…".to_string(),
2140            "e\u{301}\u{301}\u{301}".to_string(),
2141            "\u{feff}leading BOM".to_string(),
2142            "line\nbreak\ttab".to_string(),
2143            "x".repeat(10_000),
2144        ];
2145        let sv = StringVec::from_vec(
2146            names
2147                .iter()
2148                .map(|s| AzString::from(s.clone()))
2149                .collect::<Vec<_>>(),
2150        );
2151        let lv = ListView::default().with_columns(sv);
2152        assert_eq!(lv.columns.len(), names.len());
2153        for (got, want) in lv.columns.iter().zip(names.iter()) {
2154            assert_eq!(got.as_str(), want.as_str(), "column name must round-trip");
2155        }
2156
2157        let dom = lv.dom();
2158        let (header, _) = header_and_rows(&dom);
2159        let hdr = header.children.as_ref();
2160        assert_eq!(hdr.len(), names.len());
2161        for (col, want) in hdr.iter().zip(names.iter()) {
2162            let text = col.children.as_ref();
2163            assert_eq!(text.len(), 1, "each header cell holds one text node");
2164            assert_eq!(text_of(&text[0]), want.as_str());
2165        }
2166    }
2167
2168    #[test]
2169    fn with_and_set_rows_round_trip_including_empty_and_ragged_rows() {
2170        let lv = ListView::default().with_rows(ListViewRowVec::from_vec(vec![
2171            row_with(0),
2172            row_with(1),
2173            row_with(5),
2174        ]));
2175        assert_eq!(lv.rows.len(), 3);
2176        assert_eq!(lv.rows.as_ref()[0].cells.len(), 0);
2177        assert_eq!(lv.rows.as_ref()[2].cells.len(), 5);
2178
2179        let mut m = ListView::default().with_rows(ListViewRowVec::from_vec(vec![row_with(2)]));
2180        m.set_rows(ListViewRowVec::from_const_slice(&[]));
2181        assert!(m.rows.is_empty(), "rows can be cleared again");
2182    }
2183
2184    /// `sorted_by` is a raw column index with no validation โ€” an out-of-range
2185    /// value is stored verbatim and must not break the DOM build.
2186    #[test]
2187    fn sorted_by_is_stored_verbatim_even_when_out_of_range() {
2188        for v in [None, Some(0_usize), Some(2), Some(usize::MAX)] {
2189            let lv = ListView::create(cols(&["a", "b"])).with_sorted_by(v.into());
2190            assert_eq!(
2191                lv.sorted_by.as_ref().copied(),
2192                v,
2193                "sorted_by is not validated against the column count"
2194            );
2195            let dom = lv.dom();
2196            let (header, _) = header_and_rows(&dom);
2197            assert_eq!(header.children.as_ref().len(), 2);
2198        }
2199
2200        let mut m = ListView::default();
2201        m.set_sorted_by(Some(7_usize).into());
2202        assert_eq!(m.sorted_by.as_ref().copied(), Some(7));
2203        m.set_sorted_by(None.into());
2204        assert!(m.sorted_by.is_none(), "sorted_by can be reset to None");
2205    }
2206
2207    /// The fixed-point `PixelValue` encoding saturates (NaN -> 0, ยฑinf -> the
2208    /// isize limits) instead of trapping, and the setter stores the value bit
2209    /// for bit.
2210    #[test]
2211    fn scroll_offset_round_trips_extreme_and_non_finite_values() {
2212        for v in [
2213            0.0_f32,
2214            -0.0,
2215            1.0,
2216            -1.0,
2217            0.001,
2218            -12345.678,
2219            f32::MAX,
2220            f32::MIN,
2221            f32::MIN_POSITIVE,
2222            f32::NAN,
2223            f32::INFINITY,
2224            f32::NEG_INFINITY,
2225        ] {
2226            let p = px(v);
2227            let lv = ListView::default().with_scroll_offset(p);
2228            assert_eq!(lv.scroll_offset, p, "scroll_offset stored verbatim ({v})");
2229            let got = lv.scroll_offset.inner.number.get();
2230            assert!(
2231                got.is_finite(),
2232                "encoded offset stays finite for {v} (got {got})"
2233            );
2234        }
2235
2236        assert_eq!(px(f32::NAN).inner.number.get(), 0.0, "NaN saturates to 0");
2237        assert!(px(f32::INFINITY).inner.number.get() > 0.0);
2238        assert!(px(f32::NEG_INFINITY).inner.number.get() < 0.0);
2239
2240        let mut m = ListView::default();
2241        m.set_scroll_offset(px(5.0));
2242        m.set_scroll_offset(px(-5.0));
2243        assert_eq!(m.scroll_offset, px(-5.0), "the last write wins");
2244    }
2245
2246    /// `PixelValueNoPercent` does not actually reject a `%` metric โ€” the setter
2247    /// takes whatever it is handed. Pinned so a future validation change is a
2248    /// deliberate decision, not a silent one.
2249    #[test]
2250    fn scroll_offset_accepts_a_percent_metric_despite_the_type_name() {
2251        let percent = PixelValueNoPercent {
2252            inner: PixelValue::percent(50.0),
2253        };
2254        let lv = ListView::default().with_scroll_offset(percent);
2255        assert_eq!(lv.scroll_offset, percent);
2256        assert_eq!(lv.scroll_offset.inner.metric, SizeMetric::Percent);
2257    }
2258
2259    #[test]
2260    fn content_height_wraps_in_some_and_has_no_clearing_setter() {
2261        let mut m = ListView::default();
2262        assert!(m.content_height.is_none(), "unset by default");
2263        for v in [0.0_f32, -1.0, f32::MAX, f32::NAN, f32::NEG_INFINITY] {
2264            let p = px(v);
2265            m.set_content_height(p);
2266            assert_eq!(m.content_height.as_ref(), Some(&p), "stores {v} verbatim");
2267            assert!(m
2268                .content_height
2269                .as_ref()
2270                .expect("just set")
2271                .inner
2272                .number
2273                .get()
2274                .is_finite());
2275        }
2276        assert!(
2277            m.content_height.is_some(),
2278            "Some() is sticky โ€” no unset API"
2279        );
2280
2281        let lv = ListView::default().with_content_height(px(42.0));
2282        assert_eq!(lv.content_height.as_ref(), Some(&px(42.0)));
2283    }
2284
2285    #[test]
2286    fn column_context_menu_is_stored_and_replaced() {
2287        let mut m = ListView::default();
2288        assert!(m.column_context_menu.is_none());
2289
2290        let empty_menu = Menu::create(MenuItemVec::from_const_slice(&[]));
2291        m.set_column_context_menu(empty_menu.clone());
2292        assert_eq!(
2293            m.column_context_menu.as_ref(),
2294            Some(&empty_menu),
2295            "an empty menu is accepted, not silently dropped"
2296        );
2297
2298        let full = Menu::create(MenuItemVec::from_vec(vec![MenuItem::Separator; 256]));
2299        m.set_column_context_menu(full.clone());
2300        assert_eq!(
2301            m.column_context_menu.as_ref(),
2302            Some(&full),
2303            "last write wins"
2304        );
2305        assert!(
2306            m.column_context_menu.is_some(),
2307            "there is no way to unset it"
2308        );
2309
2310        let lv = ListView::default().with_column_context_menu(full.clone());
2311        assert_eq!(lv.column_context_menu.as_ref(), Some(&full));
2312        // The menu is metadata only โ€” it must not alter the DOM shape.
2313        let dom = lv.dom();
2314        let (header, rows) = header_and_rows(&dom);
2315        assert!(header.children.as_ref().is_empty());
2316        assert!(rows.children.as_ref().is_empty());
2317    }
2318
2319    #[test]
2320    fn click_hook_setters_replace_rather_than_accumulate() {
2321        let rcb: ListViewOnRowClickCallbackType = noop_row_cb;
2322        let ccb: ListViewOnColumnClickCallbackType = noop_col_cb;
2323
2324        let mut m = ListView::default();
2325        assert!(m.on_row_click.is_none());
2326        assert!(m.on_column_click.is_none());
2327        m.set_on_row_click(RefAny::new(1_u32), rcb);
2328        m.set_on_row_click(RefAny::new(2_u32), rcb);
2329        assert!(m.on_row_click.is_some());
2330        let mut payload = m.on_row_click.as_ref().expect("just set").refany.clone();
2331        assert_eq!(
2332            *payload.downcast_ref::<u32>().expect("u32 payload"),
2333            2,
2334            "the second registration replaces the first"
2335        );
2336
2337        let lv = ListView::default()
2338            .with_on_row_click(RefAny::new(()), rcb)
2339            .with_on_column_click(RefAny::new(()), ccb);
2340        assert!(lv.on_row_click.is_some());
2341        assert!(lv.on_column_click.is_some());
2342        assert!(
2343            lv.on_lazy_load_scroll.is_none(),
2344            "unrelated hooks stay unset"
2345        );
2346    }
2347
2348    // ------------------------------------------------------------------
2349    // `swap_with_default`
2350    // ------------------------------------------------------------------
2351
2352    #[test]
2353    fn swap_with_default_moves_state_out_and_leaves_a_pristine_default() {
2354        let rcb: ListViewOnRowClickCallbackType = noop_row_cb;
2355        let mut lv = ListView::create(cols(&["a", "b"]))
2356            .with_rows(ListViewRowVec::from_vec(vec![row_with(1)]))
2357            .with_sorted_by(Some(1_usize).into())
2358            .with_scroll_offset(px(9.0))
2359            .with_content_height(px(1000.0))
2360            .with_column_context_menu(Menu::create(MenuItemVec::from_const_slice(&[])))
2361            .with_on_row_click(RefAny::new(()), rcb);
2362
2363        let taken = lv.swap_with_default();
2364        assert_eq!(taken.columns.len(), 2);
2365        assert_eq!(taken.rows.len(), 1);
2366        assert_eq!(taken.sorted_by.as_ref().copied(), Some(1));
2367        assert_eq!(taken.scroll_offset, px(9.0));
2368        assert!(taken.content_height.is_some());
2369        assert!(taken.column_context_menu.is_some());
2370        assert!(taken.on_row_click.is_some());
2371
2372        assert!(lv.columns.is_empty());
2373        assert!(lv.rows.is_empty());
2374        assert!(lv.sorted_by.is_none());
2375        assert_eq!(lv.scroll_offset, PixelValueNoPercent::zero());
2376        assert!(lv.content_height.is_none());
2377        assert!(lv.column_context_menu.is_none());
2378        assert!(lv.on_row_click.is_none());
2379        assert!(lv.on_column_click.is_none());
2380        assert!(lv.on_lazy_load_scroll.is_none());
2381
2382        // Repeated swaps of an already-default value are a no-op, not a
2383        // double-free of the moved-out heap buffers.
2384        let again = lv.swap_with_default();
2385        assert!(again.columns.is_empty() && again.rows.is_empty());
2386        let third = lv.swap_with_default();
2387        assert!(third.columns.is_empty() && third.rows.is_empty());
2388        drop(taken);
2389    }
2390
2391    // ------------------------------------------------------------------
2392    // `dom()` โ€” shape + callback wiring
2393    // ------------------------------------------------------------------
2394
2395    #[test]
2396    fn dom_shape_matches_the_column_and_row_counts() {
2397        let empty = ListView::default().dom();
2398        let (h, r) = header_and_rows(&empty);
2399        assert!(h.children.as_ref().is_empty());
2400        assert!(r.children.as_ref().is_empty());
2401
2402        // Columns without rows and rows without columns are both legal.
2403        let no_rows = ListView::create(cols(&["a", "b"])).dom();
2404        let (h, r) = header_and_rows(&no_rows);
2405        assert_eq!(h.children.as_ref().len(), 2);
2406        assert!(r.children.as_ref().is_empty());
2407
2408        let no_cols = ListView::default()
2409            .with_rows(ListViewRowVec::from_vec(vec![row_with(3)]))
2410            .dom();
2411        let (h, r) = header_and_rows(&no_cols);
2412        assert!(h.children.as_ref().is_empty());
2413        assert_eq!(r.children.as_ref().len(), 1);
2414        assert_eq!(
2415            r.children.as_ref()[0].children.as_ref().len(),
2416            3,
2417            "cells are rendered even with no matching column headers"
2418        );
2419
2420        // Ragged rows keep their own cell counts (no padding to the column count).
2421        let ragged = ListView::create(cols(&["a", "b", "c"]))
2422            .with_rows(ListViewRowVec::from_vec(vec![
2423                row_with(0),
2424                row_with(1),
2425                row_with(3),
2426                row_with(7),
2427            ]))
2428            .dom();
2429        let (h, r) = header_and_rows(&ragged);
2430        assert_eq!(h.children.as_ref().len(), 3);
2431        let rows = r.children.as_ref();
2432        assert_eq!(rows.len(), 4);
2433        for (row, want) in rows.iter().zip([0_usize, 1, 3, 7]) {
2434            assert_eq!(row.children.as_ref().len(), want);
2435            for cell in row.children.as_ref() {
2436                assert_eq!(cell.children.as_ref().len(), 1, "one child per cell");
2437            }
2438        }
2439    }
2440
2441    /// The wired-in `MouseUp` handler must receive the *right* index and a
2442    /// faithful snapshot of the list state โ€” this exercises the exact
2443    /// `downcast_ref` path `on_list_view_{row,column}_click` take.
2444    #[test]
2445    fn dom_wires_click_payloads_with_the_right_index_and_state_snapshot() {
2446        let rcb: ListViewOnRowClickCallbackType = noop_row_cb;
2447        let ccb: ListViewOnColumnClickCallbackType = noop_col_cb;
2448        let lv = ListView::create(cols(&["c0", "c1"]))
2449            .with_rows(ListViewRowVec::from_vec(vec![
2450                row_with(2),
2451                row_with(2),
2452                row_with(2),
2453            ]))
2454            .with_sorted_by(Some(1_usize).into())
2455            .with_scroll_offset(px(-17.5))
2456            .with_on_row_click(RefAny::new(()), rcb)
2457            .with_on_column_click(RefAny::new(()), ccb);
2458
2459        let dom = lv.dom();
2460        let (header, rows) = header_and_rows(&dom);
2461
2462        for (i, col) in header.children.as_ref().iter().enumerate() {
2463            let cbs = col.root.callbacks.as_ref();
2464            assert_eq!(cbs.len(), 1);
2465            assert!(matches!(
2466                cbs[0].event,
2467                EventFilter::Hover(HoverEventFilter::MouseUp)
2468            ));
2469            assert_eq!(cbs[0].callback.cb, on_list_view_column_click as usize);
2470            let mut any = cbs[0].refany.clone();
2471            let data = any
2472                .downcast_ref::<ColumnClickData>()
2473                .expect("ColumnClickData payload");
2474            assert_eq!(data.col_index, i, "each header carries its own index");
2475            assert_eq!(data.state.current_row_count, 3);
2476            assert_eq!(data.state.columns.len(), 2);
2477            assert_eq!(data.state.sorted_by.as_ref().copied(), Some(1));
2478            assert_eq!(data.state.scroll_offset, px(-17.5));
2479            assert!(data.on_column_click.is_some());
2480        }
2481
2482        for (i, row) in rows.children.as_ref().iter().enumerate() {
2483            let cbs = row.root.callbacks.as_ref();
2484            assert_eq!(cbs.len(), 1);
2485            assert!(matches!(
2486                cbs[0].event,
2487                EventFilter::Hover(HoverEventFilter::MouseUp)
2488            ));
2489            assert_eq!(cbs[0].callback.cb, on_list_view_row_click as usize);
2490            let mut any = cbs[0].refany.clone();
2491            let data = any
2492                .downcast_ref::<RowClickData>()
2493                .expect("RowClickData payload");
2494            assert_eq!(data.row_index, i, "each row carries its own index");
2495            assert_eq!(data.state.current_row_count, 3);
2496            assert!(data.on_row_click.is_some());
2497        }
2498    }
2499
2500    /// Row and column hooks are wired independently โ€” setting one must not
2501    /// attach a dispatcher to the other.
2502    #[test]
2503    fn click_callbacks_are_wired_per_hook_and_only_when_set() {
2504        let rcb: ListViewOnRowClickCallbackType = noop_row_cb;
2505        let ccb: ListViewOnColumnClickCallbackType = noop_col_cb;
2506
2507        let bare = ListView::create(cols(&["a", "b"])).dom();
2508        let (h, _) = header_and_rows(&bare);
2509        for col in h.children.as_ref() {
2510            assert!(
2511                col.root.callbacks.as_ref().is_empty(),
2512                "no hook, no dispatch"
2513            );
2514        }
2515
2516        let row_only = ListView::create(cols(&["a"]))
2517            .with_rows(ListViewRowVec::from_vec(vec![row_with(1)]))
2518            .with_on_row_click(RefAny::new(()), rcb)
2519            .dom();
2520        let (h, r) = header_and_rows(&row_only);
2521        assert!(h.children.as_ref()[0].root.callbacks.as_ref().is_empty());
2522        assert_eq!(r.children.as_ref()[0].root.callbacks.as_ref().len(), 1);
2523
2524        let col_only = ListView::create(cols(&["a"]))
2525            .with_rows(ListViewRowVec::from_vec(vec![row_with(1)]))
2526            .with_on_column_click(RefAny::new(()), ccb)
2527            .dom();
2528        let (h, r) = header_and_rows(&col_only);
2529        assert_eq!(h.children.as_ref()[0].root.callbacks.as_ref().len(), 1);
2530        assert!(r.children.as_ref()[0].root.callbacks.as_ref().is_empty());
2531    }
2532
2533    /// A wrong-typed payload must make the internal handlers bail out rather
2534    /// than reinterpreting foreign memory. `CallbackInfo` cannot be built here
2535    /// without the full `LayoutWindow` harness, so this pins the guard that
2536    /// runs *before* any `CallbackInfo` use: the `downcast_ref` type check.
2537    #[test]
2538    fn click_handler_payload_downcast_rejects_foreign_types() {
2539        let mut wrong = RefAny::new(0_u64);
2540        assert!(
2541            wrong.downcast_ref::<RowClickData>().is_none(),
2542            "handler must not accept a foreign payload type"
2543        );
2544        assert!(wrong.downcast_ref::<ColumnClickData>().is_none());
2545
2546        // ... and a RowClickData payload is not mistaken for a ColumnClickData.
2547        let mut row_payload = RefAny::new(RowClickData {
2548            row_index: 0,
2549            state: ListViewState {
2550                columns: StringVec::from_const_slice(&[]),
2551                sorted_by: None.into(),
2552                current_row_count: 0,
2553                scroll_offset: PixelValueNoPercent::zero(),
2554                current_scroll_position: LogicalPosition::zero(),
2555                current_content_height: LogicalSize::zero(),
2556            },
2557            on_row_click: None.into(),
2558        });
2559        assert!(row_payload.downcast_ref::<ColumnClickData>().is_none());
2560        assert!(row_payload.downcast_ref::<RowClickData>().is_some());
2561    }
2562
2563    #[test]
2564    fn dom_survives_a_large_column_and_row_count() {
2565        const N_COLS: usize = 64;
2566        const N_ROWS: usize = 64;
2567        let rcb: ListViewOnRowClickCallbackType = noop_row_cb;
2568        let names = (0..N_COLS)
2569            .map(|i| AzString::from(format!("col{i}")))
2570            .collect::<Vec<_>>();
2571        let rows = (0..N_ROWS).map(|_| row_with(N_COLS)).collect::<Vec<_>>();
2572        let dom = ListView::create(StringVec::from_vec(names))
2573            .with_rows(ListViewRowVec::from_vec(rows))
2574            .with_on_row_click(RefAny::new(()), rcb)
2575            .dom();
2576
2577        let (h, r) = header_and_rows(&dom);
2578        assert_eq!(h.children.as_ref().len(), N_COLS);
2579        assert_eq!(r.children.as_ref().len(), N_ROWS);
2580        for row in r.children.as_ref() {
2581            assert_eq!(row.children.as_ref().len(), N_COLS);
2582            assert_eq!(row.root.callbacks.as_ref().len(), 1);
2583        }
2584    }
2585}