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 Var,
16 animation::{Transitionable, easing::EasingStep},
17 context_var, impl_from_and_into_var,
18};
19
20pub use zng_view_api::LineOrientation;
21
22use crate::widget::VarLayout;
23
24use super::{WIDGET, WidgetId, info::WidgetBorderInfo};
25
26#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
28pub enum LineStyle {
29 Solid,
31 Double,
33
34 Dotted,
36 Dashed,
38
39 Groove,
41 Ridge,
43
44 Wavy(f32),
49
50 Hidden,
55}
56impl fmt::Debug for LineStyle {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 if f.alternate() {
59 write!(f, "LineStyle::")?;
60 }
61 match self {
62 LineStyle::Solid => write!(f, "Solid"),
63 LineStyle::Double => write!(f, "Double"),
64 LineStyle::Dotted => write!(f, "Dotted"),
65 LineStyle::Dashed => write!(f, "Dashed"),
66 LineStyle::Groove => write!(f, "Groove"),
67 LineStyle::Ridge => write!(f, "Ridge"),
68 LineStyle::Wavy(t) => write!(f, "Wavy({t})"),
69 LineStyle::Hidden => write!(f, "Hidden"),
70 }
71 }
72}
73impl Transitionable for LineStyle {
74 fn lerp(self, to: &Self, step: EasingStep) -> Self {
75 match (self, *to) {
76 (Self::Wavy(a), Self::Wavy(b)) => Self::Wavy(a.lerp(&b, step)),
77 (a, b) => {
78 if step < 1.fct() {
79 a
80 } else {
81 b
82 }
83 }
84 }
85 }
86}
87
88#[repr(u8)]
90#[derive(Clone, Copy, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
91pub enum BorderStyle {
92 Solid = 1,
94 Double = 2,
96
97 Dotted = 3,
99 Dashed = 4,
101
102 Hidden = 5,
104
105 Groove = 6,
107 Ridge = 7,
109
110 Inset = 8,
112 Outset = 9,
114}
115impl fmt::Debug for BorderStyle {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 if f.alternate() {
118 write!(f, "BorderStyle::")?;
119 }
120 match self {
121 BorderStyle::Solid => write!(f, "Solid"),
122 BorderStyle::Double => write!(f, "Double"),
123 BorderStyle::Dotted => write!(f, "Dotted"),
124 BorderStyle::Dashed => write!(f, "Dashed"),
125 BorderStyle::Groove => write!(f, "Groove"),
126 BorderStyle::Ridge => write!(f, "Ridge"),
127 BorderStyle::Hidden => write!(f, "Hidden"),
128 BorderStyle::Inset => write!(f, "Inset"),
129 BorderStyle::Outset => write!(f, "Outset"),
130 }
131 }
132}
133impl From<BorderStyle> for zng_view_api::BorderStyle {
134 fn from(s: BorderStyle) -> Self {
135 match s {
136 BorderStyle::Solid => zng_view_api::BorderStyle::Solid,
137 BorderStyle::Double => zng_view_api::BorderStyle::Double,
138 BorderStyle::Dotted => zng_view_api::BorderStyle::Dotted,
139 BorderStyle::Dashed => zng_view_api::BorderStyle::Dashed,
140 BorderStyle::Hidden => zng_view_api::BorderStyle::Hidden,
141 BorderStyle::Groove => zng_view_api::BorderStyle::Groove,
142 BorderStyle::Ridge => zng_view_api::BorderStyle::Ridge,
143 BorderStyle::Inset => zng_view_api::BorderStyle::Inset,
144 BorderStyle::Outset => zng_view_api::BorderStyle::Outset,
145 }
146 }
147}
148impl Transitionable for BorderStyle {
149 fn lerp(self, to: &Self, step: EasingStep) -> Self {
151 if step < 1.fct() { self } else { *to }
152 }
153}
154
155#[repr(C)]
157#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
158pub struct BorderSide {
159 pub color: Rgba,
161 pub style: BorderStyle,
163}
164impl fmt::Debug for BorderSide {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 if f.alternate() {
167 f.debug_struct("BorderSide")
168 .field("color", &self.color)
169 .field("style", &self.style)
170 .finish()
171 } else {
172 if let BorderStyle::Hidden = self.style {
173 if self.color.alpha.abs() < 0.0001 {
174 return write!(f, "Hidden");
175 }
176 }
177 write!(f, "({:?}, {:?})", self.color, self.style)
178 }
179 }
180}
181impl BorderSide {
182 pub fn new<C: Into<Rgba>, S: Into<BorderStyle>>(color: C, style: S) -> Self {
184 BorderSide {
185 color: color.into(),
186 style: style.into(),
187 }
188 }
189
190 pub fn solid<C: Into<Rgba>>(color: C) -> Self {
192 Self::new(color, BorderStyle::Solid)
193 }
194 pub fn double<C: Into<Rgba>>(color: C) -> Self {
196 Self::new(color, BorderStyle::Double)
197 }
198
199 pub fn dotted<C: Into<Rgba>>(color: C) -> Self {
201 Self::new(color, BorderStyle::Dotted)
202 }
203 pub fn dashed<C: Into<Rgba>>(color: C) -> Self {
205 Self::new(color, BorderStyle::Dashed)
206 }
207
208 pub fn groove<C: Into<Rgba>>(color: C) -> Self {
210 Self::new(color, BorderStyle::Groove)
211 }
212 pub fn ridge<C: Into<Rgba>>(color: C) -> Self {
214 Self::new(color, BorderStyle::Ridge)
215 }
216
217 pub fn inset<C: Into<Rgba>>(color: C) -> Self {
219 Self::new(color, BorderStyle::Inset)
220 }
221
222 pub fn outset<C: Into<Rgba>>(color: C) -> Self {
224 Self::new(color, BorderStyle::Outset)
225 }
226
227 pub fn hidden() -> Self {
229 Self::new(colors::BLACK.transparent(), BorderStyle::Hidden)
230 }
231}
232impl From<BorderSide> for zng_view_api::BorderSide {
233 fn from(s: BorderSide) -> Self {
234 zng_view_api::BorderSide {
235 color: s.color,
236 style: s.style.into(),
237 }
238 }
239}
240impl Default for BorderSide {
241 fn default() -> Self {
243 Self::hidden()
244 }
245}
246
247#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
251pub struct CornerRadius {
252 pub top_left: Size,
254 pub top_right: Size,
256 pub bottom_right: Size,
258 pub bottom_left: Size,
260}
261impl fmt::Debug for CornerRadius {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 if f.alternate() {
264 f.debug_struct("BorderRadius")
265 .field("top_left", &self.top_left)
266 .field("top_right", &self.top_right)
267 .field("bottom_right", &self.bottom_right)
268 .field("bottom_left", &self.bottom_left)
269 .finish()
270 } else if self.all_corners_eq() {
271 write!(f, "{:?}", self.top_left)
272 } else {
273 write!(
274 f,
275 "({:?}, {:?}, {:?}, {:?})",
276 self.top_left, self.top_right, self.bottom_right, self.bottom_left
277 )
278 }
279 }
280}
281impl CornerRadius {
282 pub fn new<TL: Into<Size>, TR: Into<Size>, BR: Into<Size>, BL: Into<Size>>(
284 top_left: TL,
285 top_right: TR,
286 bottom_right: BR,
287 bottom_left: BL,
288 ) -> Self {
289 CornerRadius {
290 top_left: top_left.into(),
291 top_right: top_right.into(),
292 bottom_right: bottom_right.into(),
293 bottom_left: bottom_left.into(),
294 }
295 }
296
297 pub fn new_all<E: Into<Size>>(ellipse: E) -> Self {
299 let e = ellipse.into();
300 CornerRadius {
301 top_left: e.clone(),
302 top_right: e.clone(),
303 bottom_left: e.clone(),
304 bottom_right: e,
305 }
306 }
307
308 pub fn zero() -> Self {
310 Self::new_all(Size::zero())
311 }
312
313 pub fn all_corners_eq(&self) -> bool {
315 self.top_left == self.top_right && self.top_left == self.bottom_right && self.top_left == self.bottom_left
316 }
317}
318impl Layout2d for CornerRadius {
319 type Px = PxCornerRadius;
320
321 fn layout_dft(&self, default: Self::Px) -> Self::Px {
322 PxCornerRadius {
323 top_left: self.top_left.layout_dft(default.top_left),
324 top_right: self.top_right.layout_dft(default.top_right),
325 bottom_left: self.bottom_left.layout_dft(default.bottom_left),
326 bottom_right: self.bottom_right.layout_dft(default.bottom_right),
327 }
328 }
329
330 fn affect_mask(&self) -> LayoutMask {
331 self.top_left.affect_mask() | self.top_right.affect_mask() | self.bottom_left.affect_mask() | self.bottom_right.affect_mask()
332 }
333}
334impl_from_and_into_var! {
335 fn from(all: Size) -> CornerRadius {
337 CornerRadius::new_all(all)
338 }
339 fn from(all: Length) -> CornerRadius {
341 CornerRadius::new_all(all)
342 }
343
344 fn from(percent: FactorPercent) -> CornerRadius {
346 CornerRadius::new_all(percent)
347 }
348 fn from(norm: Factor) -> CornerRadius {
350 CornerRadius::new_all(norm)
351 }
352
353 fn from(f: f32) -> CornerRadius {
355 CornerRadius::new_all(f)
356 }
357 fn from(i: i32) -> CornerRadius {
359 CornerRadius::new_all(i)
360 }
361
362 fn from<TL: Into<Size>, TR: Into<Size>, BR: Into<Size>, BL: Into<Size>>(
364 (top_left, top_right, bottom_right, bottom_left): (TL, TR, BR, BL),
365 ) -> CornerRadius {
366 CornerRadius::new(top_left, top_right, bottom_right, bottom_left)
367 }
368
369 fn from(corner_radius: PxCornerRadius) -> CornerRadius {
371 CornerRadius::new(
372 corner_radius.top_left,
373 corner_radius.top_right,
374 corner_radius.bottom_right,
375 corner_radius.bottom_left,
376 )
377 }
378}
379
380#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
382pub struct BorderSides {
383 pub left: BorderSide,
385 pub right: BorderSide,
387
388 pub top: BorderSide,
390 pub bottom: BorderSide,
392}
393impl fmt::Debug for BorderSides {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 if f.alternate() {
396 f.debug_struct("BorderSides")
397 .field("left", &self.left)
398 .field("right", &self.right)
399 .field("top", &self.top)
400 .field("bottom", &self.bottom)
401 .finish()
402 } else if self.all_eq() {
403 write!(f, "{:?}", self.top)
404 } else if self.dimensions_eq() {
405 write!(f, "({:?}, {:?})", self.top, self.left)
406 } else {
407 write!(f, "({:?}, {:?}, {:?}, {:?})", self.top, self.right, self.bottom, self.left)
408 }
409 }
410}
411impl BorderSides {
412 pub fn new_all<S: Into<BorderSide>>(side: S) -> Self {
414 let side = side.into();
415 BorderSides {
416 left: side,
417 right: side,
418 top: side,
419 bottom: side,
420 }
421 }
422
423 pub fn new_vh<TB: Into<BorderSide>, LR: Into<BorderSide>>(top_bottom: TB, left_right: LR) -> Self {
425 let top_bottom = top_bottom.into();
426 let left_right = left_right.into();
427 BorderSides {
428 left: left_right,
429 right: left_right,
430 top: top_bottom,
431 bottom: top_bottom,
432 }
433 }
434
435 pub fn new<T: Into<BorderSide>, R: Into<BorderSide>, B: Into<BorderSide>, L: Into<BorderSide>>(
437 top: T,
438 right: R,
439 bottom: B,
440 left: L,
441 ) -> Self {
442 BorderSides {
443 left: left.into(),
444 right: right.into(),
445 top: top.into(),
446 bottom: bottom.into(),
447 }
448 }
449
450 pub fn new_top<T: Into<BorderSide>>(top: T) -> Self {
452 Self::new(top, BorderSide::hidden(), BorderSide::hidden(), BorderSide::hidden())
453 }
454
455 pub fn new_right<R: Into<BorderSide>>(right: R) -> Self {
457 Self::new(BorderSide::hidden(), right, BorderSide::hidden(), BorderSide::hidden())
458 }
459
460 pub fn new_bottom<B: Into<BorderSide>>(bottom: B) -> Self {
462 Self::new(BorderSide::hidden(), BorderSide::hidden(), bottom, BorderSide::hidden())
463 }
464
465 pub fn new_left<L: Into<BorderSide>>(left: L) -> Self {
467 Self::new(BorderSide::hidden(), BorderSide::hidden(), BorderSide::hidden(), left)
468 }
469
470 pub fn solid<C: Into<Rgba>>(color: C) -> Self {
472 Self::new_all(BorderSide::solid(color))
473 }
474 pub fn double<C: Into<Rgba>>(color: C) -> Self {
476 Self::new_all(BorderSide::double(color))
477 }
478
479 pub fn dotted<C: Into<Rgba>>(color: C) -> Self {
481 Self::new_all(BorderSide::dotted(color))
482 }
483 pub fn dashed<C: Into<Rgba>>(color: C) -> Self {
485 Self::new_all(BorderSide::dashed(color))
486 }
487
488 pub fn groove<C: Into<Rgba>>(color: C) -> Self {
490 Self::new_all(BorderSide::groove(color))
491 }
492 pub fn ridge<C: Into<Rgba>>(color: C) -> Self {
494 Self::new_all(BorderSide::ridge(color))
495 }
496
497 pub fn inset<C: Into<Rgba>>(color: C) -> Self {
499 Self::new_all(BorderSide::inset(color))
500 }
501 pub fn outset<C: Into<Rgba>>(color: C) -> Self {
503 Self::new_all(BorderSide::outset(color))
504 }
505
506 pub fn hidden() -> Self {
508 Self::new_all(BorderSide::hidden())
509 }
510
511 pub fn all_eq(&self) -> bool {
513 self.top == self.bottom && self.top == self.left && self.top == self.right
514 }
515
516 pub fn dimensions_eq(&self) -> bool {
518 self.top == self.bottom && self.left == self.right
519 }
520}
521impl Default for BorderSides {
522 fn default() -> Self {
524 Self::hidden()
525 }
526}
527
528impl_from_and_into_var! {
529 fn from(color: Rgba) -> BorderSide {
531 BorderSide::solid(color)
532 }
533 fn from(color: Hsva) -> BorderSide {
535 BorderSide::solid(color)
536 }
537 fn from(color: Hsla) -> BorderSide {
539 BorderSide::solid(color)
540 }
541 fn from(color: Rgba) -> BorderSides {
543 BorderSides::new_all(color)
544 }
545 fn from(color: Hsva) -> BorderSides {
547 BorderSides::new_all(color)
548 }
549 fn from(color: Hsla) -> BorderSides {
551 BorderSides::new_all(color)
552 }
553
554 fn from(style: BorderStyle) -> BorderSide {
558 BorderSide::new(colors::BLACK.transparent(), style)
559 }
560 fn from(style: BorderStyle) -> BorderSides {
564 BorderSides::new_all(style)
565 }
566
567 fn from<C: Into<Rgba>, S: Into<BorderStyle>>((color, style): (C, S)) -> BorderSide {
569 BorderSide::new(color, style)
570 }
571
572 fn from<C: Into<Rgba>, S: Into<BorderStyle>>((color, style): (C, S)) -> BorderSides {
574 BorderSides::new_all(BorderSide::new(color, style))
575 }
576
577 fn from<T: Into<BorderSide>, R: Into<BorderSide>, B: Into<BorderSide>, L: Into<BorderSide>>(
579 (top, right, bottom, left): (T, R, B, L),
580 ) -> BorderSides {
581 BorderSides::new(top, right, bottom, left)
582 }
583
584 fn from<TB: Into<Rgba>, LR: Into<Rgba>, S: Into<BorderStyle>>((top_bottom, left_right, style): (TB, LR, S)) -> BorderSides {
586 let style = style.into();
587 BorderSides::new_vh((top_bottom, style), (left_right, style))
588 }
589
590 fn from<T: Into<Rgba>, R: Into<Rgba>, B: Into<Rgba>, L: Into<Rgba>, S: Into<BorderStyle>>(
592 (top, right, bottom, left, style): (T, R, B, L, S),
593 ) -> BorderSides {
594 let style = style.into();
595 BorderSides::new((top, style), (right, style), (bottom, style), (left, style))
596 }
597}
598
599#[derive(Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
605pub enum CornerRadiusFit {
606 None,
608 Widget,
612 #[default]
618 Tree,
619}
620impl fmt::Debug for CornerRadiusFit {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 if f.alternate() {
623 write!(f, "CornerRadiusFit::")?;
624 }
625 match self {
626 Self::None => write!(f, "None"),
627 Self::Widget => write!(f, "Widget"),
628 Self::Tree => write!(f, "Tree"),
629 }
630 }
631}
632
633context_var! {
634 pub static BORDER_ALIGN_VAR: FactorSideOffsets = FactorSideOffsets::zero();
636
637 pub static BORDER_OVER_VAR: bool = true;
639
640 pub static CORNER_RADIUS_VAR: CornerRadius = CornerRadius::zero();
642
643 pub static CORNER_RADIUS_FIT_VAR: CornerRadiusFit = CornerRadiusFit::default();
645}
646
647pub struct BORDER;
649impl BORDER {
650 pub fn border_offsets(&self) -> PxSideOffsets {
654 let data = BORDER_DATA.get();
655 if data.widget_id == WIDGET.try_id() {
656 data.wgt_offsets
657 } else {
658 PxSideOffsets::zero()
659 }
660 }
661
662 pub fn inner_offsets(&self) -> PxSideOffsets {
664 let data = BORDER_DATA.get();
665 if data.widget_id == WIDGET.try_id() {
666 data.wgt_inner_offsets
667 } else {
668 PxSideOffsets::zero()
669 }
670 }
671
672 pub fn border_radius(&self) -> PxCornerRadius {
676 match CORNER_RADIUS_FIT_VAR.get() {
677 CornerRadiusFit::Tree => BORDER_DATA.get().border_radius(),
678 CornerRadiusFit::Widget => {
679 let data = BORDER_DATA.get();
680 if data.widget_id == Some(WIDGET.id()) {
681 data.border_radius()
682 } else {
683 CORNER_RADIUS_VAR.layout()
684 }
685 }
686 _ => CORNER_RADIUS_VAR.layout(),
687 }
688 }
689
690 pub fn inner_radius(&self) -> PxCornerRadius {
692 match CORNER_RADIUS_FIT_VAR.get() {
693 CornerRadiusFit::Tree => BORDER_DATA.get().inner_radius(),
694 CornerRadiusFit::Widget => {
695 let data = BORDER_DATA.get();
696 if data.widget_id == WIDGET.try_id() {
697 data.inner_radius()
698 } else {
699 CORNER_RADIUS_VAR.layout()
700 }
701 }
702 _ => CORNER_RADIUS_VAR.layout(),
703 }
704 }
705
706 pub fn outer_radius(&self) -> PxCornerRadius {
708 BORDER_DATA.get().corner_radius
709 }
710
711 pub fn fill_bounds(&self) -> (PxRect, PxCornerRadius) {
717 let align = BORDER_ALIGN_VAR.get();
718
719 let fill_size = LAYOUT.constraints().fill_size();
720 let inner_offsets = self.inner_offsets();
721
722 if align == FactorSideOffsets::zero() {
723 let fill_size = PxSize::new(
724 fill_size.width + inner_offsets.horizontal(),
725 fill_size.height + inner_offsets.vertical(),
726 );
727 return (PxRect::from_size(fill_size), self.outer_radius());
728 } else if align == FactorSideOffsets::new_all(1.0.fct()) {
729 return (
730 PxRect::new(PxPoint::new(inner_offsets.left, inner_offsets.top), fill_size),
731 self.inner_radius(),
732 );
733 }
734
735 let outer = self.outer_radius();
736 let inner = self.inner_radius();
737
738 let b_align = FactorSideOffsets {
739 top: 1.0.fct() - align.top,
740 right: 1.0.fct() - align.right,
741 bottom: 1.0.fct() - align.bottom,
742 left: 1.0.fct() - align.left,
743 };
744 let bounds = PxRect {
745 origin: PxPoint::new(inner_offsets.left * (align.left), inner_offsets.top * align.top),
746 size: PxSize::new(
747 fill_size.width + inner_offsets.left * b_align.left + inner_offsets.right * b_align.right,
748 fill_size.height + inner_offsets.top * b_align.top + inner_offsets.bottom * b_align.bottom,
749 ),
750 };
751
752 let radius = PxCornerRadius {
753 top_left: PxSize::new(
754 outer.top_left.width.lerp(&inner.top_left.width, align.left),
755 outer.top_left.height.lerp(&inner.top_left.height, align.top),
756 ),
757 top_right: PxSize::new(
758 outer.top_right.width.lerp(&inner.top_right.width, align.right),
759 outer.top_right.height.lerp(&inner.top_right.height, align.top),
760 ),
761 bottom_left: PxSize::new(
762 outer.bottom_left.width.lerp(&inner.bottom_left.width, align.left),
763 outer.bottom_left.height.lerp(&inner.bottom_left.height, align.bottom),
764 ),
765 bottom_right: PxSize::new(
766 outer.bottom_right.width.lerp(&inner.bottom_right.width, align.right),
767 outer.bottom_right.height.lerp(&inner.bottom_right.height, align.bottom),
768 ),
769 };
770
771 (bounds, radius)
772 }
773
774 pub(super) fn with_inner(&self, f: impl FnOnce() -> PxSize) -> PxSize {
775 let mut data = BORDER_DATA.get_clone();
776 let border = WIDGET.border();
777 data.add_inner(&border);
778
779 BORDER_DATA.with_context(&mut Some(Arc::new(data)), || {
780 let corner_radius = BORDER.border_radius();
781 border.set_corner_radius(corner_radius);
782 border.set_offsets(PxSideOffsets::zero());
783 f()
784 })
785 }
786
787 pub fn measure_border(&self, offsets: PxSideOffsets, f: impl FnOnce() -> PxSize) -> PxSize {
789 let mut data = BORDER_DATA.get_clone();
790 data.add_offset(None, offsets);
791 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f)
792 }
793
794 pub fn layout_border(&self, offsets: PxSideOffsets, f: impl FnOnce()) {
796 let mut data = BORDER_DATA.get_clone();
797 data.add_offset(Some(&WIDGET.border()), offsets);
798 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f);
799 }
800
801 pub fn with_corner_radius<R>(&self, f: impl FnOnce() -> R) -> R {
809 let mut data = BORDER_DATA.get_clone();
810 data.set_corner_radius();
811 BORDER_DATA.with_context(&mut Some(Arc::new(data)), f)
812 }
813
814 pub fn border_layout(&self) -> (PxRect, PxSideOffsets) {
818 BORDER_LAYOUT.get().unwrap_or_else(|| {
819 #[cfg(debug_assertions)]
820 tracing::error!("the `border_layout` is only available inside the layout and render methods of the border visual node");
821 (PxRect::zero(), PxSideOffsets::zero())
822 })
823 }
824
825 pub fn with_border_layout(&self, rect: PxRect, offsets: PxSideOffsets, f: impl FnOnce()) {
827 BORDER_LAYOUT.with_context(&mut Some(Arc::new(Some((rect, offsets)))), f)
828 }
829}
830
831context_local! {
832 static BORDER_DATA: BorderOffsetsData = BorderOffsetsData::default();
833 static BORDER_LAYOUT: Option<(PxRect, PxSideOffsets)> = None;
834}
835
836#[derive(Debug, Clone, Default)]
837struct BorderOffsetsData {
838 widget_id: Option<WidgetId>,
839 wgt_offsets: PxSideOffsets,
840 wgt_inner_offsets: PxSideOffsets,
841
842 eval_cr: bool,
843 corner_radius: PxCornerRadius,
844 cr_offsets: PxSideOffsets,
845 cr_inner_offsets: PxSideOffsets,
846}
847impl BorderOffsetsData {
848 fn add_offset(&mut self, layout_info: Option<&WidgetBorderInfo>, offset: PxSideOffsets) {
852 let widget_id = Some(WIDGET.id());
853 let is_wgt_start = self.widget_id != widget_id;
854 if is_wgt_start {
855 self.widget_id = widget_id;
857 self.wgt_offsets = PxSideOffsets::zero();
858 self.wgt_inner_offsets = PxSideOffsets::zero();
859 self.eval_cr |= layout_info.is_some() && matches!(CORNER_RADIUS_FIT_VAR.get(), CornerRadiusFit::Widget);
860 }
861 self.wgt_offsets = self.wgt_inner_offsets;
862 self.wgt_inner_offsets += offset;
863
864 if mem::take(&mut self.eval_cr) {
865 self.corner_radius = CORNER_RADIUS_VAR.layout();
866 self.cr_offsets = PxSideOffsets::zero();
867 self.cr_inner_offsets = PxSideOffsets::zero();
868 }
869 self.cr_offsets = self.cr_inner_offsets;
870 self.cr_inner_offsets += offset;
871
872 if let Some(border) = layout_info {
873 if is_wgt_start {
874 border.set_corner_radius(self.corner_radius);
875 }
876 border.set_offsets(self.wgt_inner_offsets);
877 }
878 }
879
880 fn add_inner(&mut self, layout_info: &WidgetBorderInfo) {
881 self.add_offset(Some(layout_info), PxSideOffsets::zero());
883 }
884
885 fn set_corner_radius(&mut self) {
886 self.eval_cr = matches!(CORNER_RADIUS_FIT_VAR.get(), CornerRadiusFit::Tree);
887 }
888
889 fn border_radius(&self) -> PxCornerRadius {
890 self.corner_radius.deflate(self.cr_offsets)
891 }
892
893 fn inner_radius(&self) -> PxCornerRadius {
894 self.corner_radius.deflate(self.cr_inner_offsets)
895 }
896}