1use std::{fmt, mem, sync::Arc};
4
5use zng_app_context::context_local;
6use zng_color::{Hsla, Hsva, Rgba, colors};
7use zng_layout::{
8 context::{LAYOUT, LayoutMask},
9 unit::{
10 Factor, FactorPercent, FactorSideOffsets, FactorUnits, Layout2d, Length, PxCornerRadius, PxPoint, PxRect, PxSideOffsets, PxSize,
11 Size,
12 },
13};
14use zng_var::{
15 animation::{Transitionable, easing::EasingStep},
16 context_var, impl_from_and_into_var,
17};
18
19pub use zng_view_api::LineOrientation;
20
21use crate::widget::VarLayout;
22
23use super::{WIDGET, WidgetId, info::WidgetBorderInfo};
24
25#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
27pub enum LineStyle {
28 Solid,
30 Double,
32
33 Dotted,
35 Dashed,
37
38 Groove,
40 Ridge,
42
43 Wavy(f32),
48
49 Hidden,
54}
55impl fmt::Debug for LineStyle {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 if f.alternate() {
58 write!(f, "LineStyle::")?;
59 }
60 match self {
61 LineStyle::Solid => write!(f, "Solid"),
62 LineStyle::Double => write!(f, "Double"),
63 LineStyle::Dotted => write!(f, "Dotted"),
64 LineStyle::Dashed => write!(f, "Dashed"),
65 LineStyle::Groove => write!(f, "Groove"),
66 LineStyle::Ridge => write!(f, "Ridge"),
67 LineStyle::Wavy(t) => write!(f, "Wavy({t})"),
68 LineStyle::Hidden => write!(f, "Hidden"),
69 }
70 }
71}
72impl Transitionable for LineStyle {
73 fn lerp(self, to: &Self, step: EasingStep) -> Self {
74 match (self, *to) {
75 (Self::Wavy(a), Self::Wavy(b)) => Self::Wavy(a.lerp(&b, step)),
76 (a, b) => {
77 if step < 1.fct() {
78 a
79 } else {
80 b
81 }
82 }
83 }
84 }
85}
86
87#[repr(u8)]
89#[derive(Clone, Copy, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
90pub enum BorderStyle {
91 Solid = 1,
93 Double = 2,
95
96 Dotted = 3,
98 Dashed = 4,
100
101 Hidden = 5,
103
104 Groove = 6,
106 Ridge = 7,
108
109 Inset = 8,
111 Outset = 9,
113}
114impl fmt::Debug for BorderStyle {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 if f.alternate() {
117 write!(f, "BorderStyle::")?;
118 }
119 match self {
120 BorderStyle::Solid => write!(f, "Solid"),
121 BorderStyle::Double => write!(f, "Double"),
122 BorderStyle::Dotted => write!(f, "Dotted"),
123 BorderStyle::Dashed => write!(f, "Dashed"),
124 BorderStyle::Groove => write!(f, "Groove"),
125 BorderStyle::Ridge => write!(f, "Ridge"),
126 BorderStyle::Hidden => write!(f, "Hidden"),
127 BorderStyle::Inset => write!(f, "Inset"),
128 BorderStyle::Outset => write!(f, "Outset"),
129 }
130 }
131}
132impl From<BorderStyle> for zng_view_api::BorderStyle {
133 fn from(s: BorderStyle) -> Self {
134 match s {
135 BorderStyle::Solid => zng_view_api::BorderStyle::Solid,
136 BorderStyle::Double => zng_view_api::BorderStyle::Double,
137 BorderStyle::Dotted => zng_view_api::BorderStyle::Dotted,
138 BorderStyle::Dashed => zng_view_api::BorderStyle::Dashed,
139 BorderStyle::Hidden => zng_view_api::BorderStyle::Hidden,
140 BorderStyle::Groove => zng_view_api::BorderStyle::Groove,
141 BorderStyle::Ridge => zng_view_api::BorderStyle::Ridge,
142 BorderStyle::Inset => zng_view_api::BorderStyle::Inset,
143 BorderStyle::Outset => zng_view_api::BorderStyle::Outset,
144 }
145 }
146}
147impl Transitionable for BorderStyle {
148 fn lerp(self, to: &Self, step: EasingStep) -> Self {
150 if step < 1.fct() { self } else { *to }
151 }
152}
153
154#[repr(C)]
156#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
157pub struct BorderSide {
158 pub color: Rgba,
160 pub style: BorderStyle,
162}
163impl fmt::Debug for BorderSide {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 if f.alternate() {
166 f.debug_struct("BorderSide")
167 .field("color", &self.color)
168 .field("style", &self.style)
169 .finish()
170 } else {
171 if let BorderStyle::Hidden = self.style
172 && self.color.alpha.abs() < 0.0001
173 {
174 return write!(f, "Hidden");
175 }
176 write!(f, "({:?}, {:?})", self.color, self.style)
177 }
178 }
179}
180impl BorderSide {
181 pub fn new<C: Into<Rgba>, S: Into<BorderStyle>>(color: C, style: S) -> Self {
183 BorderSide {
184 color: color.into(),
185 style: style.into(),
186 }
187 }
188
189 pub fn solid<C: Into<Rgba>>(color: C) -> Self {
191 Self::new(color, BorderStyle::Solid)
192 }
193 pub fn double<C: Into<Rgba>>(color: C) -> Self {
195 Self::new(color, BorderStyle::Double)
196 }
197
198 pub fn dotted<C: Into<Rgba>>(color: C) -> Self {
200 Self::new(color, BorderStyle::Dotted)
201 }
202 pub fn dashed<C: Into<Rgba>>(color: C) -> Self {
204 Self::new(color, BorderStyle::Dashed)
205 }
206
207 pub fn groove<C: Into<Rgba>>(color: C) -> Self {
209 Self::new(color, BorderStyle::Groove)
210 }
211 pub fn ridge<C: Into<Rgba>>(color: C) -> Self {
213 Self::new(color, BorderStyle::Ridge)
214 }
215
216 pub fn inset<C: Into<Rgba>>(color: C) -> Self {
218 Self::new(color, BorderStyle::Inset)
219 }
220
221 pub fn outset<C: Into<Rgba>>(color: C) -> Self {
223 Self::new(color, BorderStyle::Outset)
224 }
225
226 pub fn hidden() -> Self {
228 Self::new(colors::BLACK.transparent(), BorderStyle::Hidden)
229 }
230}
231impl From<BorderSide> for zng_view_api::BorderSide {
232 fn from(s: BorderSide) -> Self {
233 zng_view_api::BorderSide {
234 color: s.color,
235 style: s.style.into(),
236 }
237 }
238}
239impl Default for BorderSide {
240 fn default() -> Self {
242 Self::hidden()
243 }
244}
245
246#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
250pub struct CornerRadius {
251 pub top_left: Size,
253 pub top_right: Size,
255 pub bottom_right: Size,
257 pub bottom_left: Size,
259}
260impl fmt::Debug for CornerRadius {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 if f.alternate() {
263 f.debug_struct("BorderRadius")
264 .field("top_left", &self.top_left)
265 .field("top_right", &self.top_right)
266 .field("bottom_right", &self.bottom_right)
267 .field("bottom_left", &self.bottom_left)
268 .finish()
269 } else if self.all_corners_eq() {
270 write!(f, "{:?}", self.top_left)
271 } else {
272 write!(
273 f,
274 "({:?}, {:?}, {:?}, {:?})",
275 self.top_left, self.top_right, self.bottom_right, self.bottom_left
276 )
277 }
278 }
279}
280impl CornerRadius {
281 pub fn new<TL: Into<Size>, TR: Into<Size>, BR: Into<Size>, BL: Into<Size>>(
283 top_left: TL,
284 top_right: TR,
285 bottom_right: BR,
286 bottom_left: BL,
287 ) -> Self {
288 CornerRadius {
289 top_left: top_left.into(),
290 top_right: top_right.into(),
291 bottom_right: bottom_right.into(),
292 bottom_left: bottom_left.into(),
293 }
294 }
295
296 pub fn new_all<E: Into<Size>>(ellipse: E) -> Self {
298 let e = ellipse.into();
299 CornerRadius {
300 top_left: e.clone(),
301 top_right: e.clone(),
302 bottom_left: e.clone(),
303 bottom_right: e,
304 }
305 }
306
307 pub fn zero() -> Self {
309 Self::new_all(Size::zero())
310 }
311
312 pub fn all_corners_eq(&self) -> bool {
314 self.top_left == self.top_right && self.top_left == self.bottom_right && self.top_left == self.bottom_left
315 }
316}
317impl Layout2d for CornerRadius {
318 type Px = PxCornerRadius;
319
320 fn layout_dft(&self, default: Self::Px) -> Self::Px {
321 PxCornerRadius {
322 top_left: self.top_left.layout_dft(default.top_left),
323 top_right: self.top_right.layout_dft(default.top_right),
324 bottom_left: self.bottom_left.layout_dft(default.bottom_left),
325 bottom_right: self.bottom_right.layout_dft(default.bottom_right),
326 }
327 }
328
329 fn affect_mask(&self) -> LayoutMask {
330 self.top_left.affect_mask() | self.top_right.affect_mask() | self.bottom_left.affect_mask() | self.bottom_right.affect_mask()
331 }
332}
333impl_from_and_into_var! {
334 fn from(all: Size) -> CornerRadius {
336 CornerRadius::new_all(all)
337 }
338 fn from(all: Length) -> CornerRadius {
340 CornerRadius::new_all(all)
341 }
342
343 fn from(percent: FactorPercent) -> CornerRadius {
345 CornerRadius::new_all(percent)
346 }
347 fn from(norm: Factor) -> CornerRadius {
349 CornerRadius::new_all(norm)
350 }
351
352 fn from(f: f32) -> CornerRadius {
354 CornerRadius::new_all(f)
355 }
356 fn from(i: i32) -> CornerRadius {
358 CornerRadius::new_all(i)
359 }
360
361 fn from<TL: Into<Size>, TR: Into<Size>, BR: Into<Size>, BL: Into<Size>>(
363 (top_left, top_right, bottom_right, bottom_left): (TL, TR, BR, BL),
364 ) -> CornerRadius {
365 CornerRadius::new(top_left, top_right, bottom_right, bottom_left)
366 }
367
368 fn from(corner_radius: PxCornerRadius) -> CornerRadius {
370 CornerRadius::new(
371 corner_radius.top_left,
372 corner_radius.top_right,
373 corner_radius.bottom_right,
374 corner_radius.bottom_left,
375 )
376 }
377}
378
379#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
381pub struct BorderSides {
382 pub left: BorderSide,
384 pub right: BorderSide,
386
387 pub top: BorderSide,
389 pub bottom: BorderSide,
391}
392impl fmt::Debug for BorderSides {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 if f.alternate() {
395 f.debug_struct("BorderSides")
396 .field("left", &self.left)
397 .field("right", &self.right)
398 .field("top", &self.top)
399 .field("bottom", &self.bottom)
400 .finish()
401 } else if self.all_eq() {
402 write!(f, "{:?}", self.top)
403 } else if self.dimensions_eq() {
404 write!(f, "({:?}, {:?})", self.top, self.left)
405 } else {
406 write!(f, "({:?}, {:?}, {:?}, {:?})", self.top, self.right, self.bottom, self.left)
407 }
408 }
409}
410impl BorderSides {
411 pub fn new_all<S: Into<BorderSide>>(side: S) -> Self {
413 let side = side.into();
414 BorderSides {
415 left: side,
416 right: side,
417 top: side,
418 bottom: side,
419 }
420 }
421
422 pub fn new_vh<TB: Into<BorderSide>, LR: Into<BorderSide>>(top_bottom: TB, left_right: LR) -> Self {
424 let top_bottom = top_bottom.into();
425 let left_right = left_right.into();
426 BorderSides {
427 left: left_right,
428 right: left_right,
429 top: top_bottom,
430 bottom: top_bottom,
431 }
432 }
433
434 pub fn new<T: Into<BorderSide>, R: Into<BorderSide>, B: Into<BorderSide>, L: Into<BorderSide>>(
436 top: T,
437 right: R,
438 bottom: B,
439 left: L,
440 ) -> Self {
441 BorderSides {
442 left: left.into(),
443 right: right.into(),
444 top: top.into(),
445 bottom: bottom.into(),
446 }
447 }
448
449 pub fn new_top<T: Into<BorderSide>>(top: T) -> Self {
451 Self::new(top, BorderSide::hidden(), BorderSide::hidden(), BorderSide::hidden())
452 }
453
454 pub fn new_right<R: Into<BorderSide>>(right: R) -> Self {
456 Self::new(BorderSide::hidden(), right, BorderSide::hidden(), BorderSide::hidden())
457 }
458
459 pub fn new_bottom<B: Into<BorderSide>>(bottom: B) -> Self {
461 Self::new(BorderSide::hidden(), BorderSide::hidden(), bottom, BorderSide::hidden())
462 }
463
464 pub fn new_left<L: Into<BorderSide>>(left: L) -> Self {
466 Self::new(BorderSide::hidden(), BorderSide::hidden(), BorderSide::hidden(), left)
467 }
468
469 pub fn solid<C: Into<Rgba>>(color: C) -> Self {
471 Self::new_all(BorderSide::solid(color))
472 }
473 pub fn double<C: Into<Rgba>>(color: C) -> Self {
475 Self::new_all(BorderSide::double(color))
476 }
477
478 pub fn dotted<C: Into<Rgba>>(color: C) -> Self {
480 Self::new_all(BorderSide::dotted(color))
481 }
482 pub fn dashed<C: Into<Rgba>>(color: C) -> Self {
484 Self::new_all(BorderSide::dashed(color))
485 }
486
487 pub fn groove<C: Into<Rgba>>(color: C) -> Self {
489 Self::new_all(BorderSide::groove(color))
490 }
491 pub fn ridge<C: Into<Rgba>>(color: C) -> Self {
493 Self::new_all(BorderSide::ridge(color))
494 }
495
496 pub fn inset<C: Into<Rgba>>(color: C) -> Self {
498 Self::new_all(BorderSide::inset(color))
499 }
500 pub fn outset<C: Into<Rgba>>(color: C) -> Self {
502 Self::new_all(BorderSide::outset(color))
503 }
504
505 pub fn hidden() -> Self {
507 Self::new_all(BorderSide::hidden())
508 }
509
510 pub fn all_eq(&self) -> bool {
512 self.top == self.bottom && self.top == self.left && self.top == self.right
513 }
514
515 pub fn dimensions_eq(&self) -> bool {
517 self.top == self.bottom && self.left == self.right
518 }
519}
520impl Default for BorderSides {
521 fn default() -> Self {
523 Self::hidden()
524 }
525}
526
527impl_from_and_into_var! {
528 fn from(color: Rgba) -> BorderSide {
530 BorderSide::solid(color)
531 }
532 fn from(color: Hsva) -> BorderSide {
534 BorderSide::solid(color)
535 }
536 fn from(color: Hsla) -> BorderSide {
538 BorderSide::solid(color)
539 }
540 fn from(color: Rgba) -> BorderSides {
542 BorderSides::new_all(color)
543 }
544 fn from(color: Hsva) -> BorderSides {
546 BorderSides::new_all(color)
547 }
548 fn from(color: Hsla) -> BorderSides {
550 BorderSides::new_all(color)
551 }
552
553 fn from(style: BorderStyle) -> BorderSide {
557 BorderSide::new(colors::BLACK.transparent(), style)
558 }
559 fn from(style: BorderStyle) -> BorderSides {
563 BorderSides::new_all(style)
564 }
565
566 fn from<C: Into<Rgba>, S: Into<BorderStyle>>((color, style): (C, S)) -> BorderSide {
568 BorderSide::new(color, style)
569 }
570
571 fn from<C: Into<Rgba>, S: Into<BorderStyle>>((color, style): (C, S)) -> BorderSides {
573 BorderSides::new_all(BorderSide::new(color, style))
574 }
575
576 fn from<T: Into<BorderSide>, R: Into<BorderSide>, B: Into<BorderSide>, L: Into<BorderSide>>(
578 (top, right, bottom, left): (T, R, B, L),
579 ) -> BorderSides {
580 BorderSides::new(top, right, bottom, left)
581 }
582
583 fn from<TB: Into<Rgba>, LR: Into<Rgba>, S: Into<BorderStyle>>((top_bottom, left_right, style): (TB, LR, S)) -> BorderSides {
585 let style = style.into();
586 BorderSides::new_vh((top_bottom, style), (left_right, style))
587 }
588
589 fn from<T: Into<Rgba>, R: Into<Rgba>, B: Into<Rgba>, L: Into<Rgba>, S: Into<BorderStyle>>(
591 (top, right, bottom, left, style): (T, R, B, L, S),
592 ) -> BorderSides {
593 let style = style.into();
594 BorderSides::new((top, style), (right, style), (bottom, style), (left, style))
595 }
596}
597
598#[derive(Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
604pub enum CornerRadiusFit {
605 None,
607 Widget,
611 #[default]
617 Tree,
618}
619impl fmt::Debug for CornerRadiusFit {
620 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621 if f.alternate() {
622 write!(f, "CornerRadiusFit::")?;
623 }
624 match self {
625 Self::None => write!(f, "None"),
626 Self::Widget => write!(f, "Widget"),
627 Self::Tree => write!(f, "Tree"),
628 }
629 }
630}
631
632context_var! {
633 pub static BORDER_ALIGN_VAR: FactorSideOffsets = FactorSideOffsets::zero();
635
636 pub static BORDER_OVER_VAR: bool = true;
638
639 pub static CORNER_RADIUS_VAR: CornerRadius = CornerRadius::zero();
641
642 pub static CORNER_RADIUS_FIT_VAR: CornerRadiusFit = CornerRadiusFit::default();
644}
645
646pub struct BORDER;
648impl BORDER {
649 pub fn border_offsets(&self) -> PxSideOffsets {
653 let data = BORDER_DATA.get();
654 if data.widget_id == WIDGET.try_id() {
655 data.wgt_offsets
656 } else {
657 PxSideOffsets::zero()
658 }
659 }
660
661 pub fn inner_offsets(&self) -> PxSideOffsets {
663 let data = BORDER_DATA.get();
664 if data.widget_id == WIDGET.try_id() {
665 data.wgt_inner_offsets
666 } else {
667 PxSideOffsets::zero()
668 }
669 }
670
671 pub fn border_radius(&self) -> PxCornerRadius {
675 match CORNER_RADIUS_FIT_VAR.get() {
676 CornerRadiusFit::Tree => BORDER_DATA.get().border_radius(),
677 CornerRadiusFit::Widget => {
678 let data = BORDER_DATA.get();
679 if data.widget_id == Some(WIDGET.id()) {
680 data.border_radius()
681 } else {
682 CORNER_RADIUS_VAR.layout()
683 }
684 }
685 _ => CORNER_RADIUS_VAR.layout(),
686 }
687 }
688
689 pub fn inner_radius(&self) -> PxCornerRadius {
691 match CORNER_RADIUS_FIT_VAR.get() {
692 CornerRadiusFit::Tree => BORDER_DATA.get().inner_radius(),
693 CornerRadiusFit::Widget => {
694 let data = BORDER_DATA.get();
695 if data.widget_id == WIDGET.try_id() {
696 data.inner_radius()
697 } else {
698 CORNER_RADIUS_VAR.layout()
699 }
700 }
701 _ => CORNER_RADIUS_VAR.layout(),
702 }
703 }
704
705 pub fn outer_radius(&self) -> PxCornerRadius {
707 BORDER_DATA.get().corner_radius
708 }
709
710 pub fn fill_bounds(&self) -> (PxRect, PxCornerRadius) {
716 let align = BORDER_ALIGN_VAR.get();
717
718 let fill_size = LAYOUT.constraints().fill_size();
719 let inner_offsets = self.inner_offsets();
720
721 if align == FactorSideOffsets::zero() {
722 let fill_size = PxSize::new(
723 fill_size.width + inner_offsets.horizontal(),
724 fill_size.height + inner_offsets.vertical(),
725 );
726 return (PxRect::from_size(fill_size), self.outer_radius());
727 } else if align == FactorSideOffsets::new_all(1.0.fct()) {
728 return (
729 PxRect::new(PxPoint::new(inner_offsets.left, inner_offsets.top), fill_size),
730 self.inner_radius(),
731 );
732 }
733
734 let outer = self.outer_radius();
735 let inner = self.inner_radius();
736
737 let b_align = FactorSideOffsets {
738 top: 1.0.fct() - align.top,
739 right: 1.0.fct() - align.right,
740 bottom: 1.0.fct() - align.bottom,
741 left: 1.0.fct() - align.left,
742 };
743 let bounds = PxRect {
744 origin: PxPoint::new(inner_offsets.left * (align.left), inner_offsets.top * align.top),
745 size: PxSize::new(
746 fill_size.width + inner_offsets.left * b_align.left + inner_offsets.right * b_align.right,
747 fill_size.height + inner_offsets.top * b_align.top + inner_offsets.bottom * b_align.bottom,
748 ),
749 };
750
751 let radius = PxCornerRadius {
752 top_left: PxSize::new(
753 outer.top_left.width.lerp(&inner.top_left.width, align.left),
754 outer.top_left.height.lerp(&inner.top_left.height, align.top),
755 ),
756 top_right: PxSize::new(
757 outer.top_right.width.lerp(&inner.top_right.width, align.right),
758 outer.top_right.height.lerp(&inner.top_right.height, align.top),
759 ),
760 bottom_left: PxSize::new(
761 outer.bottom_left.width.lerp(&inner.bottom_left.width, align.left),
762 outer.bottom_left.height.lerp(&inner.bottom_left.height, align.bottom),
763 ),
764 bottom_right: PxSize::new(
765 outer.bottom_right.width.lerp(&inner.bottom_right.width, align.right),
766 outer.bottom_right.height.lerp(&inner.bottom_right.height, align.bottom),
767 ),
768 };
769
770 (bounds, radius)
771 }
772
773 pub(super) fn with_inner(&self, f: impl FnOnce() -> PxSize) -> PxSize {
774 let mut data = BORDER_DATA.get_clone();
775 let border = WIDGET.border();
776 data.add_inner(&border);
777
778 BORDER_DATA.with_context(&mut Some(Arc::new(data)), || {
779 let corner_radius = BORDER.border_radius();
780 border.set_corner_radius(corner_radius);
781 border.set_offsets(PxSideOffsets::zero());
782 f()
783 })
784 }
785
786 pub fn measure_border(&self, offsets: PxSideOffsets, f: impl FnOnce() -> PxSize) -> PxSize {
788 let mut data = BORDER_DATA.get_clone();
789 data.add_offset(None, offsets);
790 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f)
791 }
792
793 pub fn layout_border(&self, offsets: PxSideOffsets, f: impl FnOnce()) {
795 let mut data = BORDER_DATA.get_clone();
796 data.add_offset(Some(&WIDGET.border()), offsets);
797 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f);
798 }
799
800 pub fn with_corner_radius<R>(&self, f: impl FnOnce() -> R) -> R {
808 let mut data = BORDER_DATA.get_clone();
809 data.set_corner_radius();
810 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f)
811 }
812
813 pub fn border_layout(&self) -> (PxRect, PxSideOffsets) {
817 BORDER_LAYOUT.get().unwrap_or_else(|| {
818 #[cfg(debug_assertions)]
819 tracing::error!("the `border_layout` is only available inside the layout and render methods of the border visual node");
820 (PxRect::zero(), PxSideOffsets::zero())
821 })
822 }
823
824 pub fn with_border_layout(&self, rect: PxRect, offsets: PxSideOffsets, f: impl FnOnce()) {
826 BORDER_LAYOUT.with_context(&mut Some(Arc::new(Some((rect, offsets)))), f)
827 }
828}
829
830context_local! {
831 static BORDER_DATA: BorderOffsetsData = BorderOffsetsData::default();
832 static BORDER_LAYOUT: Option<(PxRect, PxSideOffsets)> = None;
833}
834
835#[derive(Debug, Clone, Default)]
836struct BorderOffsetsData {
837 widget_id: Option<WidgetId>,
838 wgt_offsets: PxSideOffsets,
839 wgt_inner_offsets: PxSideOffsets,
840
841 eval_cr: bool,
842 corner_radius: PxCornerRadius,
843 cr_offsets: PxSideOffsets,
844 cr_inner_offsets: PxSideOffsets,
845}
846impl BorderOffsetsData {
847 fn add_offset(&mut self, layout_info: Option<&WidgetBorderInfo>, offset: PxSideOffsets) {
851 let widget_id = Some(WIDGET.id());
852 let is_wgt_start = self.widget_id != widget_id;
853 if is_wgt_start {
854 self.widget_id = widget_id;
856 self.wgt_offsets = PxSideOffsets::zero();
857 self.wgt_inner_offsets = PxSideOffsets::zero();
858 self.eval_cr |= layout_info.is_some() && matches!(CORNER_RADIUS_FIT_VAR.get(), CornerRadiusFit::Widget);
859 }
860 self.wgt_offsets = self.wgt_inner_offsets;
861 self.wgt_inner_offsets += offset;
862
863 if mem::take(&mut self.eval_cr) {
864 self.corner_radius = CORNER_RADIUS_VAR.layout();
865 self.cr_offsets = PxSideOffsets::zero();
866 self.cr_inner_offsets = PxSideOffsets::zero();
867 }
868 self.cr_offsets = self.cr_inner_offsets;
869 self.cr_inner_offsets += offset;
870
871 if let Some(border) = layout_info {
872 if is_wgt_start {
873 border.set_corner_radius(self.corner_radius);
874 }
875 border.set_offsets(self.wgt_inner_offsets);
876 }
877 }
878
879 fn add_inner(&mut self, layout_info: &WidgetBorderInfo) {
880 self.add_offset(Some(layout_info), PxSideOffsets::zero());
882 }
883
884 fn set_corner_radius(&mut self) {
885 self.eval_cr = matches!(CORNER_RADIUS_FIT_VAR.get(), CornerRadiusFit::Tree);
886 }
887
888 fn border_radius(&self) -> PxCornerRadius {
889 self.corner_radius.deflate(self.cr_offsets)
890 }
891
892 fn inner_radius(&self) -> PxCornerRadius {
893 self.corner_radius.deflate(self.cr_inner_offsets)
894 }
895}