1use 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)] use azul_css::{
14 dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
15 props::{
16 basic::*,
17 layout::*,
18 property::{CssProperty, *},
19 style::*,
20 },
21 *,
22};
23use azul_css::css::BoxOrStatic;
24
25use crate::callbacks::{Callback, CallbackInfo};
26
27const STRING_16146701490593874959: AzString = AzString::from_const_str("system:ui");
28const 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
40const 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 })];
51const STYLE_BACKGROUND_CONTENT_16746671892555275291_ITEMS: &[StyleBackgroundContent] =
52 &[StyleBackgroundContent::Color(ColorU {
53 r: 255,
54 g: 255,
55 b: 255,
56 a: 255,
57 })];
58const STYLE_FONT_FAMILY_8122988506401935406_ITEMS: &[StyleFontFamily] =
59 &[StyleFontFamily::System(STRING_16146701490593874959)];
60const 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];
80const 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
101const CSS_MATCH_13824480602841492081_PROPERTIES: &[CssPropertyWithConditions] = &[
102 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 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 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 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];
320const CSS_MATCH_13824480602841492081: CssPropertyWithConditionsVec =
321 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_13824480602841492081_PROPERTIES);
322
323const CSS_MATCH_14575853790110873394_PROPERTIES: &[CssPropertyWithConditions] = &[
324 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 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];
472const CSS_MATCH_14575853790110873394: CssPropertyWithConditionsVec =
473 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_14575853790110873394_PROPERTIES);
474
475const CSS_MATCH_17290739305197504468_PROPERTIES: &[CssPropertyWithConditions] = &[
476 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];
506const CSS_MATCH_17290739305197504468: CssPropertyWithConditionsVec =
507 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_17290739305197504468_PROPERTIES);
508
509const CSS_MATCH_18014909903571752977_PROPERTIES: &[CssPropertyWithConditions] = &[
510 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];
605const CSS_MATCH_18014909903571752977: CssPropertyWithConditionsVec =
606 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_18014909903571752977_PROPERTIES);
607
608const CSS_MATCH_3088386549906605418_PROPERTIES: &[CssPropertyWithConditions] = &[
609 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];
636const CSS_MATCH_3088386549906605418: CssPropertyWithConditionsVec =
637 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_3088386549906605418_PROPERTIES);
638
639const CSS_MATCH_4415083954137121609_PROPERTIES: &[CssPropertyWithConditions] = &[
640 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 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 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 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];
858const CSS_MATCH_4415083954137121609: CssPropertyWithConditionsVec =
859 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4415083954137121609_PROPERTIES);
860
861const CSS_MATCH_4738503469417034630_PROPERTIES: &[CssPropertyWithConditions] = &[
862 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];
889const CSS_MATCH_4738503469417034630: CssPropertyWithConditionsVec =
890 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_4738503469417034630_PROPERTIES);
891
892const CSS_MATCH_9988039989460234263_PROPERTIES: &[CssPropertyWithConditions] = &[
893 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];
906const CSS_MATCH_9988039989460234263: CssPropertyWithConditionsVec =
907 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_9988039989460234263_PROPERTIES);
908
909const CSS_MATCH_18014909903571752977_PROPERTIES_NO_PADDING: &[CssPropertyWithConditions] = &[
911 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];
923const CSS_MATCH_18014909903571752977_NO_PADDING: CssPropertyWithConditionsVec =
924 CssPropertyWithConditionsVec::from_const_slice(
925 CSS_MATCH_18014909903571752977_PROPERTIES_NO_PADDING,
926 );
927
928const CSS_MATCH_4738503469417034630_PROPERTIES_NO_PADDING: &[CssPropertyWithConditions] = &[
929 CssPropertyWithConditions::simple(CssProperty::FlexGrow(LayoutFlexGrowValue::Exact(
931 LayoutFlexGrow {
932 inner: FloatValue::const_new(1),
933 },
934 ))),
935];
936const CSS_MATCH_4738503469417034630_NO_PADDING: CssPropertyWithConditionsVec =
937 CssPropertyWithConditionsVec::from_const_slice(
938 CSS_MATCH_4738503469417034630_PROPERTIES_NO_PADDING,
939 );
940
941const CSS_MATCH_11510695043643111367_PROPERTIES: &[CssPropertyWithConditions] = &[
944 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,
1000 b: 234,
1001 a: 255,
1002 },
1003 }),
1004 )),
1005 CssPropertyWithConditions::on_hover(CssProperty::BorderRightColor(
1006 StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1007 inner: ColorU {
1008 r: 126,
1009 g: 180,
1010 b: 234,
1011 a: 255,
1012 },
1013 }),
1014 )),
1015 CssPropertyWithConditions::on_hover(CssProperty::BorderTopColor(
1016 StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1017 inner: ColorU {
1018 r: 126,
1019 g: 180,
1020 b: 234,
1021 a: 255,
1022 },
1023 }),
1024 )),
1025 CssPropertyWithConditions::on_hover(CssProperty::BackgroundContent(
1026 StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1027 STYLE_BACKGROUND_CONTENT_15534185073326444643_ITEMS,
1028 )),
1029 )),
1030 CssPropertyWithConditions::simple(CssProperty::PaddingRight(LayoutPaddingRightValue::Exact(
1032 LayoutPaddingRight {
1033 inner: PixelValue::const_px(5),
1034 },
1035 ))),
1036 CssPropertyWithConditions::simple(CssProperty::PaddingLeft(LayoutPaddingLeftValue::Exact(
1037 LayoutPaddingLeft {
1038 inner: PixelValue::const_px(5),
1039 },
1040 ))),
1041 CssPropertyWithConditions::simple(CssProperty::PaddingBottom(LayoutPaddingBottomValue::Exact(
1042 LayoutPaddingBottom {
1043 inner: PixelValue::const_px(1),
1044 },
1045 ))),
1046 CssPropertyWithConditions::simple(CssProperty::PaddingTop(LayoutPaddingTopValue::Exact(
1047 LayoutPaddingTop {
1048 inner: PixelValue::const_px(1),
1049 },
1050 ))),
1051 CssPropertyWithConditions::simple(CssProperty::MarginTop(LayoutMarginTopValue::Exact(
1052 LayoutMarginTop {
1053 inner: PixelValue::const_px(2),
1054 },
1055 ))),
1056 CssPropertyWithConditions::simple(CssProperty::TextAlign(StyleTextAlignValue::Exact(
1058 StyleTextAlign::Center,
1059 ))),
1060 CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
1061 LayoutHeight::Px(PixelValue::const_px(21)),
1062 ))),
1063 CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
1064 LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
1065 inner: PixelValue::const_px(1),
1066 }),
1067 )),
1068 CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
1069 LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
1070 inner: PixelValue::const_px(1),
1071 }),
1072 )),
1073 CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
1074 LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
1075 inner: PixelValue::const_px(1),
1076 }),
1077 )),
1078 CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
1079 LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
1080 inner: PixelValue::const_px(1),
1081 }),
1082 )),
1083 CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
1084 StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
1085 inner: BorderStyle::Solid,
1086 }),
1087 )),
1088 CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
1089 StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
1090 inner: BorderStyle::Solid,
1091 }),
1092 )),
1093 CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
1094 StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
1095 inner: BorderStyle::Solid,
1096 }),
1097 )),
1098 CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
1099 StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
1100 inner: BorderStyle::Solid,
1101 }),
1102 )),
1103 CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
1104 StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
1105 inner: ColorU {
1106 r: 172,
1107 g: 172,
1108 b: 172,
1109 a: 255,
1110 },
1111 }),
1112 )),
1113 CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
1114 StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
1115 inner: ColorU {
1116 r: 172,
1117 g: 172,
1118 b: 172,
1119 a: 255,
1120 },
1121 }),
1122 )),
1123 CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
1124 StyleBorderRightColorValue::Exact(StyleBorderRightColor {
1125 inner: ColorU {
1126 r: 172,
1127 g: 172,
1128 b: 172,
1129 a: 255,
1130 },
1131 }),
1132 )),
1133 CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
1134 StyleBorderTopColorValue::Exact(StyleBorderTopColor {
1135 inner: ColorU {
1136 r: 172,
1137 g: 172,
1138 b: 172,
1139 a: 255,
1140 },
1141 }),
1142 )),
1143 CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
1144 StyleBackgroundContentVecValue::Exact(StyleBackgroundContentVec::from_const_slice(
1145 STYLE_BACKGROUND_CONTENT_8560341490937422656_ITEMS,
1146 )),
1147 )),
1148 CssPropertyWithConditions::simple(CssProperty::AlignItems(LayoutAlignItemsValue::Exact(
1149 LayoutAlignItems::Center,
1150 ))),
1151];
1152const CSS_MATCH_11510695043643111367: CssPropertyWithConditionsVec =
1153 CssPropertyWithConditionsVec::from_const_slice(CSS_MATCH_11510695043643111367_PROPERTIES);
1154
1155#[derive(Debug, Clone)]
1157#[repr(C)]
1158pub struct TabHeader {
1159 pub tabs: StringVec,
1161 pub active_tab: usize,
1163 pub on_click: OptionTabOnClick,
1165}
1166
1167impl Default for TabHeader {
1168 fn default() -> Self {
1169 Self {
1170 tabs: StringVec::from_const_slice(&[]),
1171 active_tab: 0,
1172 on_click: None.into(),
1173 }
1174 }
1175}
1176
1177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1179#[repr(C)]
1180pub struct TabHeaderState {
1181 pub active_tab: usize,
1183}
1184
1185pub type TabOnClickCallbackType = extern "C" fn(RefAny, CallbackInfo, TabHeaderState) -> Update;
1187impl_widget_callback!(
1188 TabOnClick,
1189 OptionTabOnClick,
1190 TabOnClickCallback,
1191 TabOnClickCallbackType
1192);
1193
1194azul_core::impl_managed_callback! {
1195 wrapper: TabOnClickCallback,
1196 info_ty: CallbackInfo,
1197 return_ty: Update,
1198 default_ret: Update::DoNothing,
1199 invoker_static: TAB_ON_CLICK_INVOKER,
1200 invoker_ty: AzTabOnClickCallbackInvoker,
1201 thunk_fn: az_tab_on_click_callback_thunk,
1202 setter_fn: AzApp_setTabOnClickCallbackInvoker,
1203 from_handle_fn: AzTabOnClickCallback_createFromHostHandle,
1204 extra_args: [ state: TabHeaderState ],
1205}
1206
1207impl TabHeader {
1208 #[must_use] pub fn create(tabs: StringVec) -> Self {
1209 Self {
1210 tabs,
1211 active_tab: 0,
1212 on_click: None.into(),
1213 }
1214 }
1215
1216 #[must_use]
1217 pub fn swap_with_default(&mut self) -> Self {
1218 let mut default = Self::default();
1219 core::mem::swap(&mut default, self);
1220 default
1221 }
1222
1223 pub const fn set_active_tab(&mut self, active_tab: usize) {
1224 self.active_tab = active_tab;
1225 }
1226
1227 #[must_use] pub const fn with_active_tab(mut self, active_tab: usize) -> Self {
1228 self.set_active_tab(active_tab);
1229 self
1230 }
1231
1232 pub fn set_on_click<C: Into<TabOnClickCallback>>(&mut self, refany: RefAny, on_click: C) {
1233 self.on_click = Some(TabOnClick {
1234 refany,
1235 callback: on_click.into(),
1236 })
1237 .into();
1238 }
1239
1240 #[must_use]
1241 pub fn with_on_click<C: Into<TabOnClickCallback>>(
1242 mut self,
1243 refany: RefAny,
1244 on_click: C,
1245 ) -> Self {
1246 self.set_on_click(refany, on_click);
1247 self
1248 }
1249
1250 #[allow(clippy::too_many_lines)] #[must_use] pub fn dom(self) -> Dom {
1252 use azul_core::callbacks::CoreCallbackDataVec;
1253
1254 const IDS_AND_CLASSES_5117007530891373979: &[IdOrClass] = &[
1256 Class(AzString::from_const_str(
1257 "__azul-native-tabs-tab-norightborder",
1258 )),
1259 Class(AzString::from_const_str(
1260 "__azul-native-tabs-tab-not-active",
1261 )),
1262 ]; const IDS_AND_CLASSES_15002865554973741556: &[IdOrClass] = &[Class(
1266 AzString::from_const_str("__azul-native-tabs-tab-active"),
1267 )];
1268
1269 const IDS_AND_CLASSES_16877793354714897051: &[IdOrClass] = &[
1271 Class(AzString::from_const_str(
1272 "__azul-native-tabs-tab-noleftborder",
1273 )),
1274 Class(AzString::from_const_str(
1275 "__azul-native-tabs-tab-not-active",
1276 )),
1277 ];
1278
1279 const IDS_AND_CLASSES_INACTIVE: &[IdOrClass] = &[Class(
1281 AzString::from_const_str("__azul-native-tabs-tab-not-active"),
1282 )];
1283
1284 let on_click_is_some = self.on_click.is_some();
1285
1286 Dom::create_div()
1287 .with_css_props(CSS_MATCH_9988039989460234263)
1288 .with_ids_and_classes({
1289 const IDS_AND_CLASSES_6172459441955124689: &[IdOrClass] =
1290 &[Class(AzString::from_const_str("__azul-native-tabs-header"))];
1291 IdOrClassVec::from_const_slice(IDS_AND_CLASSES_6172459441955124689)
1292 })
1293 .with_children({
1294 let mut tab_items = vec![Dom::create_div()
1295 .with_css_props(CSS_MATCH_17290739305197504468)
1296 .with_ids_and_classes({
1297 const IDS_AND_CLASSES_8360971686689797550: &[IdOrClass] = &[Class(
1298 AzString::from_const_str("__azul-native-tabs-before-tabs"),
1299 )];
1300 IdOrClassVec::from_const_slice(IDS_AND_CLASSES_8360971686689797550)
1301 })];
1302
1303 let dataset = TabLocalDataset {
1304 tab_idx: 0,
1305 on_click: self.on_click,
1306 };
1307
1308 for (tab_idx, tab) in self.tabs.as_ref().iter().enumerate() {
1309 let next_tab_is_active = self.active_tab == tab_idx.saturating_add(1);
1310 let previous_tab_was_active = if self.active_tab == 0 {
1311 false
1312 } else {
1313 self.active_tab == tab_idx.saturating_sub(1)
1314 };
1315
1316 let tab_is_active = self.active_tab == tab_idx;
1317
1318 let (ids_and_classes, css_props) = if tab_is_active {
1319 (
1320 IDS_AND_CLASSES_15002865554973741556,
1321 CSS_MATCH_14575853790110873394,
1322 )
1323 } else if next_tab_is_active {
1324 (
1326 IDS_AND_CLASSES_5117007530891373979,
1327 CSS_MATCH_4415083954137121609,
1328 )
1329 } else if previous_tab_was_active {
1330 (
1332 IDS_AND_CLASSES_16877793354714897051,
1333 CSS_MATCH_13824480602841492081,
1334 )
1335 } else {
1336 (IDS_AND_CLASSES_INACTIVE, CSS_MATCH_11510695043643111367)
1338 };
1339
1340 let mut dataset = dataset.clone();
1341 dataset.tab_idx = tab_idx;
1342 let dataset = RefAny::new(dataset);
1343
1344 tab_items.push(
1345 Dom::create_text(tab.clone())
1346 .with_callbacks(if on_click_is_some {
1347 vec![CoreCallbackData {
1348 event: EventFilter::Hover(HoverEventFilter::MouseUp),
1349 callback: CoreCallback {
1350 cb: on_tab_click as usize,
1351 ctx: azul_core::refany::OptionRefAny::None,
1352 },
1353 refany: dataset.clone(),
1354 }]
1355 .into()
1356 } else {
1357 CoreCallbackDataVec::from_const_slice(&[])
1358 })
1359 .with_dataset(Some(dataset).into())
1360 .with_css_props(css_props)
1361 .with_ids_and_classes(IdOrClassVec::from_const_slice(ids_and_classes)),
1362 );
1363 }
1364
1365 tab_items.push(
1366 Dom::create_div()
1367 .with_css_props(CSS_MATCH_3088386549906605418)
1368 .with_ids_and_classes({
1369 const IDS_AND_CLASSES_11001585590816277275: &[IdOrClass] = &[Class(
1370 AzString::from_const_str("__azul-native-tabs-after-tabs"),
1371 )];
1372 IdOrClassVec::from_const_slice(IDS_AND_CLASSES_11001585590816277275)
1373 }),
1374 );
1375
1376 tab_items.into()
1377 })
1378 }
1379}
1380
1381#[derive(Debug, Clone)]
1383#[repr(C)]
1384pub struct TabContent {
1385 pub content: Dom,
1387 pub has_padding: bool,
1389}
1390
1391impl Default for TabContent {
1392 fn default() -> Self {
1393 Self {
1394 content: Dom::create_div(),
1395 has_padding: true,
1396 }
1397 }
1398}
1399
1400impl TabContent {
1401 #[must_use] pub const fn new(content: Dom) -> Self {
1402 Self {
1403 content,
1404 has_padding: true,
1405 }
1406 }
1407
1408 #[must_use]
1409 pub fn swap_with_default(&mut self) -> Self {
1410 let mut default = Self::default();
1411 core::mem::swap(&mut default, self);
1412 default
1413 }
1414
1415 #[must_use] pub const fn with_padding(mut self, padding: bool) -> Self {
1416 self.set_padding(padding);
1417 self
1418 }
1419
1420 pub const fn set_padding(&mut self, padding: bool) {
1421 self.has_padding = padding;
1422 }
1423
1424 #[must_use] pub fn dom(self) -> Dom {
1425 const IDS_AND_CLASSES_2989815829020816222: &[IdOrClass] = &[Class(
1426 AzString::from_const_str("__azul-native-tabs-content"),
1427 )];
1428
1429 let tab_content_css_style = if self.has_padding {
1430 CSS_MATCH_18014909903571752977
1431 } else {
1432 CSS_MATCH_18014909903571752977_NO_PADDING
1433 };
1434
1435 Dom::create_div()
1436 .with_css_props(tab_content_css_style)
1437 .with_children(DomVec::from_vec(vec![Dom::create_div()
1438 .with_ids_and_classes(IdOrClassVec::from_const_slice(
1439 IDS_AND_CLASSES_2989815829020816222,
1440 ))
1441 .with_children(DomVec::from_vec(vec![self.content]))]))
1442 }
1443}
1444
1445#[derive(Clone)]
1446struct TabLocalDataset {
1447 tab_idx: usize,
1448 on_click: OptionTabOnClick,
1449}
1450
1451extern "C" fn on_tab_click(mut refany: RefAny, info: CallbackInfo) -> Update {
1452 fn select_new_tab_inner(mut refany: RefAny, info: &CallbackInfo) -> Option<Update> {
1453 let mut tab_local_dataset = refany.downcast_mut::<TabLocalDataset>()?;
1454 let tab_idx = tab_local_dataset.tab_idx;
1455 let tab_header_state = TabHeaderState {
1456 active_tab: tab_idx,
1457 };
1458
1459 let result = {
1460 let tab_local_dataset = &mut *tab_local_dataset;
1462 let onclick = &mut tab_local_dataset.on_click;
1463
1464 match onclick.as_mut() {
1465 Some(TabOnClick { callback, refany }) => {
1466 (callback.cb)(refany.clone(), *info, tab_header_state)
1467 }
1468 None => Update::DoNothing,
1469 }
1470 };
1471
1472 Some(result)
1473 }
1474
1475 select_new_tab_inner(refany, &info).unwrap_or(Update::RefreshDom)
1476}
1477
1478#[cfg(test)]
1479mod autotest_generated {
1480 use std::{
1481 collections::BTreeMap,
1482 sync::{Arc, Mutex},
1483 };
1484
1485 use azul_core::{
1486 dom::{DomId, DomNodeId, NodeId, NodeType},
1487 geom::OptionLogicalPosition,
1488 gl::OptionGlContextPtr,
1489 hit_test::ScrollPosition,
1490 refany::OptionRefAny,
1491 resources::RendererResources,
1492 styled_dom::{NodeHierarchyItemId, StyledDom},
1493 window::{MonitorVec, RawWindowHandle},
1494 };
1495 use azul_css::system::SystemStyle;
1496 use rust_fontconfig::FcFontCache;
1497
1498 use super::*;
1499 #[cfg(feature = "icu")]
1500 use crate::icu::IcuLocalizerHandle;
1501 use crate::{
1502 callbacks::{CallbackChange, CallbackInfoRefData, ExternalSystemCallbacks},
1503 window::LayoutWindow,
1504 window_state::FullWindowState,
1505 };
1506
1507 const CLASS_ACTIVE: &str = "__azul-native-tabs-tab-active";
1512 const CLASS_NOT_ACTIVE: &str = "__azul-native-tabs-tab-not-active";
1513 const CLASS_NO_LEFT: &str = "__azul-native-tabs-tab-noleftborder";
1514 const CLASS_NO_RIGHT: &str = "__azul-native-tabs-tab-norightborder";
1515 const CLASS_HEADER: &str = "__azul-native-tabs-header";
1516 const CLASS_BEFORE: &str = "__azul-native-tabs-before-tabs";
1517 const CLASS_AFTER: &str = "__azul-native-tabs-after-tabs";
1518 const CLASS_CONTENT: &str = "__azul-native-tabs-content";
1519
1520 fn strings(items: &[&str]) -> StringVec {
1521 StringVec::from_vec(items.iter().map(|s| AzString::from(*s)).collect())
1522 }
1523
1524 fn numbered_labels(n: usize) -> StringVec {
1525 StringVec::from_vec((0..n).map(|i| AzString::from(format!("tab {i}"))).collect())
1526 }
1527
1528 fn text_of(node: &Dom) -> Option<&str> {
1530 match node.root.get_node_type() {
1531 NodeType::Text(s) => Some(s.as_ref().as_str()),
1532 _ => None,
1533 }
1534 }
1535
1536 fn classes(node: &Dom) -> Vec<String> {
1537 node.root
1538 .get_ids_and_classes()
1539 .as_ref()
1540 .iter()
1541 .filter_map(|c| match c {
1542 IdOrClass::Class(s) => Some(s.as_str().to_string()),
1543 IdOrClass::Id(_) => None,
1544 })
1545 .collect()
1546 }
1547
1548 fn class_strs(node: &Dom) -> Vec<&'static str> {
1549 classes(node)
1552 .into_iter()
1553 .map(|c| {
1554 [
1555 CLASS_ACTIVE,
1556 CLASS_NOT_ACTIVE,
1557 CLASS_NO_LEFT,
1558 CLASS_NO_RIGHT,
1559 CLASS_HEADER,
1560 CLASS_BEFORE,
1561 CLASS_AFTER,
1562 CLASS_CONTENT,
1563 ]
1564 .into_iter()
1565 .find(|known| *known == c)
1566 .unwrap_or_else(|| panic!("tabs.rs emitted an unknown class: {c}"))
1567 })
1568 .collect()
1569 }
1570
1571 fn declared(v: &CssPropertyWithConditionsVec) -> Vec<(CssProperty, usize)> {
1574 v.as_ref()
1575 .iter()
1576 .map(|p| (p.property.clone(), p.apply_if.as_ref().len()))
1577 .collect()
1578 }
1579
1580 fn inline_declared(node: &Dom) -> Vec<(CssProperty, usize)> {
1582 node.root
1583 .style
1584 .iter_inline_properties()
1585 .map(|(p, conds)| (p.clone(), conds.as_ref().len()))
1586 .collect()
1587 }
1588
1589 fn decls_where(
1591 v: &CssPropertyWithConditionsVec,
1592 pred: fn(&CssProperty) -> bool,
1593 ) -> Vec<CssProperty> {
1594 v.as_ref()
1595 .iter()
1596 .filter(|p| pred(&p.property))
1597 .map(|p| p.property.clone())
1598 .collect()
1599 }
1600
1601 fn plain_decls_where(
1605 v: &CssPropertyWithConditionsVec,
1606 pred: fn(&CssProperty) -> bool,
1607 ) -> Vec<CssProperty> {
1608 v.as_ref()
1609 .iter()
1610 .filter(|p| p.apply_if.as_ref().is_empty() && pred(&p.property))
1611 .map(|p| p.property.clone())
1612 .collect()
1613 }
1614
1615 fn style_for_classes(cls: &[&str]) -> CssPropertyWithConditionsVec {
1617 let cls = cls.to_vec();
1618 if cls == [CLASS_ACTIVE] {
1619 CSS_MATCH_14575853790110873394
1620 } else if cls == [CLASS_NO_RIGHT, CLASS_NOT_ACTIVE] {
1621 CSS_MATCH_4415083954137121609
1622 } else if cls == [CLASS_NO_LEFT, CLASS_NOT_ACTIVE] {
1623 CSS_MATCH_13824480602841492081
1624 } else if cls == [CLASS_NOT_ACTIVE] {
1625 CSS_MATCH_11510695043643111367
1626 } else {
1627 panic!("unexpected class combination on a tab node: {cls:?}");
1628 }
1629 }
1630
1631 fn dataset_of(node: &Dom) -> RefAny {
1634 node.root
1635 .get_dataset()
1636 .expect("every tab node carries a TabLocalDataset")
1637 .clone()
1638 }
1639
1640 fn tab_idx_of(node: &Dom) -> usize {
1641 let mut ds = dataset_of(node);
1642 let tab_idx = ds
1643 .downcast_ref::<TabLocalDataset>()
1644 .expect("the dataset must be a TabLocalDataset")
1645 .tab_idx;
1646 tab_idx
1647 }
1648
1649 #[derive(Default)]
1651 struct ClickLog {
1652 seen: Vec<TabHeaderState>,
1653 }
1654
1655 extern "C" fn record_click(
1656 mut refany: RefAny,
1657 _: CallbackInfo,
1658 state: TabHeaderState,
1659 ) -> Update {
1660 if let Some(mut log) = refany.downcast_mut::<ClickLog>() {
1661 log.seen.push(state);
1662 }
1663 Update::RefreshDom
1664 }
1665
1666 extern "C" fn click_do_nothing(_: RefAny, _: CallbackInfo, _: TabHeaderState) -> Update {
1667 Update::DoNothing
1668 }
1669
1670 extern "C" fn click_refresh_all(_: RefAny, _: CallbackInfo, _: TabHeaderState) -> Update {
1671 Update::RefreshDomAllWindows
1672 }
1673
1674 fn cb(f: TabOnClickCallbackType) -> TabOnClickCallback {
1676 f.into()
1677 }
1678
1679 fn logged(refany: &mut RefAny) -> Vec<TabHeaderState> {
1680 refany
1681 .downcast_ref::<ClickLog>()
1682 .expect("payload must still be a ClickLog")
1683 .seen
1684 .clone()
1685 }
1686
1687 fn dataset(tab_idx: usize, on_click: OptionTabOnClick) -> RefAny {
1688 RefAny::new(TabLocalDataset { tab_idx, on_click })
1689 }
1690
1691 fn click_handler(refany: RefAny, callback: TabOnClickCallbackType) -> OptionTabOnClick {
1692 Some(TabOnClick {
1693 refany,
1694 callback: cb(callback),
1695 })
1696 .into()
1697 }
1698
1699 fn run_click(data: RefAny) -> (Update, Vec<CallbackChange>) {
1704 let layout_window =
1705 LayoutWindow::new(FcFontCache::default()).expect("LayoutWindow::new failed");
1706
1707 let renderer_resources = RendererResources::default();
1708 let previous_window_state: Option<FullWindowState> = None;
1709 let current_window_state = FullWindowState::default();
1710 let gl_context = OptionGlContextPtr::None;
1711 let scroll_states: BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>> =
1712 BTreeMap::new();
1713 let window_handle = RawWindowHandle::Unsupported;
1714 let system_callbacks = ExternalSystemCallbacks::rust_internal();
1715
1716 let ref_data = CallbackInfoRefData {
1717 layout_window: &layout_window,
1718 renderer_resources: &renderer_resources,
1719 previous_window_state: &previous_window_state,
1720 current_window_state: ¤t_window_state,
1721 gl_context: &gl_context,
1722 current_scroll_manager: &scroll_states,
1723 current_window_handle: &window_handle,
1724 system_callbacks: &system_callbacks,
1725 system_style: Arc::new(SystemStyle::default()),
1726 monitors: Arc::new(Mutex::new(MonitorVec::from_const_slice(&[]))),
1727 #[cfg(feature = "icu")]
1728 icu_localizer: IcuLocalizerHandle::default(),
1729 ctx: OptionRefAny::None,
1730 };
1731
1732 let changes: Arc<Mutex<Vec<CallbackChange>>> = Arc::new(Mutex::new(Vec::new()));
1733
1734 let info = CallbackInfo::new(
1735 &ref_data,
1736 &changes,
1737 DomNodeId {
1738 dom: DomId::ROOT_ID,
1739 node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0))),
1740 },
1741 OptionLogicalPosition::None,
1742 OptionLogicalPosition::None,
1743 );
1744
1745 let update = on_tab_click(data, info);
1746 let recorded = core::mem::take(&mut *changes.lock().expect("change log poisoned"));
1747 (update, recorded)
1748 }
1749
1750 #[test]
1755 fn create_keeps_the_label_vec_verbatim_and_starts_inert() {
1756 for labels in [
1757 strings(&[]),
1758 strings(&["only"]),
1759 strings(&["a", "b", "c"]),
1760 numbered_labels(1000),
1761 ] {
1762 let expected: Vec<AzString> = labels.as_ref().to_vec();
1763 let header = TabHeader::create(labels);
1764
1765 assert_eq!(
1766 header.tabs.as_ref(),
1767 expected.as_slice(),
1768 "create must not reorder, drop or rewrite labels"
1769 );
1770 assert_eq!(header.active_tab, 0, "a fresh header selects the first tab");
1771 assert!(
1772 header.on_click.is_none(),
1773 "create must not install a callback"
1774 );
1775 }
1776 }
1777
1778 #[test]
1779 fn create_accepts_an_empty_label_vec_and_still_renders_the_spacers() {
1780 let dom = TabHeader::create(strings(&[])).dom();
1783 let children = dom.children.as_ref();
1784
1785 assert_eq!(children.len(), 2, "an empty header is just the two spacers");
1786 assert_eq!(class_strs(&children[0]), vec![CLASS_BEFORE]);
1787 assert_eq!(class_strs(&children[1]), vec![CLASS_AFTER]);
1788 }
1789
1790 #[test]
1791 fn create_round_trips_pathological_labels_through_the_dom() {
1792 let long = "\u{e9}".repeat(50_000);
1795 let pathological: Vec<String> = vec![
1796 String::new(),
1797 " ".to_string(),
1798 "\0embedded\0nul\0".to_string(),
1799 "\u{1F600}\u{1F3F4}\u{E0067}\u{E007F}".to_string(), "a\u{0301}\u{0327}\u{0328}".to_string(), "\u{202E}reversed\u{202C}".to_string(), "\r\n\t".to_string(),
1803 "\u{FFFD}\u{FEFF}".to_string(), long.clone(),
1805 ];
1806
1807 let labels = StringVec::from_vec(
1808 pathological
1809 .iter()
1810 .map(|s| AzString::from(s.clone()))
1811 .collect(),
1812 );
1813 let dom = TabHeader::create(labels).dom();
1814 let children = dom.children.as_ref();
1815
1816 assert_eq!(children.len(), pathological.len() + 2);
1817 for (i, expected) in pathological.iter().enumerate() {
1818 assert_eq!(
1819 text_of(&children[i + 1]),
1820 Some(expected.as_str()),
1821 "label {i} did not round-trip through the DOM"
1822 );
1823 }
1824 }
1825
1826 #[test]
1831 fn set_active_tab_stores_any_usize_without_clamping_or_panicking() {
1832 let extremes = [
1836 0usize,
1837 1,
1838 2,
1839 usize::MAX / 2,
1840 usize::MAX - 1,
1841 usize::MAX,
1842 0usize.wrapping_sub(1),
1843 ];
1844
1845 for value in extremes {
1846 let mut header = TabHeader::create(strings(&["a", "b", "c"]));
1847 header.set_active_tab(value);
1848 assert_eq!(
1849 header.active_tab, value,
1850 "set_active_tab must store the index verbatim (no clamp to len)"
1851 );
1852
1853 header.set_active_tab(value);
1855 assert_eq!(header.active_tab, value);
1856 header.set_active_tab(0);
1857 assert_eq!(header.active_tab, 0, "the last write must win");
1858 }
1859 }
1860
1861 #[test]
1862 fn set_active_tab_touches_nothing_but_the_index() {
1863 let mut header = TabHeader::create(strings(&["a", "b"]));
1864 header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
1865
1866 header.set_active_tab(usize::MAX);
1867
1868 assert_eq!(
1869 header.tabs.as_ref(),
1870 strings(&["a", "b"]).as_ref(),
1871 "the labels must be untouched"
1872 );
1873 assert!(
1874 header.on_click.is_some(),
1875 "the callback must survive a selection change"
1876 );
1877 }
1878
1879 #[test]
1880 fn with_active_tab_agrees_with_set_active_tab_at_every_extreme() {
1881 for value in [0usize, 1, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
1882 let built = TabHeader::create(strings(&["a", "b"])).with_active_tab(value);
1883 let mut mutated = TabHeader::create(strings(&["a", "b"]));
1884 mutated.set_active_tab(value);
1885
1886 assert_eq!(built.active_tab, mutated.active_tab);
1887 assert_eq!(built.active_tab, value);
1888 assert_eq!(built.tabs.as_ref(), mutated.tabs.as_ref());
1889 assert!(built.on_click.is_none() && mutated.on_click.is_none());
1890 }
1891 }
1892
1893 #[test]
1894 fn with_active_tab_chains_last_wins() {
1895 let header = TabHeader::create(strings(&["a"]))
1896 .with_active_tab(usize::MAX)
1897 .with_active_tab(7)
1898 .with_active_tab(0);
1899 assert_eq!(header.active_tab, 0);
1900 }
1901
1902 #[test]
1903 fn dom_never_marks_a_tab_active_when_the_index_is_out_of_range() {
1904 let n = 4usize;
1907 for active in [n, n + 1, n + 2, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
1908 let dom = TabHeader::create(numbered_labels(n))
1909 .with_active_tab(active)
1910 .dom();
1911 let children = dom.children.as_ref();
1912 assert_eq!(children.len(), n + 2, "active={active}: wrong child count");
1913
1914 for (i, node) in children[1..=n].iter().enumerate() {
1915 let cls = class_strs(node);
1916 assert!(
1917 !cls.contains(&CLASS_ACTIVE),
1918 "active={active}: tab {i} must not be styled active"
1919 );
1920 assert!(
1921 cls.contains(&CLASS_NOT_ACTIVE),
1922 "active={active}: tab {i} must be styled inactive"
1923 );
1924 }
1925 }
1926 }
1927
1928 #[test]
1929 fn dom_at_usize_max_gives_every_tab_the_plain_inactive_style() {
1930 let n = 5usize;
1933 let dom = TabHeader::create(numbered_labels(n))
1934 .with_active_tab(usize::MAX)
1935 .dom();
1936
1937 for (i, node) in dom.children.as_ref()[1..=n].iter().enumerate() {
1938 assert_eq!(
1939 class_strs(node),
1940 vec![CLASS_NOT_ACTIVE],
1941 "tab {i} must carry the plain inactive class only"
1942 );
1943 assert_eq!(
1944 inline_declared(node),
1945 declared(&CSS_MATCH_11510695043643111367),
1946 "tab {i} must carry the plain inactive style"
1947 );
1948 }
1949 }
1950
1951 #[test]
1952 fn dom_one_past_the_end_still_seams_the_last_tab() {
1953 let n = 3usize;
1958 let dom = TabHeader::create(numbered_labels(n)).with_active_tab(n).dom();
1959 let children = dom.children.as_ref();
1960
1961 assert_eq!(
1962 class_strs(&children[n]),
1963 vec![CLASS_NO_RIGHT, CLASS_NOT_ACTIVE],
1964 "the last tab is styled as if the (non-existent) next tab were active"
1965 );
1966 for (i, node) in children[1..n].iter().enumerate() {
1967 assert_eq!(class_strs(node), vec![CLASS_NOT_ACTIVE], "tab {i}");
1968 }
1969 }
1970
1971 #[test]
1976 fn swap_with_default_moves_the_state_out_and_leaves_a_default() {
1977 let mut header = TabHeader::create(strings(&["a", "b", "c"])).with_active_tab(2);
1978 header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
1979
1980 let taken = header.swap_with_default();
1981
1982 assert_eq!(taken.tabs.as_ref(), strings(&["a", "b", "c"]).as_ref());
1983 assert_eq!(taken.active_tab, 2);
1984 assert!(taken.on_click.is_some(), "the callback moves out with the state");
1985
1986 assert!(header.tabs.as_ref().is_empty(), "the husk must have no tabs");
1987 assert_eq!(header.active_tab, 0);
1988 assert!(
1989 header.on_click.is_none(),
1990 "the husk must not keep a live callback"
1991 );
1992 }
1993
1994 #[test]
1995 fn swap_with_default_twice_yields_a_default_the_second_time() {
1996 let mut header = TabHeader::create(strings(&["a"])).with_active_tab(usize::MAX);
1997 let first = header.swap_with_default();
1998 let second = header.swap_with_default();
1999
2000 assert_eq!(first.active_tab, usize::MAX);
2001 assert_eq!(first.tabs.as_ref().len(), 1);
2002 assert_eq!(second.active_tab, 0);
2003 assert!(second.tabs.as_ref().is_empty());
2004 }
2005
2006 #[test]
2007 fn swap_with_default_releases_the_callback_payload_when_dropped() {
2008 let user = RefAny::new(ClickLog::default());
2009 let mut header = TabHeader::create(strings(&["a"]));
2010 header.set_on_click(user.clone(), cb(record_click));
2011 assert_eq!(user.get_ref_count(), 2);
2012
2013 drop(header.swap_with_default());
2014 assert_eq!(
2015 user.get_ref_count(),
2016 1,
2017 "the swapped-out header must drop its payload clone"
2018 );
2019 }
2020
2021 #[test]
2026 fn with_on_click_installs_the_callback_and_touches_nothing_else() {
2027 let before = TabHeader::create(strings(&["a", "b"])).with_active_tab(1);
2028 let mut after = TabHeader::create(strings(&["a", "b"]))
2029 .with_active_tab(1)
2030 .with_on_click(RefAny::new(ClickLog::default()), cb(record_click));
2031
2032 assert_eq!(after.tabs.as_ref(), before.tabs.as_ref());
2033 assert_eq!(after.active_tab, before.active_tab);
2034
2035 let installed = after
2036 .on_click
2037 .as_mut()
2038 .expect("with_on_click must install Some(..)");
2039 assert_eq!(installed.callback.cb as usize, record_click as usize);
2040 assert!(
2041 installed.refany.downcast_ref::<ClickLog>().is_some(),
2042 "the payload must be stored as handed in"
2043 );
2044 }
2045
2046 #[test]
2047 fn set_on_click_overwrites_the_previous_callback_and_payload() {
2048 let mut header = TabHeader::create(strings(&["a"]));
2049 header.set_on_click(RefAny::new(ClickLog::default()), cb(record_click));
2050 header.set_on_click(RefAny::new(42u32), cb(click_do_nothing));
2051
2052 let installed = header.on_click.as_mut().expect("still Some after overwrite");
2053 assert_eq!(
2054 installed.callback.cb as usize,
2055 click_do_nothing as usize,
2056 "the last set_on_click must win"
2057 );
2058 assert_eq!(
2059 installed.refany.downcast_ref::<u32>().map(|v| *v),
2060 Some(42),
2061 "the payload must be replaced together with the fn pointer"
2062 );
2063 assert!(
2064 installed.refany.downcast_ref::<ClickLog>().is_none(),
2065 "the stale payload must be gone"
2066 );
2067 }
2068
2069 #[test]
2070 fn set_on_click_drops_the_previous_payload() {
2071 let first = RefAny::new(ClickLog::default());
2072 let second = RefAny::new(ClickLog::default());
2073 let mut header = TabHeader::create(strings(&["a"]));
2074
2075 header.set_on_click(first.clone(), cb(record_click));
2076 assert_eq!(first.get_ref_count(), 2);
2077 header.set_on_click(second.clone(), cb(record_click));
2078
2079 assert_eq!(
2080 first.get_ref_count(),
2081 1,
2082 "overwriting the callback must release the old payload"
2083 );
2084 assert_eq!(second.get_ref_count(), 2);
2085 }
2086
2087 #[test]
2088 fn generic_callback_conversion_round_trips_the_fn_pointer() {
2089 let raw = record_click as usize;
2093 let generic = Callback {
2094 cb: unsafe { core::mem::transmute::<usize, crate::callbacks::CallbackType>(raw) },
2095 ctx: azul_core::refany::OptionRefAny::None,
2096 };
2097 let converted: TabOnClickCallback = generic.into();
2098 assert_eq!(converted.cb as usize, raw);
2099 }
2100
2101 #[test]
2106 fn dom_is_a_header_div_wrapping_spacer_tabs_spacer() {
2107 for n in [0usize, 1, 2, 3, 17] {
2108 let dom = TabHeader::create(numbered_labels(n)).dom();
2109
2110 assert_eq!(class_strs(&dom), vec![CLASS_HEADER]);
2111 assert_eq!(inline_declared(&dom), declared(&CSS_MATCH_9988039989460234263));
2112 assert!(
2113 matches!(dom.root.get_node_type(), NodeType::Div),
2114 "the header itself is a plain div"
2115 );
2116
2117 let children = dom.children.as_ref();
2118 assert_eq!(children.len(), n + 2, "n={n}: spacer + {n} tabs + spacer");
2119
2120 assert_eq!(class_strs(&children[0]), vec![CLASS_BEFORE]);
2121 assert_eq!(
2122 inline_declared(&children[0]),
2123 declared(&CSS_MATCH_17290739305197504468)
2124 );
2125 assert_eq!(class_strs(&children[n + 1]), vec![CLASS_AFTER]);
2126 assert_eq!(
2127 inline_declared(&children[n + 1]),
2128 declared(&CSS_MATCH_3088386549906605418)
2129 );
2130
2131 for (i, node) in children[1..=n].iter().enumerate() {
2132 assert_eq!(
2133 text_of(node),
2134 Some(format!("tab {i}").as_str()),
2135 "n={n}: tab {i} sits at sibling position {}",
2136 i + 1
2137 );
2138 }
2139 }
2140 }
2141
2142 #[test]
2143 fn dom_marks_exactly_one_tab_active_for_every_in_range_index() {
2144 let n = 6usize;
2145 for active in 0..n {
2146 let dom = TabHeader::create(numbered_labels(n))
2147 .with_active_tab(active)
2148 .dom();
2149 let children = dom.children.as_ref();
2150
2151 let active_nodes: Vec<usize> = children[1..=n]
2152 .iter()
2153 .enumerate()
2154 .filter(|(_, node)| class_strs(node).contains(&CLASS_ACTIVE))
2155 .map(|(i, _)| i)
2156 .collect();
2157 assert_eq!(
2158 active_nodes,
2159 vec![active],
2160 "exactly the selected tab must carry the active class"
2161 );
2162
2163 for (i, node) in children[1..=n].iter().enumerate() {
2164 let cls = class_strs(node);
2165 assert_eq!(
2166 cls.contains(&CLASS_ACTIVE),
2167 !cls.contains(&CLASS_NOT_ACTIVE),
2168 "active={active}, tab {i}: active/not-active must be exclusive"
2169 );
2170 assert!(
2171 !(cls.contains(&CLASS_NO_LEFT) && cls.contains(&CLASS_NO_RIGHT)),
2172 "active={active}, tab {i}: a tab cannot drop both side borders"
2173 );
2174 assert!(
2175 !(cls.contains(&CLASS_ACTIVE)
2176 && (cls.contains(&CLASS_NO_LEFT) || cls.contains(&CLASS_NO_RIGHT))),
2177 "active={active}, tab {i}: the active tab keeps both side borders"
2178 );
2179 }
2180 }
2181 }
2182
2183 #[test]
2184 fn dom_gives_the_neighbours_of_the_active_tab_their_seam_classes() {
2185 let dom = TabHeader::create(numbered_labels(5)).with_active_tab(2).dom();
2187 let children = dom.children.as_ref();
2188
2189 assert_eq!(class_strs(&children[1]), vec![CLASS_NOT_ACTIVE], "tab 0");
2190 assert_eq!(
2191 class_strs(&children[2]),
2192 vec![CLASS_NO_RIGHT, CLASS_NOT_ACTIVE],
2193 "tab 1 sits before the active tab"
2194 );
2195 assert_eq!(class_strs(&children[3]), vec![CLASS_ACTIVE], "tab 2");
2196 assert_eq!(
2197 class_strs(&children[4]),
2198 vec![CLASS_NO_LEFT, CLASS_NOT_ACTIVE],
2199 "tab 3 sits after the active tab"
2200 );
2201 assert_eq!(class_strs(&children[5]), vec![CLASS_NOT_ACTIVE], "tab 4");
2202 }
2203
2204 #[test]
2205 fn dom_first_tab_active_leaves_the_second_tab_with_a_left_border() {
2206 let dom = TabHeader::create(numbered_labels(4)).with_active_tab(0).dom();
2213 let children = dom.children.as_ref();
2214
2215 assert_eq!(class_strs(&children[1]), vec![CLASS_ACTIVE], "tab 0");
2216 assert_eq!(
2217 class_strs(&children[2]),
2218 vec![CLASS_NOT_ACTIVE],
2219 "tab 1 should be [-noleftborder, -not-active] like the active=1..n case"
2220 );
2221 assert_eq!(class_strs(&children[3]), vec![CLASS_NOT_ACTIVE], "tab 2");
2222
2223 let dom = TabHeader::create(numbered_labels(4)).with_active_tab(1).dom();
2226 assert_eq!(
2227 class_strs(&dom.children.as_ref()[3]),
2228 vec![CLASS_NO_LEFT, CLASS_NOT_ACTIVE],
2229 "tab 2 after active tab 1"
2230 );
2231 }
2232
2233 #[test]
2234 fn dom_pairs_every_tab_style_with_the_classes_it_advertises() {
2235 let n = 6usize;
2240 for active in [0usize, 1, 2, n - 1, n, usize::MAX] {
2241 let dom = TabHeader::create(numbered_labels(n))
2242 .with_active_tab(active)
2243 .dom();
2244 for (i, node) in dom.children.as_ref()[1..=n].iter().enumerate() {
2245 let cls = class_strs(node);
2246 assert_eq!(
2247 inline_declared(node),
2248 declared(&style_for_classes(&cls)),
2249 "active={active}, tab {i}: style does not match classes {cls:?}"
2250 );
2251 }
2252 }
2253 }
2254
2255 #[test]
2256 fn neighbour_styles_null_the_border_on_the_side_their_class_names_claim() {
2257 let no_left = plain_decls_where(&CSS_MATCH_13824480602841492081, |p| {
2258 matches!(p, CssProperty::BorderLeftWidth(_))
2259 });
2260 let no_right = plain_decls_where(&CSS_MATCH_4415083954137121609, |p| {
2261 matches!(p, CssProperty::BorderRightWidth(_))
2262 });
2263
2264 assert_eq!(
2265 no_left.first(),
2266 Some(&CssProperty::BorderLeftWidth(
2267 LayoutBorderLeftWidthValue::None
2268 )),
2269 "the -noleftborder style must null the LEFT border first"
2270 );
2271 assert_eq!(
2272 no_right.first(),
2273 Some(&CssProperty::BorderRightWidth(
2274 LayoutBorderRightWidthValue::None
2275 )),
2276 "the -norightborder style must null the RIGHT border first"
2277 );
2278
2279 assert!(
2281 !decls_where(&CSS_MATCH_13824480602841492081, |p| matches!(
2282 p,
2283 CssProperty::BorderRightWidth(_)
2284 ))
2285 .contains(&CssProperty::BorderRightWidth(
2286 LayoutBorderRightWidthValue::None
2287 )),
2288 "the -noleftborder style must keep the right border"
2289 );
2290 assert!(
2291 !decls_where(&CSS_MATCH_4415083954137121609, |p| matches!(
2292 p,
2293 CssProperty::BorderLeftWidth(_)
2294 ))
2295 .contains(&CssProperty::BorderLeftWidth(
2296 LayoutBorderLeftWidthValue::None
2297 )),
2298 "the -norightborder style must keep the left border"
2299 );
2300
2301 let plain_left = plain_decls_where(&CSS_MATCH_11510695043643111367, |p| {
2304 matches!(p, CssProperty::BorderLeftWidth(_))
2305 });
2306 let plain_right = plain_decls_where(&CSS_MATCH_11510695043643111367, |p| {
2307 matches!(p, CssProperty::BorderRightWidth(_))
2308 });
2309 assert_eq!(plain_left.len(), 1, "one unconditional left-border width");
2310 assert_eq!(plain_right.len(), 1, "one unconditional right-border width");
2311 assert_ne!(
2312 plain_left[0],
2313 CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::None),
2314 "a tab with no active neighbour keeps its left border"
2315 );
2316 assert_ne!(
2317 plain_right[0],
2318 CssProperty::BorderRightWidth(LayoutBorderRightWidthValue::None),
2319 "a tab with no active neighbour keeps its right border"
2320 );
2321 }
2322
2323 #[test]
2324 fn tab_styles_redeclare_properties_and_therefore_depend_on_declaration_order() {
2325 let heights = decls_where(&CSS_MATCH_14575853790110873394, |p| {
2333 matches!(p, CssProperty::Height(_))
2334 });
2335 assert_eq!(heights.len(), 2, "the active tab declares height twice");
2336 assert_eq!(
2337 heights[0],
2338 CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
2339 PixelValue::const_px(23)
2340 ))),
2341 "the active-tab block (23px) must come first"
2342 );
2343 assert_eq!(
2344 heights[1],
2345 CssProperty::Height(LayoutHeightValue::Exact(LayoutHeight::Px(
2346 PixelValue::const_px(21)
2347 ))),
2348 "the generic p block (21px) shadows it under last-wins"
2349 );
2350
2351 let backgrounds = decls_where(&CSS_MATCH_14575853790110873394, |p| {
2352 matches!(p, CssProperty::BackgroundContent(_))
2353 });
2354 assert_eq!(backgrounds.len(), 2);
2355 let kind = |p: &CssProperty| -> &'static str {
2356 let CssProperty::BackgroundContent(v) = p else {
2357 unreachable!()
2358 };
2359 match v
2360 .get_property()
2361 .expect("an exact background")
2362 .as_ref()
2363 .first()
2364 .expect("one layer")
2365 {
2366 StyleBackgroundContent::Color(_) => "flat",
2367 StyleBackgroundContent::LinearGradient(_) => "gradient",
2368 _ => "other",
2369 }
2370 };
2371 assert_eq!(kind(&backgrounds[0]), "flat", "active = white fill");
2372 assert_eq!(
2373 kind(&backgrounds[1]),
2374 "gradient",
2375 "the generic p block re-declares the inactive gradient"
2376 );
2377
2378 let left = plain_decls_where(&CSS_MATCH_13824480602841492081, |p| {
2380 matches!(p, CssProperty::BorderLeftWidth(_))
2381 });
2382 assert_eq!(left.len(), 2, "the -noleftborder style declares it twice");
2383 assert_eq!(left[0], CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::None));
2384 assert_eq!(
2385 left[1],
2386 CssProperty::BorderLeftWidth(LayoutBorderLeftWidthValue::Exact(
2387 LayoutBorderLeftWidth {
2388 inner: PixelValue::const_px(1)
2389 }
2390 ))
2391 );
2392 }
2393
2394 #[test]
2395 fn hover_declarations_stay_conditional_and_the_rest_stay_unconditional() {
2396 for style in [
2399 CSS_MATCH_11510695043643111367,
2400 CSS_MATCH_13824480602841492081,
2401 CSS_MATCH_4415083954137121609,
2402 ] {
2403 let conditional = style
2404 .as_ref()
2405 .iter()
2406 .filter(|p| !p.apply_if.as_ref().is_empty())
2407 .count();
2408 assert_eq!(
2409 conditional, 13,
2410 "each inactive-tab style carries exactly the 13 :hover declarations"
2411 );
2412 }
2413
2414 for style in [
2415 CSS_MATCH_14575853790110873394,
2416 CSS_MATCH_9988039989460234263,
2417 CSS_MATCH_17290739305197504468,
2418 CSS_MATCH_3088386549906605418,
2419 CSS_MATCH_18014909903571752977,
2420 CSS_MATCH_18014909903571752977_NO_PADDING,
2421 CSS_MATCH_4738503469417034630,
2422 CSS_MATCH_4738503469417034630_NO_PADDING,
2423 ] {
2424 assert!(
2425 style.as_ref().iter().all(|p| p.apply_if.as_ref().is_empty()),
2426 "this style must apply unconditionally"
2427 );
2428 }
2429 }
2430
2431 #[test]
2432 fn dom_datasets_carry_the_position_of_their_tab() {
2433 for n in [1usize, 2, 5, 1000] {
2434 let dom = TabHeader::create(numbered_labels(n)).dom();
2435 let children = dom.children.as_ref();
2436 for (i, node) in children[1..=n].iter().enumerate() {
2437 assert_eq!(
2438 tab_idx_of(node),
2439 i,
2440 "n={n}: tab {i} must carry its own index, not the loop seed"
2441 );
2442 }
2443 }
2444 }
2445
2446 #[test]
2447 fn dom_attaches_a_dataset_even_without_a_click_callback() {
2448 let dom = TabHeader::create(numbered_labels(2)).dom();
2449 for node in &dom.children.as_ref()[1..=2] {
2450 assert!(
2451 node.root.get_dataset().is_some(),
2452 "the dataset is unconditional (only the callback is not)"
2453 );
2454 assert!(
2455 node.root.get_callbacks().as_ref().is_empty(),
2456 "no callback must be attached when on_click is None"
2457 );
2458 }
2459 }
2460
2461 #[test]
2462 fn dom_attaches_exactly_one_mouseup_callback_per_tab_when_on_click_is_set() {
2463 let n = 4usize;
2464 let dom = TabHeader::create(numbered_labels(n))
2465 .with_on_click(RefAny::new(ClickLog::default()), cb(record_click))
2466 .dom();
2467 let children = dom.children.as_ref();
2468
2469 for node in &children[1..=n] {
2470 let cbs = node.root.get_callbacks();
2471 assert_eq!(cbs.as_ref().len(), 1, "exactly one callback per tab");
2472 let data = &cbs.as_ref()[0];
2473 assert_eq!(
2474 data.event,
2475 EventFilter::Hover(HoverEventFilter::MouseUp),
2476 "tabs must react on mouse-up, not mouse-down"
2477 );
2478 assert_eq!(
2479 data.callback.cb,
2480 on_tab_click as usize,
2481 "the dispatcher must be the widget's own trampoline"
2482 );
2483 }
2484
2485 assert!(children[0].root.get_callbacks().as_ref().is_empty());
2487 assert!(children[n + 1].root.get_callbacks().as_ref().is_empty());
2488 }
2489
2490 #[test]
2491 fn dom_keeps_the_estimated_child_count_consistent() {
2492 for n in [0usize, 1, 2, 50] {
2495 let dom = TabHeader::create(numbered_labels(n)).dom();
2496 assert_eq!(
2497 dom.estimated_total_children,
2498 dom.recompute_estimated_total_children(),
2499 "n={n}: cached descendant count desynced"
2500 );
2501 assert_eq!(dom.node_count(), n + 3, "header + spacer + {n} tabs + spacer");
2502
2503 let styled = StyledDom::create_from_dom(dom);
2504 assert_eq!(
2505 styled.node_hierarchy.as_ref().len(),
2506 n + 3,
2507 "n={n}: the flattened arena must match node_count()"
2508 );
2509 }
2510 }
2511
2512 #[test]
2513 fn dom_releases_every_dataset_clone_when_the_dom_is_dropped() {
2514 for n in [0usize, 1, 5] {
2517 let user = RefAny::new(ClickLog::default());
2518 let dom = TabHeader::create(numbered_labels(n))
2519 .with_on_click(user.clone(), cb(record_click))
2520 .dom();
2521
2522 assert_eq!(
2523 user.get_ref_count(),
2524 n + 1,
2525 "n={n}: one payload clone per tab, plus the caller's handle"
2526 );
2527
2528 drop(dom);
2529 assert_eq!(
2530 user.get_ref_count(),
2531 1,
2532 "n={n}: dropping the DOM must release every payload clone"
2533 );
2534 }
2535 }
2536
2537 #[test]
2538 fn dom_is_deterministic() {
2539 let build = || {
2540 let dom = TabHeader::create(numbered_labels(5))
2541 .with_active_tab(2)
2542 .dom();
2543 let children = dom.children.as_ref();
2544 let shape: Vec<(Option<String>, Vec<&'static str>)> = children
2545 .iter()
2546 .map(|c| (text_of(c).map(str::to_string), class_strs(c)))
2547 .collect();
2548 shape
2549 };
2550 assert_eq!(build(), build(), "dom() must be a pure function of its state");
2551 }
2552
2553 fn nested(depth: usize) -> Dom {
2558 let mut dom = Dom::create_text("leaf");
2559 for _ in 0..depth {
2560 dom = Dom::create_div().with_child(dom);
2561 }
2562 dom
2563 }
2564
2565 #[test]
2566 fn content_new_defaults_to_padding_and_keeps_the_content_verbatim() {
2567 let content = nested(3);
2568 let tab_content = TabContent::new(content.clone());
2569
2570 assert!(tab_content.has_padding, "new() defaults to padded");
2571 assert_eq!(
2572 tab_content.content, content,
2573 "new() must not rewrap or normalise the content"
2574 );
2575 assert!(
2576 TabContent::default().has_padding,
2577 "Default agrees with new()"
2578 );
2579 }
2580
2581 #[test]
2582 fn with_padding_and_set_padding_agree_and_are_last_wins() {
2583 for flag in [true, false] {
2584 let built = TabContent::new(Dom::create_div()).with_padding(flag);
2585 let mut mutated = TabContent::new(Dom::create_div());
2586 mutated.set_padding(flag);
2587 assert_eq!(built.has_padding, flag);
2588 assert_eq!(built.has_padding, mutated.has_padding);
2589 }
2590
2591 let toggled = TabContent::new(Dom::create_div())
2592 .with_padding(false)
2593 .with_padding(true)
2594 .with_padding(false);
2595 assert!(!toggled.has_padding, "the last write must win");
2596 }
2597
2598 #[test]
2599 fn content_dom_nests_the_content_under_the_content_class() {
2600 let content = nested(2);
2601 let dom = TabContent::new(content.clone()).dom();
2602
2603 assert!(
2604 class_strs(&dom).is_empty(),
2605 "the outer wrapper carries the style, not the class"
2606 );
2607 assert_eq!(dom.children.as_ref().len(), 1, "one wrapper child");
2608
2609 let inner = &dom.children.as_ref()[0];
2610 assert_eq!(class_strs(inner), vec![CLASS_CONTENT]);
2611 assert!(
2612 inline_declared(inner).is_empty(),
2613 "the classed node carries no inline style of its own"
2614 );
2615 assert_eq!(inner.children.as_ref().len(), 1);
2616 assert_eq!(
2617 inner.children.as_ref()[0],
2618 content,
2619 "the user content must survive the two wrappers untouched"
2620 );
2621 }
2622
2623 #[test]
2624 fn content_dom_picks_the_style_vec_the_padding_flag_asks_for() {
2625 let padded = TabContent::new(Dom::create_div()).with_padding(true).dom();
2626 assert_eq!(
2627 inline_declared(&padded),
2628 declared(&CSS_MATCH_18014909903571752977)
2629 );
2630
2631 let bare = TabContent::new(Dom::create_div()).with_padding(false).dom();
2632 assert_eq!(
2633 inline_declared(&bare),
2634 declared(&CSS_MATCH_18014909903571752977_NO_PADDING)
2635 );
2636
2637 assert_ne!(
2638 inline_declared(&padded),
2639 inline_declared(&bare),
2640 "the two padding modes must not collapse to the same style"
2641 );
2642 }
2643
2644 #[test]
2645 fn the_unpadded_content_style_declares_no_padding_at_all() {
2646 let padding_decls = decls_where(&CSS_MATCH_18014909903571752977_NO_PADDING, |p| {
2647 matches!(
2648 p,
2649 CssProperty::PaddingTop(_)
2650 | CssProperty::PaddingBottom(_)
2651 | CssProperty::PaddingLeft(_)
2652 | CssProperty::PaddingRight(_)
2653 )
2654 });
2655 assert!(
2656 padding_decls.is_empty(),
2657 "has_padding == false must not leave a padding declaration behind"
2658 );
2659
2660 let padded = decls_where(&CSS_MATCH_18014909903571752977, |p| {
2661 matches!(
2662 p,
2663 CssProperty::PaddingTop(_)
2664 | CssProperty::PaddingBottom(_)
2665 | CssProperty::PaddingLeft(_)
2666 | CssProperty::PaddingRight(_)
2667 )
2668 });
2669 assert_eq!(padded.len(), 4, "the padded variant sets all four sides");
2670 }
2671
2672 #[test]
2673 fn content_dom_keeps_the_estimated_child_count_consistent() {
2674 for depth in [0usize, 1, 3, 64] {
2675 let content = nested(depth);
2676 let expected = content.node_count() + 2; let dom = TabContent::new(content).dom();
2678
2679 assert_eq!(
2680 dom.estimated_total_children,
2681 dom.recompute_estimated_total_children(),
2682 "depth={depth}: cached descendant count desynced"
2683 );
2684 assert_eq!(dom.node_count(), expected, "depth={depth}");
2685
2686 let styled = StyledDom::create_from_dom(dom);
2687 assert_eq!(
2688 styled.node_hierarchy.as_ref().len(),
2689 expected,
2690 "depth={depth}: the flattened arena must match node_count()"
2691 );
2692 }
2693 }
2694
2695 #[test]
2696 fn content_swap_with_default_returns_the_old_state_and_leaves_an_empty_div() {
2697 let content = nested(2);
2698 let mut tab_content = TabContent::new(content.clone()).with_padding(false);
2699
2700 let taken = tab_content.swap_with_default();
2701 assert_eq!(taken.content, content);
2702 assert!(!taken.has_padding, "the flag moves out with the content");
2703
2704 assert_eq!(
2705 tab_content.content,
2706 Dom::create_div(),
2707 "the husk must be an empty div"
2708 );
2709 assert!(
2710 tab_content.has_padding,
2711 "the husk is a Default, i.e. padded again"
2712 );
2713 }
2714
2715 #[test]
2720 fn on_tab_click_reports_the_clicked_index_to_the_user_callback() {
2721 for idx in [0usize, 1, 7, usize::MAX / 2, usize::MAX - 1, usize::MAX] {
2722 let mut user = RefAny::new(ClickLog::default());
2723 let (update, changes) =
2724 run_click(dataset(idx, click_handler(user.clone(), record_click)));
2725
2726 assert_eq!(update, Update::RefreshDom);
2727 assert!(
2728 changes.is_empty(),
2729 "the tab handler is stateless — it must not push CallbackChanges"
2730 );
2731 assert_eq!(
2732 logged(&mut user),
2733 vec![TabHeaderState { active_tab: idx }],
2734 "the index must be forwarded verbatim, without arithmetic"
2735 );
2736 }
2737 }
2738
2739 #[test]
2740 fn on_tab_click_propagates_the_user_update_verbatim() {
2741 for (callback, expected) in [
2742 (record_click as TabOnClickCallbackType, Update::RefreshDom),
2743 (click_do_nothing, Update::DoNothing),
2744 (click_refresh_all, Update::RefreshDomAllWindows),
2745 ] {
2746 let (update, _) = run_click(dataset(
2747 3,
2748 click_handler(RefAny::new(ClickLog::default()), callback),
2749 ));
2750 assert_eq!(update, expected, "the user verdict must not be overridden");
2751 }
2752 }
2753
2754 #[test]
2755 fn on_tab_click_without_a_user_callback_does_nothing() {
2756 let (update, changes) = run_click(dataset(2, None.into()));
2760 assert_eq!(update, Update::DoNothing);
2761 assert!(changes.is_empty());
2762 }
2763
2764 #[test]
2765 fn on_tab_click_on_a_foreign_payload_falls_back_to_refresh() {
2766 for foreign in [RefAny::new(42u32), RefAny::new(String::from("nope"))] {
2770 let (update, changes) = run_click(foreign);
2771 assert_eq!(update, Update::RefreshDom);
2772 assert!(changes.is_empty());
2773 }
2774 }
2775
2776 #[test]
2777 fn on_tab_click_declines_while_the_dataset_is_already_borrowed() {
2778 let mut held = dataset(1, None.into());
2781 let clone = held.clone();
2782 let guard = held
2783 .downcast_ref::<TabLocalDataset>()
2784 .expect("the fixture is a TabLocalDataset");
2785
2786 let (update, changes) = run_click(clone);
2787 assert_eq!(
2788 update,
2789 Update::RefreshDom,
2790 "a contended dataset must fall back, not panic"
2791 );
2792 assert!(changes.is_empty());
2793
2794 drop(guard);
2795
2796 let (update, _) = run_click(held.clone());
2798 assert_eq!(update, Update::DoNothing);
2799 }
2800
2801 #[test]
2802 fn on_tab_click_releases_its_borrow_so_repeated_clicks_keep_working() {
2803 let mut user = RefAny::new(ClickLog::default());
2804 let data = dataset(4, click_handler(user.clone(), record_click));
2805
2806 let (first, _) = run_click(data.clone());
2807 let (second, _) = run_click(data.clone());
2808 let (third, _) = run_click(data.clone());
2809
2810 assert_eq!(
2811 [first, second, third],
2812 [Update::RefreshDom; 3],
2813 "a leaked RefMut would turn later clicks into the RefreshDom fallback \
2814 without ever reaching the user callback"
2815 );
2816 assert_eq!(
2817 logged(&mut user),
2818 vec![TabHeaderState { active_tab: 4 }; 3],
2819 "every click must reach the user callback"
2820 );
2821 }
2822
2823 #[test]
2824 fn clicking_a_rendered_tab_selects_that_tab_end_to_end() {
2825 let n = 5usize;
2826 let mut user = RefAny::new(ClickLog::default());
2827 let dom = TabHeader::create(numbered_labels(n))
2828 .with_on_click(user.clone(), cb(record_click))
2829 .dom();
2830
2831 for i in 0..n {
2832 let data = dom.children.as_ref()[i + 1]
2833 .root
2834 .get_callbacks()
2835 .as_ref()
2836 .first()
2837 .expect("every tab carries the click callback")
2838 .refany
2839 .clone();
2840 let (update, changes) = run_click(data);
2841 assert_eq!(update, Update::RefreshDom);
2842 assert!(changes.is_empty());
2843 }
2844
2845 assert_eq!(
2846 logged(&mut user),
2847 (0..n)
2848 .map(|active_tab| TabHeaderState { active_tab })
2849 .collect::<Vec<_>>(),
2850 "clicking tab i must report exactly i"
2851 );
2852 }
2853
2854 #[test]
2855 fn clicking_a_tab_does_not_move_the_active_tab_by_itself() {
2856 let user = RefAny::new(ClickLog::default());
2859 let header = TabHeader::create(numbered_labels(3))
2860 .with_on_click(user.clone(), cb(record_click));
2861 let dom = header.clone().dom();
2862
2863 let data = dom.children.as_ref()[3]
2864 .root
2865 .get_callbacks()
2866 .as_ref()
2867 .first()
2868 .expect("tab 2 carries the click callback")
2869 .refany
2870 .clone();
2871 let (update, _) = run_click(data);
2872 assert_eq!(update, Update::RefreshDom);
2873
2874 assert_eq!(
2875 header.active_tab, 0,
2876 "the header itself must be untouched by a click"
2877 );
2878 assert_eq!(
2879 class_strs(&header.dom().children.as_ref()[1]),
2880 vec![CLASS_ACTIVE],
2881 "re-rendering the unchanged header keeps tab 0 active"
2882 );
2883 }
2884}