1
//! Native-styled tab widget consisting of a [`TabHeader`] (the clickable tab bar)
2
//! and [`TabContent`] (the panel shown for the active tab).
3
//!
4
//! Styling emulates the Windows-native tab control appearance via inline CSS
5
//! constants.
6

            
7
use azul_core::{
8
    callbacks::{CoreCallback, CoreCallbackData, Update},
9
    dom::{Dom, DomVec, EventFilter, HoverEventFilter, IdOrClass, IdOrClass::Class, IdOrClassVec},
10
    refany::RefAny,
11
};
12
#[allow(clippy::wildcard_imports)] // widget/render module pulls in the css property/value types it builds with
13
use azul_css::{
14
    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
15
    props::{
16
        basic::*,
17
        layout::*,
18
        property::{CssProperty, *},
19
        style::*,
20
    },
21
    *,
22
};
23
use azul_css::css::BoxOrStatic;
24

            
25
use crate::callbacks::{Callback, CallbackInfo};
26

            
27
const STRING_16146701490593874959: AzString = AzString::from_const_str("system:ui");
28
const STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS: &[StyleBackgroundContent] =
29
    &[StyleBackgroundContent::LinearGradient(LinearGradient {
30
        direction: Direction::FromTo(DirectionCorners {
31
            dir_from: DirectionCorner::Top,
32
            dir_to: DirectionCorner::Bottom,
33
        }),
34
        extend_mode: ExtendMode::Clamp,
35
        stops: NormalizedLinearColorStopVec::from_const_slice(
36
            LINEAR_COLOR_STOP_1400070954008106244_ITEMS,
37
        ),
38
    })];
39

            
40
const STYLE_BACKGROUND_CONTENT_15534185073326444643_ITEMS: &[StyleBackgroundContent] =
41
    &[StyleBackgroundContent::LinearGradient(LinearGradient {
42
        direction: Direction::FromTo(DirectionCorners {
43
            dir_from: DirectionCorner::Top,
44
            dir_to: DirectionCorner::Bottom,
45
        }),
46
        extend_mode: ExtendMode::Clamp,
47
        stops: NormalizedLinearColorStopVec::from_const_slice(
48
            LINEAR_COLOR_STOP_16259001466875079747_ITEMS,
49
        ),
50
    })];
51
const STYLE_BACKGROUND_CONTENT_16746671892555275291_ITEMS: &[StyleBackgroundContent] =
52
    &[StyleBackgroundContent::Color(ColorU {
53
        r: 255,
54
        g: 255,
55
        b: 255,
56
        a: 255,
57
    })];
58
const STYLE_FONT_FAMILY_8122988506401935406_ITEMS: &[StyleFontFamily] =
59
    &[StyleFontFamily::System(STRING_16146701490593874959)];
60
const LINEAR_COLOR_STOP_1400070954008106244_ITEMS: &[NormalizedLinearColorStop] = &[
61
    NormalizedLinearColorStop {
62
        offset: PercentageValue::const_new(0),
63
        color: ColorOrSystem::color(ColorU {
64
            r: 240,
65
            g: 240,
66
            b: 240,
67
            a: 255,
68
        }),
69
    },
70
    NormalizedLinearColorStop {
71
        offset: PercentageValue::const_new(100),
72
        color: ColorOrSystem::color(ColorU {
73
            r: 229,
74
            g: 229,
75
            b: 229,
76
            a: 255,
77
        }),
78
    },
79
];
80
const LINEAR_COLOR_STOP_16259001466875079747_ITEMS: &[NormalizedLinearColorStop] = &[
81
    NormalizedLinearColorStop {
82
        offset: PercentageValue::const_new(0),
83
        color: ColorOrSystem::color(ColorU {
84
            r: 236,
85
            g: 244,
86
            b: 252,
87
            a: 255,
88
        }),
89
    },
90
    NormalizedLinearColorStop {
91
        offset: PercentageValue::const_new(100),
92
        color: ColorOrSystem::color(ColorU {
93
            r: 221,
94
            g: 237,
95
            b: 252,
96
            a: 255,
97
        }),
98
    },
99
];
100

            
101
const CSS_MATCH_13824480602841492081_PROPERTIES: &[CssPropertyWithConditions] = &[
102
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active:hover
103
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
104
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
105
            inner: PixelValue::const_px(1),
106
        }),
107
    )),
108
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftWidth(
109
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
110
            inner: PixelValue::const_px(1),
111
        }),
112
    )),
113
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightWidth(
114
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
115
            inner: PixelValue::const_px(1),
116
        }),
117
    )),
118
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopWidth(
119
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
120
            inner: PixelValue::const_px(1),
121
        }),
122
    )),
123
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
124
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
125
            inner: BorderStyle::Solid,
126
        }),
127
    )),
128
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftStyle(
129
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
130
            inner: BorderStyle::Solid,
131
        }),
132
    )),
133
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightStyle(
134
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
135
            inner: BorderStyle::Solid,
136
        }),
137
    )),
138
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopStyle(
139
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
140
            inner: BorderStyle::Solid,
141
        }),
142
    )),
143
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
144
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
145
            inner: ColorU {
146
                r: 126,
147
                g: 180,
148
                b: 234,
149
                a: 255,
150
            },
151
        }),
152
    )),
153
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftColor(
154
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
155
            inner: ColorU {
156
                r: 126,
157
                g: 180,
158
                b: 234,
159
                a: 255,
160
            },
161
        }),
162
    )),
163
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
164
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
165
            inner: ColorU {
166
                r: 126,
167
                g: 180,
168
                b: 234,
169
                a: 255,
170
            },
171
        }),
172
    )),
173
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
174
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
175
            inner: ColorU {
176
                r: 126,
177
                g: 180,
178
                b: 234,
179
                a: 255,
180
            },
181
        }),
182
    )),
183
    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
184
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
185
            STYLE_BACKGROUND_CONTENT_15534185073326444643_ITEMS,
186
        )),
187
    )),
188
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-noleftborder
189
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
190
        LayoutBorderLeftWidthValue::None,
191
    )),
192
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
193
        StyleBorderLeftStyleValue::None,
194
    )),
195
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
196
        StyleBorderLeftColorValue::None,
197
    )),
198
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active
199
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
200
        LayoutPaddingRight {
201
            inner: PixelValue::const_px(5),
202
        },
203
    ))),
204
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
205
        LayoutPaddingLeft {
206
            inner: PixelValue::const_px(5),
207
        },
208
    ))),
209
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
210
        LayoutPaddingBottom {
211
            inner: PixelValue::const_px(1),
212
        },
213
    ))),
214
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
215
        LayoutPaddingTop {
216
            inner: PixelValue::const_px(1),
217
        },
218
    ))),
219
    CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
220
        LayoutMarginTop {
221
            inner: PixelValue::const_px(2),
222
        },
223
    ))),
224
    // .__azul-native-tabs-header p
225
    CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
226
        StyleTextAlign::Center,
227
    ))),
228
    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
229
        LayoutHeight::Px(PixelValue::const_px(21)),
230
    ))),
231
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
232
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
233
            inner: PixelValue::const_px(1),
234
        }),
235
    )),
236
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
237
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
238
            inner: PixelValue::const_px(1),
239
        }),
240
    )),
241
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
242
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
243
            inner: PixelValue::const_px(1),
244
        }),
245
    )),
246
    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
247
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
248
            inner: PixelValue::const_px(1),
249
        }),
250
    )),
251
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
252
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
253
            inner: BorderStyle::Solid,
254
        }),
255
    )),
256
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
257
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
258
            inner: BorderStyle::Solid,
259
        }),
260
    )),
261
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
262
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
263
            inner: BorderStyle::Solid,
264
        }),
265
    )),
266
    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
267
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
268
            inner: BorderStyle::Solid,
269
        }),
270
    )),
271
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
272
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
273
            inner: ColorU {
274
                r: 172,
275
                g: 172,
276
                b: 172,
277
                a: 255,
278
            },
279
        }),
280
    )),
281
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
282
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
283
            inner: ColorU {
284
                r: 172,
285
                g: 172,
286
                b: 172,
287
                a: 255,
288
            },
289
        }),
290
    )),
291
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
292
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
293
            inner: ColorU {
294
                r: 172,
295
                g: 172,
296
                b: 172,
297
                a: 255,
298
            },
299
        }),
300
    )),
301
    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
302
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
303
            inner: ColorU {
304
                r: 172,
305
                g: 172,
306
                b: 172,
307
                a: 255,
308
            },
309
        }),
310
    )),
311
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
312
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
313
            STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS,
314
        )),
315
    )),
316
    CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
317
        LayoutAlignItems::Center,
318
    ))),
319
];
320
const CSS_MATCH_13824480602841492081: CssPropertyWithConditionsVec =
321
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_13824480602841492081_PROPERTIES);
322

            
323
const CSS_MATCH_14575853790110873394_PROPERTIES: &[CssPropertyWithConditions] = &[
324
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-active
325
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
326
        LayoutPaddingRight {
327
            inner: PixelValue::const_px(7),
328
        },
329
    ))),
330
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
331
        LayoutPaddingLeft {
332
            inner: PixelValue::const_px(7),
333
        },
334
    ))),
335
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
336
        LayoutPaddingBottom {
337
            inner: PixelValue::const_px(3),
338
        },
339
    ))),
340
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
341
        LayoutPaddingTop {
342
            inner: PixelValue::const_px(3),
343
        },
344
    ))),
345
    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
346
        LayoutHeight::Px(PixelValue::const_px(23)),
347
    ))),
348
    CssPropertyWithConditions::simple(CssProperty::BoxSizing(LayoutBoxSizingValue::Exact(
349
        LayoutBoxSizing::ContentBox,
350
    ))),
351
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
352
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
353
            inner: PixelValue::const_px(1),
354
        }),
355
    )),
356
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
357
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
358
            inner: BorderStyle::Solid,
359
        }),
360
    )),
361
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
362
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
363
            inner: ColorU {
364
                r: 255,
365
                g: 255,
366
                b: 255,
367
                a: 255,
368
            },
369
        }),
370
    )),
371
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
372
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
373
            STYLE_BACKGROUND_CONTENT_16746671892555275291_ITEMS,
374
        )),
375
    )),
376
    // .__azul-native-tabs-header p
377
    CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
378
        StyleTextAlign::Center,
379
    ))),
380
    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
381
        LayoutHeight::Px(PixelValue::const_px(21)),
382
    ))),
383
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
384
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
385
            inner: PixelValue::const_px(1),
386
        }),
387
    )),
388
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
389
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
390
            inner: PixelValue::const_px(1),
391
        }),
392
    )),
393
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
394
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
395
            inner: PixelValue::const_px(1),
396
        }),
397
    )),
398
    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
399
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
400
            inner: PixelValue::const_px(1),
401
        }),
402
    )),
403
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
404
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
405
            inner: BorderStyle::Solid,
406
        }),
407
    )),
408
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
409
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
410
            inner: BorderStyle::Solid,
411
        }),
412
    )),
413
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
414
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
415
            inner: BorderStyle::Solid,
416
        }),
417
    )),
418
    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
419
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
420
            inner: BorderStyle::Solid,
421
        }),
422
    )),
423
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
424
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
425
            inner: ColorU {
426
                r: 172,
427
                g: 172,
428
                b: 172,
429
                a: 255,
430
            },
431
        }),
432
    )),
433
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
434
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
435
            inner: ColorU {
436
                r: 172,
437
                g: 172,
438
                b: 172,
439
                a: 255,
440
            },
441
        }),
442
    )),
443
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
444
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
445
            inner: ColorU {
446
                r: 172,
447
                g: 172,
448
                b: 172,
449
                a: 255,
450
            },
451
        }),
452
    )),
453
    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
454
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
455
            inner: ColorU {
456
                r: 172,
457
                g: 172,
458
                b: 172,
459
                a: 255,
460
            },
461
        }),
462
    )),
463
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
464
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
465
            STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS,
466
        )),
467
    )),
468
    CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
469
        LayoutAlignItems::Center,
470
    ))),
471
];
472
const CSS_MATCH_14575853790110873394: CssPropertyWithConditionsVec =
473
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_14575853790110873394_PROPERTIES);
474

            
475
const CSS_MATCH_17290739305197504468_PROPERTIES: &[CssPropertyWithConditions] = &[
476
    // .__azul-native-tabs-header .__azul-native-tabs-before-tabs
477
    CssPropertyWithConditions::simple(CssProperty::Width(LayoutWidthValue::Exact(
478
        LayoutWidth::Px(PixelValue::const_px(2)),
479
    ))),
480
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
481
        LayoutFlexGrow {
482
            inner: FloatValue::const_new(1),
483
        },
484
    ))),
485
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
486
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
487
            inner: PixelValue::const_px(1),
488
        }),
489
    )),
490
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
491
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
492
            inner: BorderStyle::Solid,
493
        }),
494
    )),
495
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
496
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
497
            inner: ColorU {
498
                r: 172,
499
                g: 172,
500
                b: 172,
501
                a: 255,
502
            },
503
        }),
504
    )),
505
];
506
const CSS_MATCH_17290739305197504468: CssPropertyWithConditionsVec =
507
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_17290739305197504468_PROPERTIES);
508

            
509
const CSS_MATCH_18014909903571752977_PROPERTIES: &[CssPropertyWithConditions] = &[
510
    // .__azul-native-tabs-content
511
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
512
        LayoutPaddingRight {
513
            inner: PixelValue::const_px(5),
514
        },
515
    ))),
516
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
517
        LayoutPaddingLeft {
518
            inner: PixelValue::const_px(5),
519
        },
520
    ))),
521
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
522
        LayoutPaddingBottom {
523
            inner: PixelValue::const_px(5),
524
        },
525
    ))),
526
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
527
        LayoutPaddingTop {
528
            inner: PixelValue::const_px(5),
529
        },
530
    ))),
531
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
532
        LayoutFlexGrow {
533
            inner: FloatValue::const_new(1),
534
        },
535
    ))),
536
    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(LayoutBorderTopWidthValue::None)),
537
    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(StyleBorderTopStyleValue::None)),
538
    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(StyleBorderTopColorValue::None)),
539
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
540
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
541
            inner: PixelValue::const_px(1),
542
        }),
543
    )),
544
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
545
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
546
            inner: PixelValue::const_px(1),
547
        }),
548
    )),
549
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
550
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
551
            inner: PixelValue::const_px(1),
552
        }),
553
    )),
554
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
555
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
556
            inner: BorderStyle::Solid,
557
        }),
558
    )),
559
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
560
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
561
            inner: BorderStyle::Solid,
562
        }),
563
    )),
564
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
565
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
566
            inner: BorderStyle::Solid,
567
        }),
568
    )),
569
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
570
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
571
            inner: ColorU {
572
                r: 172,
573
                g: 172,
574
                b: 172,
575
                a: 255,
576
            },
577
        }),
578
    )),
579
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
580
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
581
            inner: ColorU {
582
                r: 172,
583
                g: 172,
584
                b: 172,
585
                a: 255,
586
            },
587
        }),
588
    )),
589
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
590
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
591
            inner: ColorU {
592
                r: 172,
593
                g: 172,
594
                b: 172,
595
                a: 255,
596
            },
597
        }),
598
    )),
599
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
600
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
601
            STYLE_BACKGROUND_CONTENT_16746671892555275291_ITEMS,
602
        )),
603
    )),
604
];
605
const CSS_MATCH_18014909903571752977: CssPropertyWithConditionsVec =
606
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_18014909903571752977_PROPERTIES);
607

            
608
const CSS_MATCH_3088386549906605418_PROPERTIES: &[CssPropertyWithConditions] = &[
609
    // .__azul-native-tabs-header .__azul-native-tabs-after-tabs
610
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
611
        LayoutFlexGrow {
612
            inner: FloatValue::const_new(1),
613
        },
614
    ))),
615
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
616
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
617
            inner: PixelValue::const_px(1),
618
        }),
619
    )),
620
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
621
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
622
            inner: BorderStyle::Solid,
623
        }),
624
    )),
625
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
626
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
627
            inner: ColorU {
628
                r: 172,
629
                g: 172,
630
                b: 172,
631
                a: 255,
632
            },
633
        }),
634
    )),
635
];
636
const CSS_MATCH_3088386549906605418: CssPropertyWithConditionsVec =
637
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_3088386549906605418_PROPERTIES);
638

            
639
const CSS_MATCH_4415083954137121609_PROPERTIES: &[CssPropertyWithConditions] = &[
640
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active:hover
641
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
642
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
643
            inner: PixelValue::const_px(1),
644
        }),
645
    )),
646
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftWidth(
647
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
648
            inner: PixelValue::const_px(1),
649
        }),
650
    )),
651
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightWidth(
652
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
653
            inner: PixelValue::const_px(1),
654
        }),
655
    )),
656
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopWidth(
657
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
658
            inner: PixelValue::const_px(1),
659
        }),
660
    )),
661
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
662
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
663
            inner: BorderStyle::Solid,
664
        }),
665
    )),
666
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftStyle(
667
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
668
            inner: BorderStyle::Solid,
669
        }),
670
    )),
671
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightStyle(
672
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
673
            inner: BorderStyle::Solid,
674
        }),
675
    )),
676
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopStyle(
677
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
678
            inner: BorderStyle::Solid,
679
        }),
680
    )),
681
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
682
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
683
            inner: ColorU {
684
                r: 126,
685
                g: 180,
686
                b: 234,
687
                a: 255,
688
            },
689
        }),
690
    )),
691
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftColor(
692
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
693
            inner: ColorU {
694
                r: 126,
695
                g: 180,
696
                b: 234,
697
                a: 255,
698
            },
699
        }),
700
    )),
701
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
702
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
703
            inner: ColorU {
704
                r: 126,
705
                g: 180,
706
                b: 234,
707
                a: 255,
708
            },
709
        }),
710
    )),
711
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
712
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
713
            inner: ColorU {
714
                r: 126,
715
                g: 180,
716
                b: 234,
717
                a: 255,
718
            },
719
        }),
720
    )),
721
    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
722
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
723
            STYLE_BACKGROUND_CONTENT_15534185073326444643_ITEMS,
724
        )),
725
    )),
726
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-norightborder
727
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
728
        LayoutBorderRightWidthValue::None,
729
    )),
730
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
731
        StyleBorderRightStyleValue::None,
732
    )),
733
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
734
        StyleBorderRightColorValue::None,
735
    )),
736
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active
737
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
738
        LayoutPaddingRight {
739
            inner: PixelValue::const_px(5),
740
        },
741
    ))),
742
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
743
        LayoutPaddingLeft {
744
            inner: PixelValue::const_px(5),
745
        },
746
    ))),
747
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
748
        LayoutPaddingBottom {
749
            inner: PixelValue::const_px(1),
750
        },
751
    ))),
752
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
753
        LayoutPaddingTop {
754
            inner: PixelValue::const_px(1),
755
        },
756
    ))),
757
    CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
758
        LayoutMarginTop {
759
            inner: PixelValue::const_px(2),
760
        },
761
    ))),
762
    // .__azul-native-tabs-header p
763
    CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
764
        StyleTextAlign::Center,
765
    ))),
766
    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
767
        LayoutHeight::Px(PixelValue::const_px(21)),
768
    ))),
769
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
770
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
771
            inner: PixelValue::const_px(1),
772
        }),
773
    )),
774
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
775
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
776
            inner: PixelValue::const_px(1),
777
        }),
778
    )),
779
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
780
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
781
            inner: PixelValue::const_px(1),
782
        }),
783
    )),
784
    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
785
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
786
            inner: PixelValue::const_px(1),
787
        }),
788
    )),
789
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
790
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
791
            inner: BorderStyle::Solid,
792
        }),
793
    )),
794
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
795
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
796
            inner: BorderStyle::Solid,
797
        }),
798
    )),
799
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
800
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
801
            inner: BorderStyle::Solid,
802
        }),
803
    )),
804
    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
805
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
806
            inner: BorderStyle::Solid,
807
        }),
808
    )),
809
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
810
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
811
            inner: ColorU {
812
                r: 172,
813
                g: 172,
814
                b: 172,
815
                a: 255,
816
            },
817
        }),
818
    )),
819
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
820
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
821
            inner: ColorU {
822
                r: 172,
823
                g: 172,
824
                b: 172,
825
                a: 255,
826
            },
827
        }),
828
    )),
829
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
830
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
831
            inner: ColorU {
832
                r: 172,
833
                g: 172,
834
                b: 172,
835
                a: 255,
836
            },
837
        }),
838
    )),
839
    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
840
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
841
            inner: ColorU {
842
                r: 172,
843
                g: 172,
844
                b: 172,
845
                a: 255,
846
            },
847
        }),
848
    )),
849
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
850
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
851
            STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS,
852
        )),
853
    )),
854
    CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
855
        LayoutAlignItems::Center,
856
    ))),
857
];
858
const CSS_MATCH_4415083954137121609: CssPropertyWithConditionsVec =
859
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4415083954137121609_PROPERTIES);
860

            
861
const CSS_MATCH_4738503469417034630_PROPERTIES: &[CssPropertyWithConditions] = &[
862
    // .__azul-native-tabs-container
863
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
864
        LayoutPaddingRight {
865
            inner: PixelValue::const_px(5),
866
        },
867
    ))),
868
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
869
        LayoutPaddingLeft {
870
            inner: PixelValue::const_px(5),
871
        },
872
    ))),
873
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
874
        LayoutPaddingBottom {
875
            inner: PixelValue::const_px(5),
876
        },
877
    ))),
878
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
879
        LayoutPaddingTop {
880
            inner: PixelValue::const_px(5),
881
        },
882
    ))),
883
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
884
        LayoutFlexGrow {
885
            inner: FloatValue::const_new(1),
886
        },
887
    ))),
888
];
889
const CSS_MATCH_4738503469417034630: CssPropertyWithConditionsVec =
890
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4738503469417034630_PROPERTIES);
891

            
892
const CSS_MATCH_9988039989460234263_PROPERTIES: &[CssPropertyWithConditions] = &[
893
    // .__azul-native-tabs-header
894
    CssPropertyWithConditions::simple(CssProperty::FontSize(StyleFontSizeValue::Exact(
895
        StyleFontSize {
896
            inner: PixelValue::const_px(11),
897
        },
898
    ))),
899
    CssPropertyWithConditions::simple(CssProperty::FontFamily(StyleFontFamilyVecValue::Exact(
900
        StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_8122988506401935406_ITEMS),
901
    ))),
902
    CssPropertyWithConditions::simple(CssProperty::FlexDirection(LayoutFlexDirectionValue::Exact(
903
        LayoutFlexDirection::Row,
904
    ))),
905
];
906
const CSS_MATCH_9988039989460234263: CssPropertyWithConditionsVec =
907
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_9988039989460234263_PROPERTIES);
908

            
909
// -- NO PADDING
910
const CSS_MATCH_18014909903571752977_PROPERTIES_NO_PADDING: &[CssPropertyWithConditions] = &[
911
    // .__azul-native-tabs-content
912
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
913
        LayoutFlexGrow {
914
            inner: FloatValue::const_new(1),
915
        },
916
    ))),
917
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
918
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
919
            STYLE_BACKGROUND_CONTENT_16746671892555275291_ITEMS,
920
        )),
921
    )),
922
];
923
const CSS_MATCH_18014909903571752977_NO_PADDING: CssPropertyWithConditionsVec =
924
    CssPropertyWithConditionsVec::from_const_slice(
925
        CSS_MATCH_18014909903571752977_PROPERTIES_NO_PADDING,
926
    );
927

            
928
const CSS_MATCH_4738503469417034630_PROPERTIES_NO_PADDING: &[CssPropertyWithConditions] = &[
929
    // .__azul-native-tabs-container
930
    CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
931
        LayoutFlexGrow {
932
            inner: FloatValue::const_new(1),
933
        },
934
    ))),
935
];
936
const CSS_MATCH_4738503469417034630_NO_PADDING: CssPropertyWithConditionsVec =
937
    CssPropertyWithConditionsVec::from_const_slice(
938
        CSS_MATCH_4738503469417034630_PROPERTIES_NO_PADDING,
939
    );
940

            
941
// -- REGULAR_INACTIVE_TAB
942

            
943
const CSS_MATCH_11510695043643111367_PROPERTIES: &[CssPropertyWithConditions] = &[
944
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active:hover
945
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomWidth(
946
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
947
            inner: PixelValue::const_px(1),
948
        }),
949
    )),
950
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftWidth(
951
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
952
            inner: PixelValue::const_px(1),
953
        }),
954
    )),
955
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightWidth(
956
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
957
            inner: PixelValue::const_px(1),
958
        }),
959
    )),
960
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopWidth(
961
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
962
            inner: PixelValue::const_px(1),
963
        }),
964
    )),
965
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomStyle(
966
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
967
            inner: BorderStyle::Solid,
968
        }),
969
    )),
970
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftStyle(
971
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
972
            inner: BorderStyle::Solid,
973
        }),
974
    )),
975
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightStyle(
976
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
977
            inner: BorderStyle::Solid,
978
        }),
979
    )),
980
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopStyle(
981
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
982
            inner: BorderStyle::Solid,
983
        }),
984
    )),
985
    CssPropertyWithConditions::on_hover(CssProperty::BorderBottomColor(
986
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
987
            inner: ColorU {
988
                r: 126,
989
                g: 180,
990
                b: 234,
991
                a: 255,
992
            },
993
        }),
994
    )),
995
    CssPropertyWithConditions::on_hover(CssProperty::BorderLeftColor(
996
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
997
            inner: ColorU {
998
                r: 126,
999
                g: 180,
                b: 234,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
            inner: ColorU {
                r: 126,
                g: 180,
                b: 234,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
            inner: ColorU {
                r: 126,
                g: 180,
                b: 234,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
            STYLE_BACKGROUND_CONTENT_15534185073326444643_ITEMS,
        )),
    )),
    // .__azul-native-tabs-header p.__azul-native-tabs-tab-not-active
    CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
        LayoutPaddingRight {
            inner: PixelValue::const_px(5),
        },
    ))),
    CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
        LayoutPaddingLeft {
            inner: PixelValue::const_px(5),
        },
    ))),
    CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
        LayoutPaddingBottom {
            inner: PixelValue::const_px(1),
        },
    ))),
    CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
        LayoutPaddingTop {
            inner: PixelValue::const_px(1),
        },
    ))),
    CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
        LayoutMarginTop {
            inner: PixelValue::const_px(2),
        },
    ))),
    // .__azul-native-tabs-header p
    CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
        StyleTextAlign::Center,
    ))),
    CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
        LayoutHeight::Px(PixelValue::const_px(21)),
    ))),
    CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
        LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
            inner: PixelValue::const_px(1),
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
        LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
            inner: PixelValue::const_px(1),
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
        LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
            inner: PixelValue::const_px(1),
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
        LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
            inner: PixelValue::const_px(1),
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
        StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
            inner: BorderStyle::Solid,
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
        StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
            inner: BorderStyle::Solid,
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
        StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
            inner: BorderStyle::Solid,
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
        StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
            inner: BorderStyle::Solid,
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
        StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
            inner: ColorU {
                r: 172,
                g: 172,
                b: 172,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
        StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
            inner: ColorU {
                r: 172,
                g: 172,
                b: 172,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
        StyleBorderRightColorValue::Exact(StyleBorderRightColor {
            inner: ColorU {
                r: 172,
                g: 172,
                b: 172,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
        StyleBorderTopColorValue::Exact(StyleBorderTopColor {
            inner: ColorU {
                r: 172,
                g: 172,
                b: 172,
                a: 255,
            },
        }),
    )),
    CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
        StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
            STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS,
        )),
    )),
    CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
        LayoutAlignItems::Center,
    ))),
];
const CSS_MATCH_11510695043643111367: CssPropertyWithConditionsVec =
    CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_11510695043643111367_PROPERTIES);
/// Header bar for a tab widget, containing the clickable tab labels.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct TabHeader {
    /// Labels for each tab.
    pub tabs: StringVec,
    /// Zero-based index of the currently active tab.
    pub active_tab: usize,
    /// Optional callback invoked when a tab is clicked.
    pub on_click: OptionTabOnClick,
}
impl Default for TabHeader {
4
    fn default() -> Self {
4
        Self {
4
            tabs: StringVec::from_const_slice(&[]),
4
            active_tab: 0,
4
            on_click: None.into(),
4
        }
4
    }
}
/// State passed to the tab-click callback, indicating which tab was selected.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct TabHeaderState {
    /// Zero-based index of the newly selected tab.
    pub active_tab: usize,
}
/// Signature for the tab-click callback function.
pub type TabOnClickCallbackType = extern "C" fn(RefAny, CallbackInfo, TabHeaderState) -> Update;
impl_widget_callback!(
    TabOnClick,
    OptionTabOnClick,
    TabOnClickCallback,
    TabOnClickCallbackType
);
azul_core::impl_managed_callback! {
    wrapper:        TabOnClickCallback,
    info_ty:        CallbackInfo,
    return_ty:      Update,
    default_ret:    Update::DoNothing,
    invoker_static: TAB_ON_CLICK_INVOKER,
    invoker_ty:     AzTabOnClickCallbackInvoker,
    thunk_fn:       az_tab_on_click_callback_thunk,
    setter_fn:      AzApp_setTabOnClickCallbackInvoker,
    from_handle_fn: AzTabOnClickCallback_createFromHostHandle,
    extra_args:     [ state: TabHeaderState ],
}
impl TabHeader {
79
    #[must_use] pub fn create(tabs: StringVec) -> Self {
79
        Self {
79
            tabs,
79
            active_tab: 0,
79
            on_click: None.into(),
79
        }
79
    }
    #[must_use]
4
    pub fn swap_with_default(&mut self) -> Self {
4
        let mut default = Self::default();
4
        core::mem::swap(&mut default, self);
4
        default
4
    }
64
    pub const fn set_active_tab(&mut self, active_tab: usize) {
64
        self.active_tab = active_tab;
64
    }
37
    #[must_use] pub const fn with_active_tab(mut self, active_tab: usize) -> Self {
37
        self.set_active_tab(active_tab);
37
        self
37
    }
14
    pub fn set_on_click<C: Into<TabOnClickCallback>>(&mut self, refany: RefAny, on_click: C) {
14
        self.on_click = Some(TabOnClick {
14
            refany,
14
            callback: on_click.into(),
14
        })
14
        .into();
14
    }
    #[must_use]
7
    pub fn with_on_click<C: Into<TabOnClickCallback>>(
7
        mut self,
7
        refany: RefAny,
7
        on_click: C,
7
    ) -> Self {
7
        self.set_on_click(refany, on_click);
7
        self
7
    }
    #[allow(clippy::too_many_lines)] // large but cohesive: single-purpose layout/render/parse routine (one branch per case)
50
    #[must_use] pub fn dom(self) -> Dom {
        use azul_core::callbacks::CoreCallbackDataVec;
        // classes for previous tab
        const IDS_AND_CLASSES_5117007530891373979: &[IdOrClass] = &[
            Class(AzString::from_const_str(
                "__azul-native-tabs-tab-norightborder",
            )),
            Class(AzString::from_const_str(
                "__azul-native-tabs-tab-not-active",
            )),
        ]; // CSS_MATCH_4415083954137121609
        // classes for current tab
        const IDS_AND_CLASSES_15002865554973741556: &[IdOrClass] = &[Class(
            AzString::from_const_str("__azul-native-tabs-tab-active"),
        )];
        // classes for next tab
        const IDS_AND_CLASSES_16877793354714897051: &[IdOrClass] = &[
            Class(AzString::from_const_str(
                "__azul-native-tabs-tab-noleftborder",
            )),
            Class(AzString::from_const_str(
                "__azul-native-tabs-tab-not-active",
            )),
        ];
        // classes for default inactive tab
        const IDS_AND_CLASSES_INACTIVE: &[IdOrClass] = &[Class(
            AzString::from_const_str("__azul-native-tabs-tab-not-active"),
        )];
50
        let on_click_is_some = self.on_click.is_some();
50
        Dom::create_div()
50
            .with_css_props(CSS_MATCH_9988039989460234263)
50
            .with_ids_and_classes({
                const IDS_AND_CLASSES_6172459441955124689: &[IdOrClass] =
                    &[Class(AzString::from_const_str("__azul-native-tabs-header"))];
50
                IdOrClassVec::from_const_slice(IDS_AND_CLASSES_6172459441955124689)
            })
50
            .with_children({
50
                let mut tab_items = vec![Dom::create_div()
50
                    .with_css_props(CSS_MATCH_17290739305197504468)
50
                    .with_ids_and_classes({
                        const IDS_AND_CLASSES_8360971686689797550: &[IdOrClass] = &[Class(
                            AzString::from_const_str("__azul-native-tabs-before-tabs"),
                        )];
50
                        IdOrClassVec::from_const_slice(IDS_AND_CLASSES_8360971686689797550)
                    })];
50
                let dataset = TabLocalDataset {
50
                    tab_idx: 0,
50
                    on_click: self.on_click,
50
                };
1247
                for (tab_idx, tab) in self.tabs.as_ref().iter().enumerate() {
1247
                    let next_tab_is_active = self.active_tab == tab_idx.saturating_add(1);
1247
                    let previous_tab_was_active = if self.active_tab == 0 {
1136
                        false
                    } else {
111
                        self.active_tab == tab_idx.saturating_sub(1)
                    };
1247
                    let tab_is_active = self.active_tab == tab_idx;
1247
                    let (ids_and_classes, css_props) = if tab_is_active {
36
                        (
36
                            IDS_AND_CLASSES_15002865554973741556,
36
                            CSS_MATCH_14575853790110873394,
36
                        )
1211
                    } else if next_tab_is_active {
                        // tab before the active tab
15
                        (
15
                            IDS_AND_CLASSES_5117007530891373979,
15
                            CSS_MATCH_4415083954137121609,
15
                        )
1196
                    } else if previous_tab_was_active {
                        // tab after the active tab
10
                        (
10
                            IDS_AND_CLASSES_16877793354714897051,
10
                            CSS_MATCH_13824480602841492081,
10
                        )
                    } else {
                        // default inactive tab
1186
                        (IDS_AND_CLASSES_INACTIVE, CSS_MATCH_11510695043643111367)
                    };
1247
                    let mut dataset = dataset.clone();
1247
                    dataset.tab_idx = tab_idx;
1247
                    let dataset = RefAny::new(dataset);
1247
                    tab_items.push(
1247
                        Dom::create_p_with_text(tab.clone())
1247
                            .with_callbacks(if on_click_is_some {
21
                                vec![CoreCallbackData {
21
                                    event: EventFilter::Hover(HoverEventFilter::MouseUp),
21
                                    callback: CoreCallback {
21
                                        cb: on_tab_click as usize,
21
                                        ctx: azul_core::refany::OptionRefAny::None,
21
                                    },
21
                                    refany: dataset.clone(),
21
                                }]
21
                                .into()
                            } else {
1226
                                CoreCallbackDataVec::from_const_slice(&[])
                            })
1247
                            .with_dataset(Some(dataset).into())
1247
                            .with_css_props(css_props)
1247
                            .with_ids_and_classes(IdOrClassVec::from_const_slice(ids_and_classes)),
                    );
                }
50
                tab_items.push(
50
                    Dom::create_div()
50
                        .with_css_props(CSS_MATCH_3088386549906605418)
50
                        .with_ids_and_classes({
                            const IDS_AND_CLASSES_11001585590816277275: &[IdOrClass] = &[Class(
                                AzString::from_const_str("__azul-native-tabs-after-tabs"),
                            )];
50
                            IdOrClassVec::from_const_slice(IDS_AND_CLASSES_11001585590816277275)
                        }),
                );
50
                tab_items.into()
            })
50
    }
}
/// Content panel displayed beneath the active tab in a tab widget.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct TabContent {
    /// The DOM subtree shown as the tab's content area.
    pub content: Dom,
    /// Whether the content area includes default padding.
    pub has_padding: bool,
}
impl Default for TabContent {
2
    fn default() -> Self {
2
        Self {
2
            content: Dom::create_div(),
2
            has_padding: true,
2
        }
2
    }
}
impl TabContent {
16
    #[must_use] pub const fn new(content: Dom) -> Self {
16
        Self {
16
            content,
16
            has_padding: true,
16
        }
16
    }
    #[must_use]
1
    pub fn swap_with_default(&mut self) -> Self {
1
        let mut default = Self::default();
1
        core::mem::swap(&mut default, self);
1
        default
1
    }
8
    #[must_use] pub const fn with_padding(mut self, padding: bool) -> Self {
8
        self.set_padding(padding);
8
        self
8
    }
10
    pub const fn set_padding(&mut self, padding: bool) {
10
        self.has_padding = padding;
10
    }
9
    #[must_use] pub fn dom(self) -> Dom {
        const IDS_AND_CLASSES_2989815829020816222: &[IdOrClass] = &[Class(
            AzString::from_const_str("__azul-native-tabs-content"),
        )];
9
        let tab_content_css_style = if self.has_padding {
8
            CSS_MATCH_18014909903571752977
        } else {
1
            CSS_MATCH_18014909903571752977_NO_PADDING
        };
9
        Dom::create_div()
9
            .with_css_props(tab_content_css_style)
9
            .with_children(DomVec::from_vec(vec![Dom::create_div()
9
                .with_ids_and_classes(IdOrClassVec::from_const_slice(
                    IDS_AND_CLASSES_2989815829020816222,
                ))
9
                .with_children(DomVec::from_vec(vec![self.content]))]))
9
    }
}
#[derive(Clone)]
struct TabLocalDataset {
    tab_idx: usize,
    on_click: OptionTabOnClick,
}
23
extern "C" fn on_tab_click(mut refany: RefAny, info: CallbackInfo) -> Update {
23
    fn select_new_tab_inner(mut refany: RefAny, info: &CallbackInfo) -> Option<Update> {
23
        let mut tab_local_dataset = refany.downcast_mut::<TabLocalDataset>()?;
20
        let tab_idx = tab_local_dataset.tab_idx;
20
        let tab_header_state = TabHeaderState {
20
            active_tab: tab_idx,
20
        };
20
        let result = {
            // rustc doesn't understand the borrowing lifetime here
20
            let tab_local_dataset = &mut *tab_local_dataset;
20
            let onclick = &mut tab_local_dataset.on_click;
20
            match onclick.as_mut() {
18
                Some(TabOnClick { callback, refany }) => {
18
                    (callback.cb)(refany.clone(), *info, tab_header_state)
                }
2
                None => Update::DoNothing,
            }
        };
20
        Some(result)
23
    }
23
    select_new_tab_inner(refany, &info).unwrap_or(Update::RefreshDom)
23
}
#[cfg(test)]
mod autotest_generated {
    use std::{
        collections::BTreeMap,
        sync::{Arc, Mutex},
    };
    use azul_core::{
        dom::{DomId, DomNodeId, NodeId, NodeType},
        geom::OptionLogicalPosition,
        gl::OptionGlContextPtr,
        hit_test::ScrollPosition,
        refany::OptionRefAny,
        resources::RendererResources,
        styled_dom::{NodeHierarchyItemId, StyledDom},
        window::{MonitorVec, RawWindowHandle},
    };
    use azul_css::system::SystemStyle;
    use rust_fontconfig::FcFontCache;
    use super::*;
    #[cfg(feature = "icu")]
    use crate::icu::IcuLocalizerHandle;
    use crate::{
        callbacks::{CallbackChange, CallbackInfoRefData, ExternalSystemCallbacks},
        window::LayoutWindow,
        window_state::FullWindowState,
    };
    // ------------------------------------------------------------------
    // Helpers
    // ------------------------------------------------------------------
    const CLASS_ACTIVE: &str = "__azul-native-tabs-tab-active";
    const CLASS_NOT_ACTIVE: &str = "__azul-native-tabs-tab-not-active";
    const CLASS_NO_LEFT: &str = "__azul-native-tabs-tab-noleftborder";
    const CLASS_NO_RIGHT: &str = "__azul-native-tabs-tab-norightborder";
    const CLASS_HEADER: &str = "__azul-native-tabs-header";
    const CLASS_BEFORE: &str = "__azul-native-tabs-before-tabs";
    const CLASS_AFTER: &str = "__azul-native-tabs-after-tabs";
    const CLASS_CONTENT: &str = "__azul-native-tabs-content";
    fn strings(items: &[&str]) -> StringVec {
        StringVec::from_vec(items.iter().map(|s| AzString::from(*s)).collect())
    }
    fn numbered_labels(n: usize) -> StringVec {
        StringVec::from_vec((0..n).map(|i| AzString::from(format!("tab {i}"))).collect())
    }
    /// The text of a text node, looking through the `<p>` block wrapper the
    /// label convention mandates (`p > text`).
    fn text_of(node: &Dom) -> Option<&str> {
        match node.root.get_node_type() {
            NodeType::Text(s) => Some(s.as_ref().as_str()),
            NodeType::P => match node.children.as_ref() {
                [only] => match only.root.get_node_type() {
                    NodeType::Text(s) => Some(s.as_ref().as_str()),
                    _ => None,
                },
                _ => None,
            },
            _ => None,
        }
    }
    fn classes(node: &Dom) -> Vec<String> {
        node.root
            .get_ids_and_classes()
            .as_ref()
            .iter()
            .filter_map(|c| match c {
                Class(s) => Some(s.as_str().to_string()),
                IdOrClass::Id(_) => None,
            })
            .collect()
    }
    fn class_strs(node: &Dom) -> Vec<&'static str> {
        // The widget only ever attaches the eight `&'static str` classes above;
        // map back to them so the tests can compare against string literals.
        classes(node)
            .into_iter()
            .map(|c| {
                [
                    CLASS_ACTIVE,
                    CLASS_NOT_ACTIVE,
                    CLASS_NO_LEFT,
                    CLASS_NO_RIGHT,
                    CLASS_HEADER,
                    CLASS_BEFORE,
                    CLASS_AFTER,
                    CLASS_CONTENT,
                ]
                .into_iter()
                .find(|known| *known == c)
                .unwrap_or_else(|| panic!("tabs.rs emitted an unknown class: {c}"))
            })
            .collect()
    }
    /// A style vec as `(property, condition-count)` pairs in declaration order —
    /// the exact shape `Css::from(CssPropertyWithConditionsVec)` preserves.
    fn declared(v: &CssPropertyWithConditionsVec) -> Vec<(CssProperty, usize)> {
        v.as_ref()
            .iter()
            .map(|p| (p.property.clone(), p.apply_if.as_ref().len()))
            .collect()
    }
    /// The same view, read back off a rendered node's inline `Css`.
    fn inline_declared(node: &Dom) -> Vec<(CssProperty, usize)> {
        node.root
            .style
            .iter_inline_properties()
            .map(|(p, conds)| (p.clone(), conds.as_ref().len()))
            .collect()
    }
    /// Every declaration in `v` matching `pred`, in declaration order.
    fn decls_where(
        v: &CssPropertyWithConditionsVec,
        pred: fn(&CssProperty) -> bool,
    ) -> Vec<CssProperty> {
        v.as_ref()
            .iter()
            .filter(|p| pred(&p.property))
            .map(|p| p.property.clone())
            .collect()
    }
    /// Same, but skipping the `:hover` block — these styles declare the border
    /// properties once for hover and again unconditionally, so a raw filter would
    /// mix the two sets.
    fn plain_decls_where(
        v: &CssPropertyWithConditionsVec,
        pred: fn(&CssProperty) -> bool,
    ) -> Vec<CssProperty> {
        v.as_ref()
            .iter()
            .filter(|p| p.apply_if.as_ref().is_empty() && pred(&p.property))
            .map(|p| p.property.clone())
            .collect()
    }
    /// The style vec the widget must pair with a given class combination.
    fn style_for_classes(cls: &[&str]) -> CssPropertyWithConditionsVec {
        let cls = cls.to_vec();
        if cls == [CLASS_ACTIVE] {
            CSS_MATCH_14575853790110873394
        } else if cls == [CLASS_NO_RIGHT, CLASS_NOT_ACTIVE] {
            CSS_MATCH_4415083954137121609
        } else if cls == [CLASS_NO_LEFT, CLASS_NOT_ACTIVE] {
            CSS_MATCH_13824480602841492081
        } else if cls == [CLASS_NOT_ACTIVE] {
            CSS_MATCH_11510695043643111367
        } else {
            panic!("unexpected class combination on a tab node: {cls:?}");
        }
    }
    /// The dataset `RefAny` a rendered tab node carries (cloned, so the caller
    /// can `downcast_*` it without borrowing the Dom mutably).
    fn dataset_of(node: &Dom) -> RefAny {
        node.root
            .get_dataset()
            .expect("every tab node carries a TabLocalDataset")
            .clone()
    }
    fn tab_idx_of(node: &Dom) -> usize {
        let mut ds = dataset_of(node);
        let tab_idx = ds
            .downcast_ref::<TabLocalDataset>()
            .expect("the dataset must be a TabLocalDataset")
            .tab_idx;
        tab_idx
    }
    /// A `RefAny` payload recording every state a user `on_click` observes.
    #[derive(Default)]
    struct ClickLog {
        seen: Vec<TabHeaderState>,
    }
    extern "C" fn record_click(
        mut refany: RefAny,
        _: CallbackInfo,
        state: TabHeaderState,
    ) -> Update {
        if let Some(mut log) = refany.downcast_mut::<ClickLog>() {
            log.seen.push(state);
        }
        Update::RefreshDom
    }
    extern "C" fn click_do_nothing(_: RefAny, _: CallbackInfo, _: TabHeaderState) -> Update {
        Update::DoNothing
    }
    extern "C" fn click_refresh_all(_: RefAny, _: CallbackInfo, _: TabHeaderState) -> Update {
        Update::RefreshDomAllWindows
    }
    /// Forces the `fn`-item -> `fn`-pointer coercion the `Into` bound needs.
    fn cb(f: TabOnClickCallbackType) -> TabOnClickCallback {
        f.into()
    }
    fn logged(refany: &mut RefAny) -> Vec<TabHeaderState> {
        refany
            .downcast_ref::<ClickLog>()
            .expect("payload must still be a ClickLog")
            .seen
            .clone()
    }
    fn dataset(tab_idx: usize, on_click: OptionTabOnClick) -> RefAny {
        RefAny::new(TabLocalDataset { tab_idx, on_click })
    }
    fn click_handler(refany: RefAny, callback: TabOnClickCallbackType) -> OptionTabOnClick {
        Some(TabOnClick {
            refany,
            callback: cb(callback),
        })
        .into()
    }
    /// Invokes `on_tab_click` with a minimal `CallbackInfo` (the handler never
    /// touches the layout window — it only downcasts its own dataset — so an
    /// empty `LayoutWindow` and node 0 as the hit node are enough).
    /// Returns the `Update` plus every recorded `CallbackChange`.
    fn run_click(data: RefAny) -> (Update, Vec<CallbackChange>) {
        let layout_window =
            LayoutWindow::new(FcFontCache::default()).expect("LayoutWindow::new failed");
        let renderer_resources = RendererResources::default();
        let previous_window_state: Option<FullWindowState> = None;
        let current_window_state = FullWindowState::default();
        let gl_context = OptionGlContextPtr::None;
        let scroll_states: BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>> =
            BTreeMap::new();
        let window_handle = RawWindowHandle::Unsupported;
        let system_callbacks = ExternalSystemCallbacks::rust_internal();
        let ref_data = CallbackInfoRefData {
            layout_window: &layout_window,
            renderer_resources: &renderer_resources,
            previous_window_state: &previous_window_state,
            current_window_state: &current_window_state,
            gl_context: &gl_context,
            current_scroll_manager: &scroll_states,
            current_window_handle: &window_handle,
            system_callbacks: &system_callbacks,
            system_style: Arc::new(SystemStyle::default()),
            monitors: Arc::new(Mutex::new(MonitorVec::from_const_slice(&[]))),
            #[cfg(feature = "icu")]
            icu_localizer: IcuLocalizerHandle::default(),
            ctx: OptionRefAny::None,
        };
        let changes: Arc<Mutex<Vec<CallbackChange>>> = Arc::new(Mutex::new(Vec::new()));
        let info = CallbackInfo::new(
            &ref_data,
            &changes,
            DomNodeId {
                dom: DomId::ROOT_ID,
                node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0))),
            },
            OptionLogicalPosition::None,
            OptionLogicalPosition::None,
        );
        let update = on_tab_click(data, info);
        let recorded = core::mem::take(&mut *changes.lock().expect("change log poisoned"));
        (update, recorded)
    }
    // ==================================================================
    // TabHeader::create
    // ==================================================================
    #[test]
    fn create_keeps_the_label_vec_verbatim_and_starts_inert() {
        for labels in [
            strings(&[]),
            strings(&["only"]),
            strings(&["a", "b", "c"]),
            numbered_labels(1000),
        ] {
            let expected: Vec<AzString> = labels.as_ref().to_vec();
            let header = TabHeader::create(labels);
            assert_eq!(
                header.tabs.as_ref(),
                expected.as_slice(),
                "create must not reorder, drop or rewrite labels"
            );
            assert_eq!(header.active_tab, 0, "a fresh header selects the first tab");
            assert!(
                header.on_click.is_none(),
                "create must not install a callback"
            );
        }
    }
    #[test]
    fn create_accepts_an_empty_label_vec_and_still_renders_the_spacers() {
        // Zero tabs is the degenerate case: the loop body never runs, but the
        // before/after spacers must still be emitted or the header collapses.
        let dom = TabHeader::create(strings(&[])).dom();
        let children = dom.children.as_ref();
        assert_eq!(children.len(), 2, "an empty header is just the two spacers");
        assert_eq!(class_strs(&children[0]), vec![CLASS_BEFORE]);
        assert_eq!(class_strs(&children[1]), vec![CLASS_AFTER]);
    }
    #[test]
    fn create_round_trips_pathological_labels_through_the_dom() {
        // The label is only ever cloned into a `NodeType::Text`, never parsed,
        // trimmed or NUL-terminated — so every byte must survive verbatim.
        let long = "\u{e9}".repeat(50_000);
        let pathological: Vec<String> = vec![
            String::new(),
            " ".to_string(),
            "\0embedded\0nul\0".to_string(),
            "\u{1F600}\u{1F3F4}\u{E0067}\u{E007F}".to_string(), // emoji + tag sequence
            "a\u{0301}\u{0327}\u{0328}".to_string(),            // stacked combining marks
            "\u{202E}reversed\u{202C}".to_string(),             // RTL override
            "\r\n\t".to_string(),
            "\u{FFFD}\u{FEFF}".to_string(), // replacement char + BOM
            long.clone(),
        ];
        let labels = StringVec::from_vec(
            pathological
                .iter()
                .map(|s| AzString::from(s.clone()))
                .collect(),
        );
        let dom = TabHeader::create(labels).dom();
        let children = dom.children.as_ref();
        assert_eq!(children.len(), pathological.len() + 2);
        for (i, expected) in pathological.iter().enumerate() {
            assert_eq!(
                text_of(&children[i + 1]),
                Some(expected.as_str()),
                "label {i} did not round-trip through the DOM"
            );
        }
    }
    // ==================================================================
    // TabHeader::set_active_tab / with_active_tab  (numeric)
    // ==================================================================
    #[test]
    fn set_active_tab_stores_any_usize_without_clamping_or_panicking() {
        // `active_tab` is an unsigned index with no documented upper bound and
        // no relation to `tabs.len()` — there is no signed path to test, so the
        // adversarial values are 0, the wrap-around of 0 and the two extremes.
        let extremes = [
            0usize,
            1,
            2,
            usize::MAX / 2,
            usize::MAX - 1,
            usize::MAX,
            0usize.wrapping_sub(1),
        ];
        for value in extremes {
            let mut header = TabHeader::create(strings(&["a", "b", "c"]));
            header.set_active_tab(value);
            assert_eq!(
                header.active_tab, value,
                "set_active_tab must store the index verbatim (no clamp to len)"
            );
            // Idempotent + last-write-wins.
            header.set_active_tab(value);
            assert_eq!(header.active_tab, value);
            header.set_active_tab(0);
            assert_eq!(header.active_tab, 0, "the last write must win");
        }
    }
    #[test]
    fn set_active_tab_touches_nothing_but_the_index() {
        let mut header = TabHeader::create(strings(&["a", "b"]));
        header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
        header.set_active_tab(usize::MAX);
        assert_eq!(
            header.tabs.as_ref(),
            strings(&["a", "b"]).as_ref(),
            "the labels must be untouched"
        );
        assert!(
            header.on_click.is_some(),
            "the callback must survive a selection change"
        );
    }
    #[test]
    fn with_active_tab_agrees_with_set_active_tab_at_every_extreme() {
        for value in [0usize, 1, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
            let built = TabHeader::create(strings(&["a", "b"])).with_active_tab(value);
            let mut mutated = TabHeader::create(strings(&["a", "b"]));
            mutated.set_active_tab(value);
            assert_eq!(built.active_tab, mutated.active_tab);
            assert_eq!(built.active_tab, value);
            assert_eq!(built.tabs.as_ref(), mutated.tabs.as_ref());
            assert!(built.on_click.is_none() && mutated.on_click.is_none());
        }
    }
    #[test]
    fn with_active_tab_chains_last_wins() {
        let header = TabHeader::create(strings(&["a"]))
            .with_active_tab(usize::MAX)
            .with_active_tab(7)
            .with_active_tab(0);
        assert_eq!(header.active_tab, 0);
    }
    #[test]
    fn dom_never_marks_a_tab_active_when_the_index_is_out_of_range() {
        // An out-of-range selection must degrade to "nothing selected" rather
        // than panicking or wrapping onto some other tab.
        let n = 4usize;
        for active in [n, n + 1, n + 2, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
            let dom = TabHeader::create(numbered_labels(n))
                .with_active_tab(active)
                .dom();
            let children = dom.children.as_ref();
            assert_eq!(children.len(), n + 2, "active={active}: wrong child count");
            for (i, node) in children[1..=n].iter().enumerate() {
                let cls = class_strs(node);
                assert!(
                    !cls.contains(&CLASS_ACTIVE),
                    "active={active}: tab {i} must not be styled active"
                );
                assert!(
                    cls.contains(&CLASS_NOT_ACTIVE),
                    "active={active}: tab {i} must be styled inactive"
                );
            }
        }
    }
    #[test]
    fn dom_at_usize_max_gives_every_tab_the_plain_inactive_style() {
        // `tab_idx.saturating_add(1)` / `saturating_sub(1)` must not wrap into a
        // false neighbour match at the extreme.
        let n = 5usize;
        let dom = TabHeader::create(numbered_labels(n))
            .with_active_tab(usize::MAX)
            .dom();
        for (i, node) in dom.children.as_ref()[1..=n].iter().enumerate() {
            assert_eq!(
                class_strs(node),
                vec![CLASS_NOT_ACTIVE],
                "tab {i} must carry the plain inactive class only"
            );
            assert_eq!(
                inline_declared(node),
                declared(&CSS_MATCH_11510695043643111367),
                "tab {i} must carry the plain inactive style"
            );
        }
    }
    #[test]
    fn dom_one_past_the_end_still_seams_the_last_tab() {
        // PINNED QUIRK: with `active_tab == tabs.len()` no tab is active, yet the
        // *last* tab still matches `active_tab == tab_idx + 1` and loses its right
        // border — a visible seam against the after-tabs spacer. Deliberately
        // pinned: if the widget starts range-checking `active_tab`, this flips.
        let n = 3usize;
        let dom = TabHeader::create(numbered_labels(n)).with_active_tab(n).dom();
        let children = dom.children.as_ref();
        assert_eq!(
            class_strs(&children[n]),
            vec![CLASS_NO_RIGHT, CLASS_NOT_ACTIVE],
            "the last tab is styled as if the (non-existent) next tab were active"
        );
        for (i, node) in children[1..n].iter().enumerate() {
            assert_eq!(class_strs(node), vec![CLASS_NOT_ACTIVE], "tab {i}");
        }
    }
    // ==================================================================
    // TabHeader::swap_with_default
    // ==================================================================
    #[test]
    fn swap_with_default_moves_the_state_out_and_leaves_a_default() {
        let mut header = TabHeader::create(strings(&["a", "b", "c"])).with_active_tab(2);
        header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
        let taken = header.swap_with_default();
        assert_eq!(taken.tabs.as_ref(), strings(&["a", "b", "c"]).as_ref());
        assert_eq!(taken.active_tab, 2);
        assert!(taken.on_click.is_some(), "the callback moves out with the state");
        assert!(header.tabs.as_ref().is_empty(), "the husk must have no tabs");
        assert_eq!(header.active_tab, 0);
        assert!(
            header.on_click.is_none(),
            "the husk must not keep a live callback"
        );
    }
    #[test]
    fn swap_with_default_twice_yields_a_default_the_second_time() {
        let mut header = TabHeader::create(strings(&["a"])).with_active_tab(usize::MAX);
        let first = header.swap_with_default();
        let second = header.swap_with_default();
        assert_eq!(first.active_tab, usize::MAX);
        assert_eq!(first.tabs.as_ref().len(), 1);
        assert_eq!(second.active_tab, 0);
        assert!(second.tabs.as_ref().is_empty());
    }
    #[test]
    fn swap_with_default_releases_the_callback_payload_when_dropped() {
        let user = RefAny::new(ClickLog::default());
        let mut header = TabHeader::create(strings(&["a"]));
        header.set_on_click(user.clone(), cb(record_click));
        assert_eq!(user.get_ref_count(), 2);
        drop(header.swap_with_default());
        assert_eq!(
            user.get_ref_count(),
            1,
            "the swapped-out header must drop its payload clone"
        );
    }
    // ==================================================================
    // TabHeader::set_on_click / with_on_click
    // ==================================================================
    #[test]
    fn with_on_click_installs_the_callback_and_touches_nothing_else() {
        let before = TabHeader::create(strings(&["a", "b"])).with_active_tab(1);
        let mut after = TabHeader::create(strings(&["a", "b"]))
            .with_active_tab(1)
            .with_on_click(RefAny::new(ClickLog::default()), cb(record_click));
        assert_eq!(after.tabs.as_ref(), before.tabs.as_ref());
        assert_eq!(after.active_tab, before.active_tab);
        let installed = after
            .on_click
            .as_mut()
            .expect("with_on_click must install Some(..)");
        assert_eq!(installed.callback.cb as usize, record_click as usize);
        assert!(
            installed.refany.downcast_ref::<ClickLog>().is_some(),
            "the payload must be stored as handed in"
        );
    }
    #[test]
    fn set_on_click_overwrites_the_previous_callback_and_payload() {
        let mut header = TabHeader::create(strings(&["a"]));
        header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
        header.set_on_click(RefAny::new(42u32), cb(click_do_nothing));
        let installed = header.on_click.as_mut().expect("still Some after overwrite");
        assert_eq!(
            installed.callback.cb as usize,
            click_do_nothing as usize,
            "the last set_on_click must win"
        );
        assert_eq!(
            installed.refany.downcast_ref::<u32>().map(|v| *v),
            Some(42),
            "the payload must be replaced together with the fn pointer"
        );
        assert!(
            installed.refany.downcast_ref::<ClickLog>().is_none(),
            "the stale payload must be gone"
        );
    }
    #[test]
    fn set_on_click_drops_the_previous_payload() {
        let first = RefAny::new(ClickLog::default());
        let second = RefAny::new(ClickLog::default());
        let mut header = TabHeader::create(strings(&["a"]));
        header.set_on_click(first.clone(), cb(record_click));
        assert_eq!(first.get_ref_count(), 2);
        header.set_on_click(second.clone(), cb(record_click));
        assert_eq!(
            first.get_ref_count(),
            1,
            "overwriting the callback must release the old payload"
        );
        assert_eq!(second.get_ref_count(), 2);
    }
    #[test]
    fn generic_callback_conversion_round_trips_the_fn_pointer() {
        // The FFI path (`From<Callback>`) transmutes the fn pointer; a corrupted
        // value would be an unconditional jump into garbage at click time.
        // (Never invoked here.)
        let raw = record_click as usize;
        let generic = Callback {
            cb: unsafe { core::mem::transmute::<usize, crate::callbacks::CallbackType>(raw) },
            ctx: OptionRefAny::None,
        };
        let converted: TabOnClickCallback = generic.into();
        assert_eq!(converted.cb as usize, raw);
    }
    // ==================================================================
    // TabHeader::dom — structure
    // ==================================================================
    #[test]
    fn dom_is_a_header_div_wrapping_spacer_tabs_spacer() {
        for n in [0usize, 1, 2, 3, 17] {
            let dom = TabHeader::create(numbered_labels(n)).dom();
            assert_eq!(class_strs(&dom), vec![CLASS_HEADER]);
            assert_eq!(inline_declared(&dom), declared(&CSS_MATCH_9988039989460234263));
            assert!(
                matches!(dom.root.get_node_type(), NodeType::Div),
                "the header itself is a plain div"
            );
            let children = dom.children.as_ref();
            assert_eq!(children.len(), n + 2, "n={n}: spacer + {n} tabs + spacer");
            assert_eq!(class_strs(&children[0]), vec![CLASS_BEFORE]);
            assert_eq!(
                inline_declared(&children[0]),
                declared(&CSS_MATCH_17290739305197504468)
            );
            assert_eq!(class_strs(&children[n + 1]), vec![CLASS_AFTER]);
            assert_eq!(
                inline_declared(&children[n + 1]),
                declared(&CSS_MATCH_3088386549906605418)
            );
            for (i, node) in children[1..=n].iter().enumerate() {
                assert_eq!(
                    text_of(node),
                    Some(format!("tab {i}").as_str()),
                    "n={n}: tab {i} sits at sibling position {}",
                    i + 1
                );
            }
        }
    }
    #[test]
    fn dom_marks_exactly_one_tab_active_for_every_in_range_index() {
        let n = 6usize;
        for active in 0..n {
            let dom = TabHeader::create(numbered_labels(n))
                .with_active_tab(active)
                .dom();
            let children = dom.children.as_ref();
            let active_nodes: Vec<usize> = children[1..=n]
                .iter()
                .enumerate()
                .filter(|(_, node)| class_strs(node).contains(&CLASS_ACTIVE))
                .map(|(i, _)| i)
                .collect();
            assert_eq!(
                active_nodes,
                vec![active],
                "exactly the selected tab must carry the active class"
            );
            for (i, node) in children[1..=n].iter().enumerate() {
                let cls = class_strs(node);
                assert_eq!(
                    cls.contains(&CLASS_ACTIVE),
                    !cls.contains(&CLASS_NOT_ACTIVE),
                    "active={active}, tab {i}: active/not-active must be exclusive"
                );
                assert!(
                    !(cls.contains(&CLASS_NO_LEFT) && cls.contains(&CLASS_NO_RIGHT)),
                    "active={active}, tab {i}: a tab cannot drop both side borders"
                );
                assert!(
                    !(cls.contains(&CLASS_ACTIVE)
                        && (cls.contains(&CLASS_NO_LEFT) || cls.contains(&CLASS_NO_RIGHT))),
                    "active={active}, tab {i}: the active tab keeps both side borders"
                );
            }
        }
    }
    #[test]
    fn dom_gives_the_neighbours_of_the_active_tab_their_seam_classes() {
        // active = 2 of 5: tab 1 loses its right border, tab 3 its left one.
        let dom = TabHeader::create(numbered_labels(5)).with_active_tab(2).dom();
        let children = dom.children.as_ref();
        assert_eq!(class_strs(&children[1]), vec![CLASS_NOT_ACTIVE], "tab 0");
        assert_eq!(
            class_strs(&children[2]),
            vec![CLASS_NO_RIGHT, CLASS_NOT_ACTIVE],
            "tab 1 sits before the active tab"
        );
        assert_eq!(class_strs(&children[3]), vec![CLASS_ACTIVE], "tab 2");
        assert_eq!(
            class_strs(&children[4]),
            vec![CLASS_NO_LEFT, CLASS_NOT_ACTIVE],
            "tab 3 sits after the active tab"
        );
        assert_eq!(class_strs(&children[5]), vec![CLASS_NOT_ACTIVE], "tab 4");
    }
    #[test]
    fn dom_first_tab_active_leaves_the_second_tab_with_a_left_border() {
        // PINNED BUG: `previous_tab_was_active` short-circuits on
        // `self.active_tab == 0`, so for the *default* selection (tab 0) the tab
        // right after the active one never gets `-noleftborder` — it draws a left
        // border straight against the active tab's right edge. Every other
        // selection (see the test above) does emit the class. Pinned as-is so the
        // fix flips this test loudly.
        let dom = TabHeader::create(numbered_labels(4)).with_active_tab(0).dom();
        let children = dom.children.as_ref();
        assert_eq!(class_strs(&children[1]), vec![CLASS_ACTIVE], "tab 0");
        assert_eq!(
            class_strs(&children[2]),
            vec![CLASS_NOT_ACTIVE],
            "tab 1 should be [-noleftborder, -not-active] like the active=1..n case"
        );
        assert_eq!(class_strs(&children[3]), vec![CLASS_NOT_ACTIVE], "tab 2");
        // The symmetric neighbour (active = 1) *does* get the class, which is what
        // makes the case above an inconsistency rather than a design choice.
        let dom = TabHeader::create(numbered_labels(4)).with_active_tab(1).dom();
        assert_eq!(
            class_strs(&dom.children.as_ref()[3]),
            vec![CLASS_NO_LEFT, CLASS_NOT_ACTIVE],
            "tab 2 after active tab 1"
        );
    }
    #[test]
    fn dom_pairs_every_tab_style_with_the_classes_it_advertises() {
        // A swapped class/style pair (e.g. the "noleftborder" node getting the
        // style that nulls the *right* border) would be invisible in a class-only
        // check, so compare the rendered inline style against the vec the class
        // combination demands.
        let n = 6usize;
        for active in [0usize, 1, 2, n - 1, n, usize::MAX] {
            let dom = TabHeader::create(numbered_labels(n))
                .with_active_tab(active)
                .dom();
            for (i, node) in dom.children.as_ref()[1..=n].iter().enumerate() {
                let cls = class_strs(node);
                assert_eq!(
                    inline_declared(node),
                    declared(&style_for_classes(&cls)),
                    "active={active}, tab {i}: style does not match classes {cls:?}"
                );
            }
        }
    }
    #[test]
    fn neighbour_styles_null_the_border_on_the_side_their_class_names_claim() {
        let no_left = plain_decls_where(&CSS_MATCH_13824480602841492081, |p| {
            matches!(p, CssProperty::BorderLeftWidth(_))
        });
        let no_right = plain_decls_where(&CSS_MATCH_4415083954137121609, |p| {
            matches!(p, CssProperty::BorderRightWidth(_))
        });
        assert_eq!(
            no_left.first(),
            Some(&CssProperty::BorderLeftWidth(
                LayoutBorderLeftWidthValue::None
            )),
            "the -noleftborder style must null the LEFT border first"
        );
        assert_eq!(
            no_right.first(),
            Some(&CssProperty::BorderRightWidth(
                LayoutBorderRightWidthValue::None
            )),
            "the -norightborder style must null the RIGHT border first"
        );
        // ...and must not null the opposite side.
        assert!(
            !decls_where(&CSS_MATCH_13824480602841492081, |p| matches!(
                p,
                CssProperty::BorderRightWidth(_)
            ))
            .contains(&CssProperty::BorderRightWidth(
                LayoutBorderRightWidthValue::None
            )),
            "the -noleftborder style must keep the right border"
        );
        assert!(
            !decls_where(&CSS_MATCH_4415083954137121609, |p| matches!(
                p,
                CssProperty::BorderLeftWidth(_)
            ))
            .contains(&CssProperty::BorderLeftWidth(
                LayoutBorderLeftWidthValue::None
            )),
            "the -norightborder style must keep the left border"
        );
        // The plain inactive style (no active neighbour) keeps both side borders:
        // exactly one unconditional declaration per side, and neither is `None`.
        let plain_left = plain_decls_where(&CSS_MATCH_11510695043643111367, |p| {
            matches!(p, CssProperty::BorderLeftWidth(_))
        });
        let plain_right = plain_decls_where(&CSS_MATCH_11510695043643111367, |p| {
            matches!(p, CssProperty::BorderRightWidth(_))
        });
        assert_eq!(plain_left.len(), 1, "one unconditional left-border width");
        assert_eq!(plain_right.len(), 1, "one unconditional right-border width");
        assert_ne!(
            plain_left[0],
            CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::None),
            "a tab with no active neighbour keeps its left border"
        );
        assert_ne!(
            plain_right[0],
            CssProperty::BorderRightWidth(LayoutBorderRightWidthValue::None),
            "a tab with no active neighbour keeps its right border"
        );
    }
    #[test]
    fn tab_styles_redeclare_properties_and_therefore_depend_on_declaration_order() {
        // PINNED HAZARD: each vec is emitted most-specific-block-first, so the
        // generic `.__azul-native-tabs-header p` block *repeats* properties the
        // state-specific block already set, with different values. Which one wins
        // is decided by the resolver, and the two in azul disagree:
        // `PropertyCache::get_property` takes the FIRST match (specific wins,
        // intended), `get_property_with_context` takes the LAST ("last wins",
        // which silently repaints the active tab like an inactive one).
        let heights = decls_where(&CSS_MATCH_14575853790110873394, |p| {
            matches!(p, CssProperty::Height(_))
        });
        assert_eq!(heights.len(), 2, "the active tab declares height twice");
        assert_eq!(
            heights[0],
            CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
                PixelValue::const_px(23)
            ))),
            "the active-tab block (23px) must come first"
        );
        assert_eq!(
            heights[1],
            CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
                PixelValue::const_px(21)
            ))),
            "the generic p block (21px) shadows it under last-wins"
        );
        let backgrounds = decls_where(&CSS_MATCH_14575853790110873394, |p| {
            matches!(p, CssProperty::BackgroundContent(_))
        });
        assert_eq!(backgrounds.len(), 2);
        let kind = |p: &CssProperty| -> &'static str {
            let CssProperty::BackgroundContent(v) = p else {
                unreachable!()
            };
            match v
                .get_property()
                .expect("an exact background")
                .as_ref()
                .first()
                .expect("one layer")
            {
                StyleBackgroundContent::Color(_) => "flat",
                StyleBackgroundContent::LinearGradient(_) => "gradient",
                _ => "other",
            }
        };
        assert_eq!(kind(&backgrounds[0]), "flat", "active = white fill");
        assert_eq!(
            kind(&backgrounds[1]),
            "gradient",
            "the generic p block re-declares the inactive gradient"
        );
        // Same shape on the seam styles: the None triple is re-declared as 1px.
        let left = plain_decls_where(&CSS_MATCH_13824480602841492081, |p| {
            matches!(p, CssProperty::BorderLeftWidth(_))
        });
        assert_eq!(left.len(), 2, "the -noleftborder style declares it twice");
        assert_eq!(left[0], CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::None));
        assert_eq!(
            left[1],
            CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::Exact(
                LayoutBorderLeftWidth {
                    inner: PixelValue::const_px(1)
                }
            ))
        );
    }
    #[test]
    fn hover_declarations_stay_conditional_and_the_rest_stay_unconditional() {
        // A hover rule leaking into the unconditional set would permanently paint
        // every inactive tab in the hover colour.
        for style in [
            CSS_MATCH_11510695043643111367,
            CSS_MATCH_13824480602841492081,
            CSS_MATCH_4415083954137121609,
        ] {
            let conditional = style
                .as_ref()
                .iter()
                .filter(|p| !p.apply_if.as_ref().is_empty())
                .count();
            assert_eq!(
                conditional, 13,
                "each inactive-tab style carries exactly the 13 :hover declarations"
            );
        }
        for style in [
            CSS_MATCH_14575853790110873394,
            CSS_MATCH_9988039989460234263,
            CSS_MATCH_17290739305197504468,
            CSS_MATCH_3088386549906605418,
            CSS_MATCH_18014909903571752977,
            CSS_MATCH_18014909903571752977_NO_PADDING,
            CSS_MATCH_4738503469417034630,
            CSS_MATCH_4738503469417034630_NO_PADDING,
        ] {
            assert!(
                style.as_ref().iter().all(|p| p.apply_if.as_ref().is_empty()),
                "this style must apply unconditionally"
            );
        }
    }
    #[test]
    fn dom_datasets_carry_the_position_of_their_tab() {
        for n in [1usize, 2, 5, 1000] {
            let dom = TabHeader::create(numbered_labels(n)).dom();
            let children = dom.children.as_ref();
            for (i, node) in children[1..=n].iter().enumerate() {
                assert_eq!(
                    tab_idx_of(node),
                    i,
                    "n={n}: tab {i} must carry its own index, not the loop seed"
                );
            }
        }
    }
    #[test]
    fn dom_attaches_a_dataset_even_without_a_click_callback() {
        let dom = TabHeader::create(numbered_labels(2)).dom();
        for node in &dom.children.as_ref()[1..=2] {
            assert!(
                node.root.get_dataset().is_some(),
                "the dataset is unconditional (only the callback is not)"
            );
            assert!(
                node.root.get_callbacks().as_ref().is_empty(),
                "no callback must be attached when on_click is None"
            );
        }
    }
    #[test]
    fn dom_attaches_exactly_one_mouseup_callback_per_tab_when_on_click_is_set() {
        let n = 4usize;
        let dom = TabHeader::create(numbered_labels(n))
            .with_on_click(RefAny::new(ClickLog::default()), cb(record_click))
            .dom();
        let children = dom.children.as_ref();
        for node in &children[1..=n] {
            let cbs = node.root.get_callbacks();
            assert_eq!(cbs.as_ref().len(), 1, "exactly one callback per tab");
            let data = &cbs.as_ref()[0];
            assert_eq!(
                data.event,
                EventFilter::Hover(HoverEventFilter::MouseUp),
                "tabs must react on mouse-up, not mouse-down"
            );
            assert_eq!(
                data.callback.cb,
                on_tab_click as usize,
                "the dispatcher must be the widget's own trampoline"
            );
        }
        // The spacers must stay inert — a click on the filler must not select.
        assert!(children[0].root.get_callbacks().as_ref().is_empty());
        assert!(children[n + 1].root.get_callbacks().as_ref().is_empty());
    }
    #[test]
    fn dom_keeps_the_estimated_child_count_consistent() {
        // `estimated_total_children` is what sizes the flat arena; a stale value
        // makes `convert_dom_into_compact_dom` under-allocate and panic.
        for n in [0usize, 1, 2, 50] {
            let dom = TabHeader::create(numbered_labels(n)).dom();
            assert_eq!(
                dom.estimated_total_children,
                dom.recompute_estimated_total_children(),
                "n={n}: cached descendant count desynced"
            );
            assert_eq!(
                dom.node_count(),
                2 * n + 3,
                "header + spacer + {n} tabs (each a <p> wrapping one text node) + spacer"
            );
            let styled = StyledDom::create_from_dom(dom);
            assert_eq!(
                styled.node_hierarchy.as_ref().len(),
                2 * n + 3,
                "n={n}: the flattened arena must match node_count()"
            );
        }
    }
    #[test]
    fn dom_releases_every_dataset_clone_when_the_dom_is_dropped() {
        // Each tab clones the user payload into its own `TabLocalDataset`; if the
        // widget leaked one, the app state would outlive the DOM forever.
        for n in [0usize, 1, 5] {
            let user = RefAny::new(ClickLog::default());
            let dom = TabHeader::create(numbered_labels(n))
                .with_on_click(user.clone(), cb(record_click))
                .dom();
            assert_eq!(
                user.get_ref_count(),
                n + 1,
                "n={n}: one payload clone per tab, plus the caller's handle"
            );
            drop(dom);
            assert_eq!(
                user.get_ref_count(),
                1,
                "n={n}: dropping the DOM must release every payload clone"
            );
        }
    }
    #[test]
    fn dom_is_deterministic() {
        let build = || {
            let dom = TabHeader::create(numbered_labels(5))
                .with_active_tab(2)
                .dom();
            let children = dom.children.as_ref();
            let shape: Vec<(Option<String>, Vec<&'static str>)> = children
                .iter()
                .map(|c| (text_of(c).map(str::to_string), class_strs(c)))
                .collect();
            shape
        };
        assert_eq!(build(), build(), "dom() must be a pure function of its state");
    }
    // ==================================================================
    // TabContent
    // ==================================================================
    fn nested(depth: usize) -> Dom {
        let mut dom = Dom::create_text_do_not_use_without_block_level_wrapper("leaf");
        for _ in 0..depth {
            dom = Dom::create_div().with_child(dom);
        }
        dom
    }
    #[test]
    fn content_new_defaults_to_padding_and_keeps_the_content_verbatim() {
        let content = nested(3);
        let tab_content = TabContent::new(content.clone());
        assert!(tab_content.has_padding, "new() defaults to padded");
        assert_eq!(
            tab_content.content, content,
            "new() must not rewrap or normalise the content"
        );
        assert!(
            TabContent::default().has_padding,
            "Default agrees with new()"
        );
    }
    #[test]
    fn with_padding_and_set_padding_agree_and_are_last_wins() {
        for flag in [true, false] {
            let built = TabContent::new(Dom::create_div()).with_padding(flag);
            let mut mutated = TabContent::new(Dom::create_div());
            mutated.set_padding(flag);
            assert_eq!(built.has_padding, flag);
            assert_eq!(built.has_padding, mutated.has_padding);
        }
        let toggled = TabContent::new(Dom::create_div())
            .with_padding(false)
            .with_padding(true)
            .with_padding(false);
        assert!(!toggled.has_padding, "the last write must win");
    }
    #[test]
    fn content_dom_nests_the_content_under_the_content_class() {
        let content = nested(2);
        let dom = TabContent::new(content.clone()).dom();
        assert!(
            class_strs(&dom).is_empty(),
            "the outer wrapper carries the style, not the class"
        );
        assert_eq!(dom.children.as_ref().len(), 1, "one wrapper child");
        let inner = &dom.children.as_ref()[0];
        assert_eq!(class_strs(inner), vec![CLASS_CONTENT]);
        assert!(
            inline_declared(inner).is_empty(),
            "the classed node carries no inline style of its own"
        );
        assert_eq!(inner.children.as_ref().len(), 1);
        assert_eq!(
            inner.children.as_ref()[0],
            content,
            "the user content must survive the two wrappers untouched"
        );
    }
    #[test]
    fn content_dom_picks_the_style_vec_the_padding_flag_asks_for() {
        let padded = TabContent::new(Dom::create_div()).with_padding(true).dom();
        assert_eq!(
            inline_declared(&padded),
            declared(&CSS_MATCH_18014909903571752977)
        );
        let bare = TabContent::new(Dom::create_div()).with_padding(false).dom();
        assert_eq!(
            inline_declared(&bare),
            declared(&CSS_MATCH_18014909903571752977_NO_PADDING)
        );
        assert_ne!(
            inline_declared(&padded),
            inline_declared(&bare),
            "the two padding modes must not collapse to the same style"
        );
    }
    #[test]
    fn the_unpadded_content_style_declares_no_padding_at_all() {
        let padding_decls = decls_where(&CSS_MATCH_18014909903571752977_NO_PADDING, |p| {
            matches!(
                p,
                CssProperty::PaddingTop(_)
                    | CssProperty::PaddingBottom(_)
                    | CssProperty::PaddingLeft(_)
                    | CssProperty::PaddingRight(_)
            )
        });
        assert!(
            padding_decls.is_empty(),
            "has_padding == false must not leave a padding declaration behind"
        );
        let padded = decls_where(&CSS_MATCH_18014909903571752977, |p| {
            matches!(
                p,
                CssProperty::PaddingTop(_)
                    | CssProperty::PaddingBottom(_)
                    | CssProperty::PaddingLeft(_)
                    | CssProperty::PaddingRight(_)
            )
        });
        assert_eq!(padded.len(), 4, "the padded variant sets all four sides");
    }
    #[test]
    fn content_dom_keeps_the_estimated_child_count_consistent() {
        for depth in [0usize, 1, 3, 64] {
            let content = nested(depth);
            let expected = content.node_count() + 2; // outer wrapper + classed wrapper
            let dom = TabContent::new(content).dom();
            assert_eq!(
                dom.estimated_total_children,
                dom.recompute_estimated_total_children(),
                "depth={depth}: cached descendant count desynced"
            );
            assert_eq!(dom.node_count(), expected, "depth={depth}");
            let styled = StyledDom::create_from_dom(dom);
            assert_eq!(
                styled.node_hierarchy.as_ref().len(),
                expected,
                "depth={depth}: the flattened arena must match node_count()"
            );
        }
    }
    #[test]
    fn content_swap_with_default_returns_the_old_state_and_leaves_an_empty_div() {
        let content = nested(2);
        let mut tab_content = TabContent::new(content.clone()).with_padding(false);
        let taken = tab_content.swap_with_default();
        assert_eq!(taken.content, content);
        assert!(!taken.has_padding, "the flag moves out with the content");
        assert_eq!(
            tab_content.content,
            Dom::create_div(),
            "the husk must be an empty div"
        );
        assert!(
            tab_content.has_padding,
            "the husk is a Default, i.e. padded again"
        );
    }
    // ==================================================================
    // on_tab_click
    // ==================================================================
    #[test]
    fn on_tab_click_reports_the_clicked_index_to_the_user_callback() {
        for idx in [0usize, 1, 7, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
            let mut user = RefAny::new(ClickLog::default());
            let (update, changes) =
                run_click(dataset(idx, click_handler(user.clone(), record_click)));
            assert_eq!(update, Update::RefreshDom);
            assert!(
                changes.is_empty(),
                "the tab handler is stateless — it must not push CallbackChanges"
            );
            assert_eq!(
                logged(&mut user),
                vec![TabHeaderState { active_tab: idx }],
                "the index must be forwarded verbatim, without arithmetic"
            );
        }
    }
    #[test]
    fn on_tab_click_propagates_the_user_update_verbatim() {
        for (callback, expected) in [
            (record_click as TabOnClickCallbackType, Update::RefreshDom),
            (click_do_nothing, Update::DoNothing),
            (click_refresh_all, Update::RefreshDomAllWindows),
        ] {
            let (update, _) = run_click(dataset(
                3,
                click_handler(RefAny::new(ClickLog::default()), callback),
            ));
            assert_eq!(update, expected, "the user verdict must not be overridden");
        }
    }
    #[test]
    fn on_tab_click_without_a_user_callback_does_nothing() {
        // A dataset with no `on_click` is reachable only by hand (dom() attaches
        // the trampoline only when a callback exists) — it must stay silent
        // rather than force a relayout.
        let (update, changes) = run_click(dataset(2, None.into()));
        assert_eq!(update, Update::DoNothing);
        assert!(changes.is_empty());
    }
    #[test]
    fn on_tab_click_on_a_foreign_payload_falls_back_to_refresh() {
        // Wrong-typed payload => `downcast_mut` returns None => the documented
        // `unwrap_or(RefreshDom)` fallback. Note this is the *opposite* verdict
        // from the no-callback case above, which is the asymmetry to watch.
        for foreign in [RefAny::new(42u32), RefAny::new(String::from("nope"))] {
            let (update, changes) = run_click(foreign);
            assert_eq!(update, Update::RefreshDom);
            assert!(changes.is_empty());
        }
    }
    #[test]
    fn on_tab_click_declines_while_the_dataset_is_already_borrowed() {
        // Borrow tracking is shared across clones, so a live `Ref` from anywhere
        // must make the handler bail out safely instead of aliasing or panicking.
        let mut held = dataset(1, None.into());
        let clone = held.clone();
        let guard = held
            .downcast_ref::<TabLocalDataset>()
            .expect("the fixture is a TabLocalDataset");
        let (update, changes) = run_click(clone);
        assert_eq!(
            update,
            Update::RefreshDom,
            "a contended dataset must fall back, not panic"
        );
        assert!(changes.is_empty());
        drop(guard);
        // ...and the borrow must be released again afterwards.
        let (update, _) = run_click(held.clone());
        assert_eq!(update, Update::DoNothing);
    }
    #[test]
    fn on_tab_click_releases_its_borrow_so_repeated_clicks_keep_working() {
        let mut user = RefAny::new(ClickLog::default());
        let data = dataset(4, click_handler(user.clone(), record_click));
        let (first, _) = run_click(data.clone());
        let (second, _) = run_click(data.clone());
        let (third, _) = run_click(data.clone());
        assert_eq!(
            [first, second, third],
            [Update::RefreshDom; 3],
            "a leaked RefMut would turn later clicks into the RefreshDom fallback \
             without ever reaching the user callback"
        );
        assert_eq!(
            logged(&mut user),
            vec![TabHeaderState { active_tab: 4 }; 3],
            "every click must reach the user callback"
        );
    }
    #[test]
    fn clicking_a_rendered_tab_selects_that_tab_end_to_end() {
        let n = 5usize;
        let mut user = RefAny::new(ClickLog::default());
        let dom = TabHeader::create(numbered_labels(n))
            .with_on_click(user.clone(), cb(record_click))
            .dom();
        for i in 0..n {
            let data = dom.children.as_ref()[i + 1]
                .root
                .get_callbacks()
                .as_ref()
                .first()
                .expect("every tab carries the click callback")
                .refany
                .clone();
            let (update, changes) = run_click(data);
            assert_eq!(update, Update::RefreshDom);
            assert!(changes.is_empty());
        }
        assert_eq!(
            logged(&mut user),
            (0..n)
                .map(|active_tab| TabHeaderState { active_tab })
                .collect::<Vec<_>>(),
            "clicking tab i must report exactly i"
        );
    }
    #[test]
    fn clicking_a_tab_does_not_move_the_active_tab_by_itself() {
        // The widget is stateless: the handler reports the click and nothing
        // else. Re-rendering the *same* header must therefore keep tab 0 active.
        let user = RefAny::new(ClickLog::default());
        let header = TabHeader::create(numbered_labels(3))
            .with_on_click(user.clone(), cb(record_click));
        let dom = header.clone().dom();
        let data = dom.children.as_ref()[3]
            .root
            .get_callbacks()
            .as_ref()
            .first()
            .expect("tab 2 carries the click callback")
            .refany
            .clone();
        let (update, _) = run_click(data);
        assert_eq!(update, Update::RefreshDom);
        assert_eq!(
            header.active_tab, 0,
            "the header itself must be untouched by a click"
        );
        assert_eq!(
            class_strs(&header.dom().children.as_ref()[1]),
            vec![CLASS_ACTIVE],
            "re-rendering the unchanged header keeps tab 0 active"
        );
    }
}