35 private const string SubRootName =
"sub-root";
36 private const int MillisecondsPerSecond = 1000;
37 private const int PanelReadyRefreshDelayMs = 50;
38 private const int MinOffsetApplyAttempts = 1;
39 private const int MaxOffsetApplyAttempts = 10;
40 private const float MinApplyOffsetDelaySeconds = 0f;
41 private const float MaxApplyOffsetDelaySeconds = 1f;
43#if UNITY_6000_4_OR_NEWER
44 private static readonly Dictionary<int, Dictionary<BannerAnchor, EntityId>> sideLocksByContainer =
new();
46 private static readonly Dictionary<int, Dictionary<BannerAnchor, int>> sideLocksByContainer =
new();
51 #region Serialized fields
54 [Tooltip(
"UIDocument that hosts UI Toolkit content. If null, it is searched on this GameObject.")]
55#if UNITY_EDITOR && UNITY_6000_5_OR_NEWER
56 [Editor.ShowIf(nameof(HasPanelRenderer), invert:
true)]
58 [SerializeField]
private UIDocument uiDocument;
60#if UNITY_6000_5_OR_NEWER
62 [Tooltip(
"PanelRenderer that hosts UI Toolkit content when no UIDocument is available.")]
64 [Editor.ShowIf(nameof(HasUIDocument), invert:
true)]
66 [SerializeField]
private PanelRenderer panelRenderer;
70 [Tooltip(
"Banner ad component implementing IBannerAd.")]
74 [Tooltip(
"Optional banner ad controller that keeps a Banner reference available, including when the Banner persists between scenes.")]
78 [Tooltip(
"Optional name of the target container in the visual tree.")]
79 [SerializeField]
private string explicitContainerName;
82 [Tooltip(
"Controls whether banner-side offsets are written as margin or padding.")]
86 [Tooltip(
"Delay in seconds before applying offset after a visible banner refresh.")]
87 [Range(MinApplyOffsetDelaySeconds, MaxApplyOffsetDelaySeconds)]
88 [SerializeField]
private float applyOffsetDelaySeconds = MinApplyOffsetDelaySeconds;
91 [Tooltip(
"Number of offset applications performed after a banner becomes visible.")]
92 [Range(MinOffsetApplyAttempts, MaxOffsetApplyAttempts)]
93 [SerializeField]
private int offsetApplyAttempts = 3;
96 [Tooltip(
"Extra margin applied when the banner is anchored to the top.")]
97 [SerializeField]
private float marginTop;
100 [Tooltip(
"Extra margin applied when the banner is anchored to the bottom.")]
101 [SerializeField]
private float marginBottom;
104 [Tooltip(
"Extra margin applied when the banner is anchored to the left.")]
105 [SerializeField]
private float marginLeft;
108 [Tooltip(
"Extra margin applied when the banner is anchored to the right.")]
109 [SerializeField]
private float marginRight;
112 [Tooltip(
"When true, only the banner side is inset. When false, full shift is allowed if the opposite side is free.")]
113 [SerializeField]
private bool shiftOnlyBannerSide =
true;
116 [Tooltip(
"When true, safe-area inset on the banner side is subtracted from the applied banner offset.")]
117 [SerializeField]
private bool accountForSafeArea =
true;
121 #region Runtime state
124 private volatile bool isComponentActive;
127 private VisualElement targetContainer;
130 private (
float top,
float bottom,
float left,
float right) appliedInsets;
131 private bool hasAppliedInsets;
132 private Vector2 appliedTranslateOffset;
133 private bool hasAppliedTranslateOffset;
134 private bool hasInitialState;
135 private bool isSubscribed;
136 private int applyOffsetRequestVersion;
137#if UNITY_6000_5_OR_NEWER
138 private VisualElement currentRoot;
139 private PanelRenderer registeredPanelRenderer;
140 private int lastPanelReloadVersion = -1;
145#if UNITY_EDITOR && UNITY_6000_5_OR_NEWER
148 private bool HasUIDocument => uiDocument !=
null;
150 private bool HasPanelRenderer => panelRenderer !=
null;
155 #region Unity lifecycle
163 protected virtual void OnValidate()
165 if (Application.isPlaying)
return;
167 if (uiDocument ==
null)
168 TryGetComponent(out uiDocument);
169#if UNITY_6000_5_OR_NEWER
170 if (uiDocument ==
null && panelRenderer ==
null)
171 TryGetComponent(out panelRenderer);
174 bannerAdComponent = bannerAdComponent ? bannerAdComponent : FindBannerAdCandidate();
176 if (bannerAdComponent ==
null || bannerAdComponent is
IBannerAd)
return;
179 $
"{nameof(BannerToolkitOffsetHelper)}: Assigned banner component must implement {nameof(IBannerAd)}.",
181 bannerAdComponent =
null;
192 if (bannerAdController !=
null && bannerAdController.Ad is
IBannerAd)
193 return bannerAdController.Ad;
195#if UNITY_6000_3_OR_NEWER
196 var behaviours = FindObjectsByType<AdUnitBehaviourBase>(FindObjectsInactive.Exclude);
197#elif UNITY_2023_1_OR_NEWER
198 var behaviours = FindObjectsByType<AdUnitBehaviourBase>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
200 var behaviours = FindObjectsOfType<AdUnitBehaviourBase>();
210 protected virtual void Reset()
212 if (Application.isPlaying)
return;
214 if (uiDocument ==
null)
215 TryGetComponent(out uiDocument);
217#if UNITY_6000_5_OR_NEWER
218 if (uiDocument ==
null && panelRenderer ==
null)
219 TryGetComponent(out panelRenderer);
222 if (uiDocument !=
null)
225#if UNITY_6000_5_OR_NEWER
226 if (panelRenderer !=
null)
230#if UNITY_6000_3_OR_NEWER
231 uiDocument = FindAnyObjectByType<UIDocument>();
232#elif UNITY_2023_1_OR_NEWER
233 uiDocument = FindFirstObjectByType<UIDocument>();
235 uiDocument = FindObjectOfType<UIDocument>();
243 protected virtual void Awake() => isComponentActive = isActiveAndEnabled;
252 isComponentActive =
true;
253 InvalidatePendingApplyOffset();
254 ClearAppliedOffsetState();
256#if UNITY_6000_5_OR_NEWER
260 ClearRuntimeReferences();
262#if UNITY_6000_5_OR_NEWER
263 if (uiDocument !=
null || currentRoot !=
null)
265 if (ResolveRuntimeReferences())
268 else if (registeredPanelRenderer ==
null)
270 ResolveRuntimeReferences();
273 if (ResolveRuntimeReferences())
277 SchedulePanelReadyRefresh();
294 isComponentActive =
false;
295 InvalidatePendingApplyOffset();
297#if UNITY_6000_5_OR_NEWER
298 UnregisterPanelRenderer();
301 ClearRuntimeReferences();
314 if (!isComponentActive)
317#if UNITY_6000_5_OR_NEWER
321 ClearRuntimeReferences();
323#if UNITY_6000_5_OR_NEWER
324 if (uiDocument ==
null && registeredPanelRenderer !=
null && currentRoot ==
null)
326 RequestPanelRendererUpdate();
327 SchedulePanelReadyRefresh();
331 if (!ResolveRuntimeReferences())
345 if (!isComponentActive)
348 if (!EnsureRuntimeReferences())
353 case { IsVisible:
true }:
356 case { IsVisible:
false }:
364 #region Event handlers
389 private void SchedulePanelReadyRefresh()
391 var root = ResolveCurrentRoot();
392 root?.schedule.Execute(RefreshAfterPanelReady).StartingIn(PanelReadyRefreshDelayMs);
400 private void RefreshAfterPanelReady()
402 if (!isComponentActive)
414 private bool EnsureRuntimeReferences()
416#if UNITY_6000_5_OR_NEWER
417 if (uiDocument ==
null && currentRoot ==
null)
420 if (!hasInitialState || bannerAd ==
null || targetContainer ==
null)
422 if (!ResolveRuntimeReferences())
437 private bool ResolveRuntimeReferences()
439 if (uiDocument ==
null)
440 TryGetComponent(out uiDocument);
442#if UNITY_6000_5_OR_NEWER
443 if (uiDocument ==
null && registeredPanelRenderer ==
null)
445 Debug.LogWarning($
"{nameof(BannerToolkitOffsetHelper)}: UIDocument or PanelRenderer not found.",
this);
449 if (uiDocument ==
null)
451 Debug.LogWarning($
"{nameof(BannerToolkitOffsetHelper)}: UIDocument not found.",
this);
456 var root = ResolveCurrentRoot();
459 Debug.LogWarning($
"{nameof(BannerToolkitOffsetHelper)}: UI Toolkit root is not available.",
this);
463 bannerAd = ResolveBannerAd();
464 if (bannerAd ==
null)
467 $
"{nameof(BannerToolkitOffsetHelper)}: Banner component implementing {nameof(IBannerAd)} not found.",
472 targetContainer = ResolveTargetContainer(root);
473 if (targetContainer ==
null)
476 hasInitialState =
true;
485 private VisualElement ResolveCurrentRoot() =>
486 uiDocument !=
null ? uiDocument.rootVisualElement :
487#if UNITY_6000_5_OR_NEWER
488 registeredPanelRenderer !=
null ? currentRoot :
492#if UNITY_6000_5_OR_NEWER
493 private void SynchronizeSource()
495 var resolvedDocument = uiDocument;
496 if (resolvedDocument ==
null)
497 TryGetComponent(out resolvedDocument);
499 var resolvedRenderer = resolvedDocument ==
null ? panelRenderer :
null;
500 if (resolvedDocument ==
null && resolvedRenderer ==
null)
501 TryGetComponent(out resolvedRenderer);
503 var sourceChanged = uiDocument != resolvedDocument || registeredPanelRenderer != resolvedRenderer;
508 ClearAppliedOffsetState();
509 targetContainer =
null;
511 hasInitialState =
false;
512 lastPanelReloadVersion = -1;
515 uiDocument = resolvedDocument;
517 if (uiDocument !=
null)
519 UnregisterPanelRenderer();
520 currentRoot = uiDocument.rootVisualElement;
524 if (registeredPanelRenderer == resolvedRenderer)
527 UnregisterPanelRenderer();
528 registeredPanelRenderer = resolvedRenderer;
529 if (registeredPanelRenderer ==
null)
return;
530 registeredPanelRenderer.RegisterUIReloadCallback(OnUIReload);
531 ((IPanelComponent)registeredPanelRenderer).PerformUpdate();
534 private void UnregisterPanelRenderer()
536 if (registeredPanelRenderer ==
null)
539 registeredPanelRenderer.UnregisterUIReloadCallback(OnUIReload);
540 registeredPanelRenderer =
null;
544 private void RequestPanelRendererUpdate()
546 var renderer = registeredPanelRenderer;
547 if (renderer ==
null)
550 ((IPanelComponent)renderer).PerformUpdate();
551 renderer.UnregisterUIReloadCallback(OnUIReload);
552 renderer.RegisterUIReloadCallback(OnUIReload);
561 private void OnUIReload(PanelRenderer renderer, VisualElement root,
int version)
563 if (!isComponentActive)
567 if (uiDocument !=
null)
569 if (!ResolveRuntimeReferences())
return;
576 if (renderer != registeredPanelRenderer)
579 if (root !=
null && version == lastPanelReloadVersion && currentRoot !=
null)
584 targetContainer =
null;
585 hasInitialState =
false;
590 lastPanelReloadVersion = version;
592 if (!ResolveRuntimeReferences())
606 private IBannerAd ResolveBannerAd()
608 if (bannerAdComponent !=
null)
610 if (bannerAdComponent is IBannerAd directBanner)
614 $
"{nameof(BannerToolkitOffsetHelper)}: Assigned banner component must implement {nameof(IBannerAd)}.",
619 if (bannerAdController ==
null || bannerAdController.Ad is not IBannerAd controllerBanner)
622 bannerAdComponent = bannerAdController.Ad;
623 return controllerBanner;
633 private VisualElement ResolveTargetContainer(VisualElement root)
635 if (
string.IsNullOrWhiteSpace(explicitContainerName))
636 return EnsureSubRoot(root);
638 var explicitContainer = root.Q<VisualElement>(explicitContainerName);
639 if (explicitContainer !=
null)
640 return explicitContainer;
643 $
"{nameof(BannerToolkitOffsetHelper)}: Container '{explicitContainerName}' not found. " +
644 $
"Falling back to '{SubRootName}'.",
647 return EnsureSubRoot(root);
657 private static VisualElement EnsureSubRoot(VisualElement root)
659 VisualElement subRoot =
null;
660 for (var index = 0; index < root.childCount; index++)
662 var child = root[index];
663 if (child.name != SubRootName)
672 subRoot =
new VisualElement { name = SubRootName };
673 ConfigureSubRoot(subRoot);
677 var children =
new List<VisualElement>(root.childCount);
678 for (var index = 0; index < root.childCount; index++)
680 var child = root[index];
681 if (ReferenceEquals(child, subRoot))
687 foreach (var child
in children)
689 child.RemoveFromHierarchy();
702 private static void ConfigureSubRoot(VisualElement subRoot)
704 subRoot.style.flexGrow = 1f;
705 subRoot.style.width =
new Length(100f, LengthUnit.Percent);
706 subRoot.style.height =
new Length(100f, LengthUnit.Percent);
714 private void ClearRuntimeReferences()
717 targetContainer =
null;
718#if UNITY_6000_5_OR_NEWER
721 ClearAppliedOffsetState();
722 hasInitialState =
false;
727 #region Event subscription
734 private void Subscribe()
736 if (isSubscribed || bannerAd ==
null)
739 bannerAd.OnShown += OnBannerShown;
740 bannerAd.OnHide += OnBannerHidden;
749 private void Unsubscribe()
754 if (bannerAd !=
null)
756 bannerAd.OnShown -= OnBannerShown;
757 bannerAd.OnHide -= OnBannerHidden;
760 isSubscribed =
false;
772 private void ApplyOffset()
774 var requestVersion = ++applyOffsetRequestVersion;
775 var delayMilliseconds = GetApplyOffsetDelayMilliseconds();
776 if (delayMilliseconds <= 0)
778 ApplyOffsetAttempt(requestVersion, 1);
782 ScheduleOffsetAttempt(requestVersion, 1, delayMilliseconds);
791 private void ScheduleOffsetAttempt(
int requestVersion,
int attempt,
long delayMilliseconds)
793 var root = ResolveCurrentRoot();
796 ApplyOffsetAttempt(requestVersion, attempt);
800 root.schedule.Execute(() => ApplyOffsetAttempt(requestVersion, attempt)).StartingIn(delayMilliseconds);
808 private void ApplyOffsetAttempt(
int requestVersion,
int attempt)
810 ApplyOffsetNow(requestVersion);
812 if (requestVersion != applyOffsetRequestVersion || !isComponentActive || bannerAd is not { IsVisible:
true } ||
813 attempt >= Mathf.Clamp(offsetApplyAttempts, MinOffsetApplyAttempts, MaxOffsetApplyAttempts))
816 ScheduleOffsetAttempt(requestVersion, attempt + 1, 0);
825 private void ApplyOffsetNow(
int requestVersion)
827 if (requestVersion != applyOffsetRequestVersion)
830 if (!isComponentActive || !hasInitialState || targetContainer ==
null || bannerAd ==
null)
833 if (!bannerAd.IsVisible)
836 if (shiftOnlyBannerSide)
838 ApplySingleSideInset();
842 ApplyFullShiftOffset();
851 private long GetApplyOffsetDelayMilliseconds() =>
852 Mathf.RoundToInt(Mathf.Clamp(
853 applyOffsetDelaySeconds,
854 MinApplyOffsetDelaySeconds,
855 MaxApplyOffsetDelaySeconds) * MillisecondsPerSecond);
862 private void InvalidatePendingApplyOffset() => applyOffsetRequestVersion++;
869 private void ResetOffset()
871 InvalidatePendingApplyOffset();
873 if (!hasInitialState || targetContainer ==
null)
876 RemoveAppliedLayoutOffset();
877 RemoveAppliedTranslateOffset();
878 ReleaseSideLock(appliedAnchor);
879 ClearAppliedOffsetState();
887 private void ApplySingleSideInset()
889 RemoveAppliedLayoutOffset();
890 RemoveAppliedTranslateOffset();
891 ReleaseSideLock(appliedAnchor);
893 var insets = GetInsetValues();
894 if (IsZeroInsets(insets))
896 ClearAppliedOffsetState();
900 var side = GetInsetSide(insets);
901 if (!TryAcquireSideLock(side))
903 ClearAppliedOffsetState();
907 var currentOffsets = GetCurrentLayoutOffsetValues(layoutOffsetMode);
908 ApplyLayoutOffsetValues(AddInsets(currentOffsets, insets), layoutOffsetMode);
910 appliedAnchor = side;
911 appliedLayoutOffsetMode = layoutOffsetMode;
912 appliedInsets = insets;
913 hasAppliedInsets =
true;
914 appliedTranslateOffset = Vector2.zero;
915 hasAppliedTranslateOffset =
false;
923 private void ApplyFullShiftOffset()
925 RemoveAppliedLayoutOffset();
926 RemoveAppliedTranslateOffset();
927 ReleaseSideLock(appliedAnchor);
929 var offset = GetShiftOffsetVector();
930 if (IsZeroVector(offset))
932 ClearAppliedOffsetState();
936 var side = GetShiftLockSide();
937 if (!TryAcquireSideLock(side))
939 ClearAppliedOffsetState();
943 var currentTranslate = GetCurrentTranslateValues();
944 ApplyTranslateValues(currentTranslate + offset);
946 appliedAnchor = side;
947 appliedInsets = (0f, 0f, 0f, 0f);
948 hasAppliedInsets =
false;
949 appliedTranslateOffset = offset;
950 hasAppliedTranslateOffset =
true;
958 private void RemoveAppliedLayoutOffset()
960 if (!hasAppliedInsets || targetContainer ==
null)
963 var currentOffsets = GetCurrentLayoutOffsetValues(appliedLayoutOffsetMode);
964 ApplyLayoutOffsetValues(SubtractInsets(currentOffsets, appliedInsets), appliedLayoutOffsetMode);
966 appliedInsets = (0f, 0f, 0f, 0f);
967 hasAppliedInsets =
false;
975 private void RemoveAppliedTranslateOffset()
977 if (!hasAppliedTranslateOffset || targetContainer ==
null)
980 var currentTranslate = GetCurrentTranslateValues();
981 ApplyTranslateValues(currentTranslate - appliedTranslateOffset);
983 appliedTranslateOffset = Vector2.zero;
984 hasAppliedTranslateOffset =
false;
993 private (
float top,
float bottom,
float left,
float right) GetInsetValues()
995 if (bannerAd ==
null)
996 return (0f, 0f, 0f, 0f);
998 var anchor = bannerAd.GetBannerAnchor();
999 var effectiveExtent = GetEffectiveBannerExtent(anchor);
1001 return anchor
switch
1003 BannerAnchor.Top => (effectiveExtent, 0f, 0f, 0f),
1005 BannerAnchor.Left => (0f, 0f, effectiveExtent, 0f),
1007 _ => (0f, 0f, 0f, 0f)
1017 private Vector2 GetShiftOffsetVector()
1019 if (bannerAd ==
null)
1020 return Vector2.zero;
1022 var anchor = bannerAd.GetBannerAnchor();
1023 var effectiveExtent = GetEffectiveBannerExtent(anchor);
1025 return anchor
switch
1027 BannerAnchor.Top =>
new Vector2(0f, effectiveExtent),
1028 BannerAnchor.Bottom =>
new Vector2(0f, -effectiveExtent),
1029 BannerAnchor.Left =>
new Vector2(effectiveExtent, 0f),
1030 BannerAnchor.Right =>
new Vector2(-effectiveExtent, 0f),
1042 private float GetEffectiveBannerExtent(
BannerAnchor anchor)
1044 var resolvedBannerExtent = GetResolvedBannerExtent(anchor);
1045 var margin = GetMarginForAnchor(anchor);
1046 if (!accountForSafeArea)
1047 return resolvedBannerExtent + margin;
1049 return Mathf.Max(0f, resolvedBannerExtent - GetSafeAreaInsetForAnchor(anchor)) + margin;
1059 private float GetResolvedBannerExtent(
BannerAnchor anchor)
1061 if (bannerAd ==
null)
1064 var resolvedHeight = Mathf.Max(0f, bannerAd.GetBannerHeightPixels());
1065 var resolvedWidth = Mathf.Max(0f, bannerAd.GetBannerWidthPixels());
1066 if (resolvedWidth <= 0f)
1067 resolvedWidth = Screen.width;
1069 return anchor
switch
1071 BannerAnchor.Top or BannerAnchor.Bottom => resolvedHeight,
1072 BannerAnchor.Left or BannerAnchor.Right => resolvedWidth,
1084 private float GetMarginForAnchor(
BannerAnchor anchor) =>
1087 BannerAnchor.Top => marginTop,
1088 BannerAnchor.Bottom => marginBottom,
1089 BannerAnchor.Left => marginLeft,
1090 BannerAnchor.Right => marginRight,
1101 private static float GetSafeAreaInsetForAnchor(
BannerAnchor anchor)
1103 var (top, bottom, left, right) = GetSafeAreaInsets();
1104 return anchor
switch
1106 BannerAnchor.Top => top,
1107 BannerAnchor.Bottom => bottom,
1108 BannerAnchor.Left => left,
1109 BannerAnchor.Right => right,
1120 private static (
float top,
float bottom,
float left,
float right) GetSafeAreaInsets()
1122 var safeArea = Screen.safeArea;
1124 top: Mathf.Max(0f, Screen.height - (safeArea.y + safeArea.height)),
1125 bottom: Mathf.Max(0f, safeArea.y),
1126 left: Mathf.Max(0f, safeArea.x),
1127 right: Mathf.Max(0f, Screen.width - (safeArea.x + safeArea.width)));
1138 if (bannerAd ==
null)
1141 return bannerAd.GetBannerAnchor()
switch
1147 _ => BannerAnchor.Custom
1159 mode == BannerToolkitLayoutOffsetMode.Margin
1160 ? GetCurrentMarginValues()
1161 : GetCurrentPaddingValues();
1170 private void ApplyLayoutOffsetValues(
1171 (
float top,
float bottom,
float left,
float right) offsetValues,
1176 ApplyMarginValues(offsetValues);
1180 ApplyPaddingValues(offsetValues);
1189 private (
float top,
float bottom,
float left,
float right) GetCurrentPaddingValues()
1191 var resolvedStyle = targetContainer.resolvedStyle;
1193 top: ResolveStyleLengthValue(targetContainer.style.paddingTop, resolvedStyle.paddingTop),
1194 bottom: ResolveStyleLengthValue(targetContainer.style.paddingBottom, resolvedStyle.paddingBottom),
1195 left: ResolveStyleLengthValue(targetContainer.style.paddingLeft, resolvedStyle.paddingLeft),
1196 right: ResolveStyleLengthValue(targetContainer.style.paddingRight, resolvedStyle.paddingRight));
1205 private void ApplyPaddingValues((
float top,
float bottom,
float left,
float right) paddingValues)
1207 targetContainer.style.paddingTop = paddingValues.top;
1208 targetContainer.style.paddingBottom = paddingValues.bottom;
1209 targetContainer.style.paddingLeft = paddingValues.left;
1210 targetContainer.style.paddingRight = paddingValues.right;
1219 private (
float top,
float bottom,
float left,
float right) GetCurrentMarginValues()
1221 var resolvedStyle = targetContainer.resolvedStyle;
1223 top: ResolveStyleLengthValue(targetContainer.style.marginTop, resolvedStyle.marginTop),
1224 bottom: ResolveStyleLengthValue(targetContainer.style.marginBottom, resolvedStyle.marginBottom),
1225 left: ResolveStyleLengthValue(targetContainer.style.marginLeft, resolvedStyle.marginLeft),
1226 right: ResolveStyleLengthValue(targetContainer.style.marginRight, resolvedStyle.marginRight));
1235 private void ApplyMarginValues((
float top,
float bottom,
float left,
float right) marginValues)
1237 targetContainer.style.marginTop = marginValues.top;
1238 targetContainer.style.marginBottom = marginValues.bottom;
1239 targetContainer.style.marginLeft = marginValues.left;
1240 targetContainer.style.marginRight = marginValues.right;
1249 private Vector2 GetCurrentTranslateValues()
1251 var resolvedTranslate = targetContainer.resolvedStyle.translate;
1252 var resolvedValues = (Vector2)resolvedTranslate;
1254 var styleTranslate = targetContainer.style.translate;
1255 if (styleTranslate.keyword is StyleKeyword.Null or StyleKeyword.Auto)
1256 return resolvedValues;
1258 var value = styleTranslate.value;
1259 var x = ResolveTranslateLength(value.x, resolvedValues.x);
1260 var y = ResolveTranslateLength(value.y, resolvedValues.y);
1261 return new Vector2(x, y);
1270 private void ApplyTranslateValues(Vector2 translateValues) =>
1271 targetContainer.style.translate =
new Translate(
1272 new Length(translateValues.x, LengthUnit.Pixel),
1273 new Length(translateValues.y, LengthUnit.Pixel),
1284 private static float ResolveStyleLengthValue(StyleLength styleLength,
float fallback) =>
1285 styleLength.keyword is StyleKeyword.Null or StyleKeyword.Auto
1287 : styleLength.value.unit == LengthUnit.Pixel
1288 ? styleLength.value.value
1299 private static float ResolveTranslateLength(Length length,
float fallback) =>
1300 length.unit == LengthUnit.Pixel ? length.value : fallback;
1310 private static (
float top,
float bottom,
float left,
float right) AddInsets(
1311 (
float top,
float bottom,
float left,
float right) left,
1312 (
float top,
float bottom,
float left,
float right) right) =>
1314 top: left.top + right.top,
1315 bottom: left.bottom + right.bottom,
1316 left: left.left + right.left,
1317 right: left.right + right.right);
1327 private static (
float top,
float bottom,
float left,
float right) SubtractInsets(
1328 (
float top,
float bottom,
float left,
float right) left,
1329 (
float top,
float bottom,
float left,
float right) right) =>
1331 top: left.top - right.top,
1332 bottom: left.bottom - right.bottom,
1333 left: left.left - right.left,
1334 right: left.right - right.right);
1343 private static bool IsZeroInsets((
float top,
float bottom,
float left,
float right) insets) =>
1344 Mathf.Approximately(insets.top, 0f) &&
1345 Mathf.Approximately(insets.bottom, 0f) &&
1346 Mathf.Approximately(insets.left, 0f) &&
1347 Mathf.Approximately(insets.right, 0f);
1356 private static bool IsZeroVector(Vector2 shiftOffset) =>
1357 Mathf.Approximately(shiftOffset.x, 0f) &&
1358 Mathf.Approximately(shiftOffset.y, 0f);
1365 private void ClearAppliedOffsetState()
1368 appliedLayoutOffsetMode = layoutOffsetMode;
1369 appliedInsets = (0f, 0f, 0f, 0f);
1370 hasAppliedInsets =
false;
1371 appliedTranslateOffset = Vector2.zero;
1372 hasAppliedTranslateOffset =
false;
1382 private static BannerAnchor GetInsetSide((
float top,
float bottom,
float left,
float right) insets) =>
1383 !Mathf.Approximately(insets.top, 0f) ? BannerAnchor.Top :
1384 !Mathf.Approximately(insets.bottom, 0f) ? BannerAnchor.Bottom :
1385 !Mathf.Approximately(insets.left, 0f) ? BannerAnchor.Left :
1386 !Mathf.Approximately(insets.right, 0f) ? BannerAnchor.Right :
BannerAnchor.Custom;
1397 side = NormalizeLockAnchor(side);
1398 if (side ==
BannerAnchor.Custom || targetContainer ==
null)
1401 var containerId = RuntimeHelpers.GetHashCode(targetContainer);
1402 if (!sideLocksByContainer.TryGetValue(containerId, out var sideLocks))
1404#if UNITY_6000_4_OR_NEWER
1405 sideLocks =
new Dictionary<BannerAnchor, EntityId>();
1407 sideLocks =
new Dictionary<BannerAnchor, int>();
1409 sideLocksByContainer[containerId] = sideLocks;
1412#if UNITY_6000_4_OR_NEWER
1413 var ownerId = GetEntityId();
1415 var ownerId = GetInstanceID();
1418 if (sideLocks.TryGetValue(side, out var lockedBy) && lockedBy != ownerId)
1421 sideLocks[side] = ownerId;
1433 side = NormalizeLockAnchor(side);
1434 if (side ==
BannerAnchor.Custom || targetContainer ==
null)
1437 var containerId = RuntimeHelpers.GetHashCode(targetContainer);
1438 if (!sideLocksByContainer.TryGetValue(containerId, out var sideLocks))
1441#if UNITY_6000_4_OR_NEWER
1442 var ownerId = GetEntityId();
1444 var ownerId = GetInstanceID();
1447 if (!sideLocks.TryGetValue(side, out var lockedBy) || lockedBy != ownerId)
1450 sideLocks.Remove(side);
1451 if (sideLocks.Count == 0)
1452 sideLocksByContainer.Remove(containerId);
1463 anchor is BannerAnchor.Center or BannerAnchor.Custom
1464 ? BannerAnchor.Custom