@nativescript/core 8.6.0-vision.2 → 8.6.0-vision.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/abortcontroller/index.d.ts +1 -1
- package/abortcontroller/index.js +4 -4
- package/abortcontroller/index.js.map +1 -1
- package/application/application-common.d.ts +21 -1
- package/application/application-common.js +23 -2
- package/application/application-common.js.map +1 -1
- package/application/application-shims.js.map +1 -1
- package/application/application.android.js +124 -59
- package/application/application.android.js.map +1 -1
- package/application/application.d.ts +8 -0
- package/application/application.ios.d.ts +2 -0
- package/application/application.ios.js +33 -2
- package/application/application.ios.js.map +1 -1
- package/core-types/index.d.ts +7 -0
- package/core-types/index.js +7 -0
- package/core-types/index.js.map +1 -1
- package/css/CSS3Parser.js.map +1 -1
- package/css/system-classes.d.ts +2 -2
- package/css/system-classes.js +4 -1
- package/css/system-classes.js.map +1 -1
- package/data/observable/index.js.map +1 -1
- package/debugger/InspectorBackendCommands.ios.js.map +1 -1
- package/debugger/webinspector-css.ios.js.map +1 -1
- package/debugger/webinspector-dom.ios.js.map +1 -1
- package/debugger/webinspector-network.ios.js.map +1 -1
- package/package.json +1 -1
- package/platforms/android/widgets-release.aar +0 -0
- package/platforms/ios/TNSWidgets.xcframework/Info.plist +18 -10
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/aarch64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-maccatalyst/TNSWidgets.framework/Resources/Info.plist +3 -3
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-maccatalyst/TNSWidgets.framework/Versions/A/Resources/Info.plist +3 -3
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-maccatalyst/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/aarch64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-maccatalyst/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/x86_64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-simulator/TNSWidgets.framework/_CodeSignature/CodeResources +1 -1
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-simulator/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/aarch64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/ios-arm64_x86_64-simulator/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/x86_64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/TNSWidgets.framework/Info.plist +0 -0
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/TNSWidgets.framework/TNSWidgets +0 -0
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/TNSWidgets.framework/_CodeSignature/CodeResources +1 -1
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets +0 -0
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/aarch64/TNSWidgets.yml +77 -77
- package/platforms/ios/TNSWidgets.xcframework/xros-arm64_x86_64-simulator/dSYMs/TNSWidgets.framework.dSYM/Contents/Resources/Relocations/x86_64/TNSWidgets.yml +77 -77
- package/ui/action-bar/action-bar-common.js.map +1 -1
- package/ui/action-bar/index.android.js +3 -1
- package/ui/action-bar/index.android.js.map +1 -1
- package/ui/action-bar/index.ios.js.map +1 -1
- package/ui/activity-indicator/activity-indicator-common.js.map +1 -1
- package/ui/animation/index.ios.d.ts +1 -0
- package/ui/animation/index.ios.js +175 -6
- package/ui/animation/index.ios.js.map +1 -1
- package/ui/builder/xml2ui.js.map +1 -1
- package/ui/button/button-common.js.map +1 -1
- package/ui/button/index.android.js +3 -1
- package/ui/button/index.android.js.map +1 -1
- package/ui/button/index.ios.d.ts +1 -0
- package/ui/button/index.ios.js +27 -11
- package/ui/button/index.ios.js.map +1 -1
- package/ui/core/view/index.android.d.ts +2 -2
- package/ui/core/view/index.android.js +7 -5
- package/ui/core/view/index.android.js.map +1 -1
- package/ui/core/view/index.d.ts +2 -2
- package/ui/core/view/index.ios.d.ts +1 -1
- package/ui/core/view/index.ios.js +78 -21
- package/ui/core/view/index.ios.js.map +1 -1
- package/ui/core/view/view-common.d.ts +3 -3
- package/ui/core/view/view-common.js.map +1 -1
- package/ui/core/view-base/index.js +5 -0
- package/ui/core/view-base/index.js.map +1 -1
- package/ui/date-picker/date-picker-common.js.map +1 -1
- package/ui/date-picker/index.android.js +4 -2
- package/ui/date-picker/index.android.js.map +1 -1
- package/ui/date-picker/index.ios.js +29 -21
- package/ui/date-picker/index.ios.js.map +1 -1
- package/ui/editable-text-base/editable-text-base-common.js.map +1 -1
- package/ui/editable-text-base/index.android.js +3 -1
- package/ui/editable-text-base/index.android.js.map +1 -1
- package/ui/frame/fragment.transitions.android.js +5 -2
- package/ui/frame/fragment.transitions.android.js.map +1 -1
- package/ui/frame/frame-common.js.map +1 -1
- package/ui/frame/index.android.js +1 -1
- package/ui/frame/index.android.js.map +1 -1
- package/ui/frame/index.ios.js +17 -4
- package/ui/frame/index.ios.js.map +1 -1
- package/ui/gestures/gestures-common.d.ts +232 -7
- package/ui/gestures/gestures-common.js +98 -4
- package/ui/gestures/gestures-common.js.map +1 -1
- package/ui/gestures/index.d.ts +2 -289
- package/ui/html-view/html-view-common.js.map +1 -1
- package/ui/image/image-common.js.map +1 -1
- package/ui/image/index.android.js +3 -1
- package/ui/image/index.android.js.map +1 -1
- package/ui/image-cache/image-cache-common.js.map +1 -1
- package/ui/image-cache/index.ios.js +13 -13
- package/ui/image-cache/index.ios.js.map +1 -1
- package/ui/index.d.ts +1 -1
- package/ui/index.js +1 -1
- package/ui/index.js.map +1 -1
- package/ui/label/index.android.js.map +1 -1
- package/ui/label/index.ios.d.ts +1 -0
- package/ui/label/index.ios.js +30 -8
- package/ui/label/index.ios.js.map +1 -1
- package/ui/layouts/absolute-layout/absolute-layout-common.js.map +1 -1
- package/ui/layouts/dock-layout/dock-layout-common.js.map +1 -1
- package/ui/layouts/flexbox-layout/flexbox-layout-common.js.map +1 -1
- package/ui/layouts/grid-layout/grid-layout-common.js.map +1 -1
- package/ui/layouts/root-layout/index.ios.js +7 -2
- package/ui/layouts/root-layout/index.ios.js.map +1 -1
- package/ui/layouts/root-layout/root-layout-common.js.map +1 -1
- package/ui/layouts/stack-layout/stack-layout-common.js.map +1 -1
- package/ui/layouts/wrap-layout/wrap-layout-common.js.map +1 -1
- package/ui/list-picker/index.android.js +6 -2
- package/ui/list-picker/index.android.js.map +1 -1
- package/ui/list-picker/index.ios.js +1 -7
- package/ui/list-picker/index.ios.js.map +1 -1
- package/ui/list-picker/list-picker-common.js.map +1 -1
- package/ui/list-view/index.android.js +8 -2
- package/ui/list-view/index.android.js.map +1 -1
- package/ui/list-view/index.ios.js.map +1 -1
- package/ui/list-view/list-view-common.js.map +1 -1
- package/ui/page/index.android.js.map +1 -1
- package/ui/page/index.d.ts +4 -4
- package/ui/page/index.ios.js +12 -3
- package/ui/page/index.ios.js.map +1 -1
- package/ui/page/page-common.js.map +1 -1
- package/ui/placeholder/index.android.js.map +1 -1
- package/ui/progress/progress-common.js.map +1 -1
- package/ui/proxy-view-container/index.js.map +1 -1
- package/ui/repeater/index.js.map +1 -1
- package/ui/scroll-view/index.android.d.ts +1 -1
- package/ui/scroll-view/index.android.js +9 -5
- package/ui/scroll-view/index.android.js.map +1 -1
- package/ui/scroll-view/index.ios.d.ts +1 -2
- package/ui/scroll-view/index.ios.js +6 -8
- package/ui/scroll-view/index.ios.js.map +1 -1
- package/ui/scroll-view/scroll-view-common.d.ts +2 -4
- package/ui/scroll-view/scroll-view-common.js +17 -24
- package/ui/scroll-view/scroll-view-common.js.map +1 -1
- package/ui/search-bar/index.android.js +6 -2
- package/ui/search-bar/index.android.js.map +1 -1
- package/ui/search-bar/search-bar-common.js.map +1 -1
- package/ui/segmented-bar/index.android.js +6 -2
- package/ui/segmented-bar/index.android.js.map +1 -1
- package/ui/segmented-bar/segmented-bar-common.js.map +1 -1
- package/ui/slider/index.android.js +2 -1
- package/ui/slider/index.android.js.map +1 -1
- package/ui/slider/slider-common.js.map +1 -1
- package/ui/styling/background-common.d.ts +5 -4
- package/ui/styling/background-common.js +3 -0
- package/ui/styling/background-common.js.map +1 -1
- package/ui/styling/background.d.ts +13 -4
- package/ui/styling/background.ios.d.ts +26 -0
- package/ui/styling/background.ios.js +681 -372
- package/ui/styling/background.ios.js.map +1 -1
- package/ui/styling/box-shadow.d.ts +1 -0
- package/ui/styling/box-shadow.js.map +1 -1
- package/ui/styling/css-selector.js.map +1 -1
- package/ui/styling/css-shadow.d.ts +3 -3
- package/ui/styling/css-shadow.js +1 -1
- package/ui/styling/style/index.d.ts +4 -3
- package/ui/styling/style/index.js.map +1 -1
- package/ui/styling/style-properties.js +10 -1
- package/ui/styling/style-properties.js.map +1 -1
- package/ui/styling/style-scope.js.map +1 -1
- package/ui/switch/index.android.js +3 -1
- package/ui/switch/index.android.js.map +1 -1
- package/ui/switch/switch-common.js.map +1 -1
- package/ui/tab-view/index.ios.js +8 -2
- package/ui/tab-view/index.ios.js.map +1 -1
- package/ui/tab-view/tab-view-common.js.map +1 -1
- package/ui/text-base/index.android.d.ts +1 -0
- package/ui/text-base/index.android.js +24 -5
- package/ui/text-base/index.android.js.map +1 -1
- package/ui/text-base/index.d.ts +9 -3
- package/ui/text-base/index.ios.d.ts +2 -2
- package/ui/text-base/index.ios.js +5 -10
- package/ui/text-base/index.ios.js.map +1 -1
- package/ui/text-base/span.js.map +1 -1
- package/ui/text-base/text-base-common.d.ts +7 -4
- package/ui/text-base/text-base-common.js +15 -0
- package/ui/text-base/text-base-common.js.map +1 -1
- package/ui/text-field/text-field-common.js.map +1 -1
- package/ui/text-view/index.android.js.map +1 -1
- package/ui/text-view/index.ios.js.map +1 -1
- package/ui/time-picker/index.android.js +3 -1
- package/ui/time-picker/index.android.js.map +1 -1
- package/ui/time-picker/time-picker-common.js.map +1 -1
- package/ui/transition/shared-transition-helper.ios.js +15 -0
- package/ui/transition/shared-transition-helper.ios.js.map +1 -1
- package/ui/transition/shared-transition.d.ts +6 -2
- package/ui/transition/shared-transition.js.map +1 -1
- package/ui/utils.d.ts +16 -19
- package/ui/utils.ios.d.ts +11 -10
- package/ui/utils.ios.js +37 -37
- package/ui/utils.ios.js.map +1 -1
- package/ui/web-view/web-view-common.js.map +1 -1
- package/utils/ios/index.d.ts +0 -1
- package/utils/ios/index.js +0 -65
- package/utils/ios/index.js.map +1 -1
- package/utils/native-helper.d.ts +0 -7
- package/utils/number-utils.d.ts +11 -0
- package/utils/number-utils.js +18 -0
- package/utils/number-utils.js.map +1 -1
- package/xhr/index.js.map +1 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { LinearGradient } from './linear-gradient';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Screen } from '../../platform';
|
|
3
|
+
import { isDataURI, isFileOrResourcePath, layout } from '../../utils';
|
|
4
|
+
import { extendPointsToTargetY } from '../../utils/number-utils';
|
|
4
5
|
import { ios as iosViewUtils } from '../utils';
|
|
5
6
|
import { ImageSource } from '../../image-source';
|
|
6
7
|
import { parse as cssParse } from '../../css-value';
|
|
7
|
-
import { Length } from './style-properties';
|
|
8
8
|
export * from './background-common';
|
|
9
9
|
const clearCGColor = UIColor.clearColor.CGColor;
|
|
10
|
+
const uriPattern = /url\(('|")(.*?)\1\)/;
|
|
10
11
|
const symbolUrl = Symbol('backgroundImageUrl');
|
|
11
12
|
export var CacheMode;
|
|
12
13
|
(function (CacheMode) {
|
|
@@ -20,59 +21,266 @@ export var ios;
|
|
|
20
21
|
if (!nativeView) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
// Unset this in case another layer handles background color (e.g. gradient)
|
|
25
|
+
nativeView.layer.backgroundColor = null;
|
|
26
|
+
// Cleanup of previous values
|
|
27
|
+
clearBackgroundVisualEffects(view);
|
|
28
|
+
// Borders, shadows, etc
|
|
29
|
+
drawBackgroundVisualEffects(view);
|
|
30
|
+
if (!background.image) {
|
|
31
|
+
callback(background?.color?.ios);
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
else {
|
|
34
|
+
if (!(background.image instanceof LinearGradient)) {
|
|
35
|
+
setUIColorFromImage(view, nativeView, callback, flip);
|
|
36
|
+
}
|
|
31
37
|
}
|
|
32
|
-
|
|
38
|
+
}
|
|
39
|
+
ios.createBackgroundUIColor = createBackgroundUIColor;
|
|
40
|
+
function drawBackgroundVisualEffects(view) {
|
|
41
|
+
const background = view.style.backgroundInternal;
|
|
42
|
+
const nativeView = view.nativeViewProtected;
|
|
43
|
+
const layer = nativeView.layer;
|
|
44
|
+
let needsLayerAdjustmentOnScroll = false;
|
|
45
|
+
// Add new gradient layer or update existing one
|
|
33
46
|
if (background.image instanceof LinearGradient) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
subscribeForScrollNotifications(view);
|
|
47
|
+
if (!nativeView.gradientLayer) {
|
|
48
|
+
nativeView.gradientLayer = CAGradientLayer.new();
|
|
49
|
+
layer.insertSublayerAtIndex(nativeView.gradientLayer, 0);
|
|
50
|
+
}
|
|
51
|
+
iosViewUtils.drawGradient(nativeView, nativeView.gradientLayer, background.image);
|
|
52
|
+
needsLayerAdjustmentOnScroll = true;
|
|
41
53
|
}
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
// Initialize clipping mask (usually for clip-path and non-uniform rounded borders)
|
|
55
|
+
maskLayerIfNeeded(nativeView, background);
|
|
56
|
+
if (background.hasUniformBorder()) {
|
|
44
57
|
const borderColor = background.getUniformBorderColor();
|
|
45
|
-
layer.borderColor =
|
|
58
|
+
layer.borderColor = borderColor?.ios?.CGColor;
|
|
46
59
|
layer.borderWidth = layout.toDeviceIndependentPixels(background.getUniformBorderWidth());
|
|
47
|
-
|
|
48
|
-
const cornerRadius = layout.toDeviceIndependentPixels(background.getUniformBorderRadius());
|
|
49
|
-
layer.cornerRadius = Math.min(Math.min(renderSize.width / 2, renderSize.height / 2), cornerRadius);
|
|
60
|
+
layer.cornerRadius = getUniformBorderRadius(view, layer.bounds);
|
|
50
61
|
}
|
|
51
62
|
else {
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
drawNonUniformBorders(nativeView, background);
|
|
64
|
+
needsLayerAdjustmentOnScroll = true;
|
|
54
65
|
}
|
|
55
|
-
// Clip-path should be called after borders are applied
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
drawClipPath(nativeView, background);
|
|
66
|
+
// Clip-path should be called after borders are applied
|
|
67
|
+
if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH && layer.mask instanceof CAShapeLayer) {
|
|
68
|
+
layer.mask.path = generateClipPath(view, layer.bounds);
|
|
59
69
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
if (background.hasBoxShadow()) {
|
|
71
|
+
drawBoxShadow(view);
|
|
72
|
+
needsLayerAdjustmentOnScroll = true;
|
|
63
73
|
}
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
if (needsLayerAdjustmentOnScroll) {
|
|
75
|
+
registerAdjustLayersOnScrollListener(view);
|
|
66
76
|
}
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
}
|
|
78
|
+
ios.drawBackgroundVisualEffects = drawBackgroundVisualEffects;
|
|
79
|
+
function clearBackgroundVisualEffects(view) {
|
|
80
|
+
const nativeView = view.nativeViewProtected;
|
|
81
|
+
if (!nativeView) {
|
|
82
|
+
return;
|
|
69
83
|
}
|
|
70
|
-
|
|
84
|
+
const background = view.style.backgroundInternal;
|
|
85
|
+
const hasGradientBackground = background.image && background.image instanceof LinearGradient;
|
|
86
|
+
// Remove mask if there is no clip path or non-uniform border with radius
|
|
87
|
+
let needsMask;
|
|
88
|
+
switch (nativeView.maskType) {
|
|
89
|
+
case iosViewUtils.LayerMask.BORDER:
|
|
90
|
+
needsMask = !background.hasUniformBorder() && background.hasBorderRadius();
|
|
91
|
+
break;
|
|
92
|
+
case iosViewUtils.LayerMask.CLIP_PATH:
|
|
93
|
+
needsMask = !!background.clipPath;
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
needsMask = false;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (!needsMask) {
|
|
100
|
+
clearLayerMask(nativeView, background);
|
|
101
|
+
}
|
|
102
|
+
// Clear box shadow if it's no longer needed
|
|
103
|
+
if (background.clearFlags & 2 /* BackgroundClearFlags.CLEAR_BOX_SHADOW */) {
|
|
104
|
+
clearBoxShadow(nativeView);
|
|
105
|
+
}
|
|
106
|
+
// Non-uniform borders cleanup
|
|
107
|
+
if (nativeView.hasNonUniformBorder) {
|
|
108
|
+
if (nativeView.hasNonUniformBorderColor && background.hasUniformBorderColor()) {
|
|
109
|
+
clearNonUniformColorBorders(nativeView);
|
|
110
|
+
}
|
|
111
|
+
if (background.hasUniformBorder()) {
|
|
112
|
+
clearNonUniformBorders(nativeView);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (nativeView.gradientLayer && !hasGradientBackground) {
|
|
116
|
+
nativeView.gradientLayer.removeFromSuperlayer();
|
|
117
|
+
nativeView.gradientLayer = null;
|
|
118
|
+
}
|
|
119
|
+
// Force unset scroll listener
|
|
120
|
+
unregisterAdjustLayersOnScrollListener(view);
|
|
121
|
+
// Reset clear flags
|
|
71
122
|
background.clearFlags = 0 /* BackgroundClearFlags.NONE */;
|
|
72
123
|
}
|
|
73
|
-
ios.
|
|
124
|
+
ios.clearBackgroundVisualEffects = clearBackgroundVisualEffects;
|
|
125
|
+
function generateShadowLayerPaths(view, bounds) {
|
|
126
|
+
const background = view.style.backgroundInternal;
|
|
127
|
+
const nativeView = view.nativeViewProtected;
|
|
128
|
+
const layer = nativeView.layer;
|
|
129
|
+
const boxShadow = background.getBoxShadow();
|
|
130
|
+
const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
|
|
131
|
+
const { width, height } = bounds.size;
|
|
132
|
+
let innerPath, shadowPath;
|
|
133
|
+
// Generate more detailed paths if view has border radius
|
|
134
|
+
if (background.hasBorderRadius()) {
|
|
135
|
+
if (background.hasUniformBorder()) {
|
|
136
|
+
const cornerRadius = layer.cornerRadius;
|
|
137
|
+
const cappedRadius = getBorderCapRadius(cornerRadius, width / 2, height / 2);
|
|
138
|
+
const cappedOuterRadii = {
|
|
139
|
+
topLeft: cappedRadius,
|
|
140
|
+
topRight: cappedRadius,
|
|
141
|
+
bottomLeft: cappedRadius,
|
|
142
|
+
bottomRight: cappedRadius,
|
|
143
|
+
};
|
|
144
|
+
const cappedOuterRadiiWithSpread = {
|
|
145
|
+
topLeft: cappedRadius + spreadRadius,
|
|
146
|
+
topRight: cappedRadius + spreadRadius,
|
|
147
|
+
bottomLeft: cappedRadius + spreadRadius,
|
|
148
|
+
bottomRight: cappedRadius + spreadRadius,
|
|
149
|
+
};
|
|
150
|
+
innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
151
|
+
shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
|
|
155
|
+
const outerTopRightRadius = layout.toDeviceIndependentPixels(background.borderTopRightRadius);
|
|
156
|
+
const outerBottomRightRadius = layout.toDeviceIndependentPixels(background.borderBottomRightRadius);
|
|
157
|
+
const outerBottomLeftRadius = layout.toDeviceIndependentPixels(background.borderBottomLeftRadius);
|
|
158
|
+
const topRadii = outerTopLeftRadius + outerTopRightRadius;
|
|
159
|
+
const rightRadii = outerTopRightRadius + outerBottomRightRadius;
|
|
160
|
+
const bottomRadii = outerBottomRightRadius + outerBottomLeftRadius;
|
|
161
|
+
const leftRadii = outerBottomLeftRadius + outerTopLeftRadius;
|
|
162
|
+
const cappedOuterRadii = {
|
|
163
|
+
topLeft: getBorderCapRadius(outerTopLeftRadius, (outerTopLeftRadius / topRadii) * width, (outerTopLeftRadius / leftRadii) * height),
|
|
164
|
+
topRight: getBorderCapRadius(outerTopRightRadius, (outerTopRightRadius / topRadii) * width, (outerTopRightRadius / rightRadii) * height),
|
|
165
|
+
bottomLeft: getBorderCapRadius(outerBottomLeftRadius, (outerBottomLeftRadius / bottomRadii) * width, (outerBottomLeftRadius / leftRadii) * height),
|
|
166
|
+
bottomRight: getBorderCapRadius(outerBottomRightRadius, (outerBottomRightRadius / bottomRadii) * width, (outerBottomRightRadius / rightRadii) * height),
|
|
167
|
+
};
|
|
168
|
+
// Add spread radius to corners that actually have radius as shadow has grown larger
|
|
169
|
+
// than view itself and needs to be rounded accordingly
|
|
170
|
+
const cappedOuterRadiiWithSpread = {
|
|
171
|
+
topLeft: cappedOuterRadii.topLeft > 0 ? cappedOuterRadii.topLeft + spreadRadius : cappedOuterRadii.topLeft,
|
|
172
|
+
topRight: cappedOuterRadii.topRight > 0 ? cappedOuterRadii.topRight + spreadRadius : cappedOuterRadii.topRight,
|
|
173
|
+
bottomLeft: cappedOuterRadii.bottomLeft > 0 ? cappedOuterRadii.bottomLeft + spreadRadius : cappedOuterRadii.bottomLeft,
|
|
174
|
+
bottomRight: cappedOuterRadii.bottomRight > 0 ? cappedOuterRadii.bottomRight + spreadRadius : cappedOuterRadii.bottomRight,
|
|
175
|
+
};
|
|
176
|
+
innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
177
|
+
shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
innerPath = CGPathCreateWithRect(bounds, null);
|
|
182
|
+
shadowPath = CGPathCreateWithRect(CGRectInset(bounds, -spreadRadius, -spreadRadius), null);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
maskPath: generateShadowMaskPath(bounds, boxShadow, innerPath),
|
|
186
|
+
shadowPath,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
ios.generateShadowLayerPaths = generateShadowLayerPaths;
|
|
190
|
+
function generateClipPath(view, bounds) {
|
|
191
|
+
const background = view.style.backgroundInternal;
|
|
192
|
+
const { origin, size } = bounds;
|
|
193
|
+
const position = {
|
|
194
|
+
left: origin.x,
|
|
195
|
+
top: origin.y,
|
|
196
|
+
bottom: size.height,
|
|
197
|
+
right: size.width,
|
|
198
|
+
};
|
|
199
|
+
if (position.right === 0 || position.bottom === 0) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
let path;
|
|
203
|
+
const clipPath = background.clipPath;
|
|
204
|
+
const functionName = clipPath.substring(0, clipPath.indexOf('('));
|
|
205
|
+
const value = clipPath.replace(`${functionName}(`, '').replace(')', '');
|
|
206
|
+
switch (functionName) {
|
|
207
|
+
case 'rect':
|
|
208
|
+
path = rectPath(value, position);
|
|
209
|
+
break;
|
|
210
|
+
case 'inset':
|
|
211
|
+
path = insetPath(value, position);
|
|
212
|
+
break;
|
|
213
|
+
case 'circle':
|
|
214
|
+
path = circlePath(value, position);
|
|
215
|
+
break;
|
|
216
|
+
case 'ellipse':
|
|
217
|
+
path = ellipsePath(value, position);
|
|
218
|
+
break;
|
|
219
|
+
case 'polygon':
|
|
220
|
+
path = polygonPath(value, position);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
return path;
|
|
224
|
+
}
|
|
225
|
+
ios.generateClipPath = generateClipPath;
|
|
226
|
+
function getUniformBorderRadius(view, bounds) {
|
|
227
|
+
const background = view.style.backgroundInternal;
|
|
228
|
+
const { width, height } = bounds.size;
|
|
229
|
+
const cornerRadius = layout.toDeviceIndependentPixels(background.getUniformBorderRadius());
|
|
230
|
+
return Math.min(Math.min(width / 2, height / 2), cornerRadius);
|
|
231
|
+
}
|
|
232
|
+
ios.getUniformBorderRadius = getUniformBorderRadius;
|
|
233
|
+
function generateNonUniformBorderInnerClipRoundedPath(view, bounds) {
|
|
234
|
+
const background = view.style.backgroundInternal;
|
|
235
|
+
const nativeView = view.nativeViewProtected;
|
|
236
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
|
|
237
|
+
return generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRadii);
|
|
238
|
+
}
|
|
239
|
+
ios.generateNonUniformBorderInnerClipRoundedPath = generateNonUniformBorderInnerClipRoundedPath;
|
|
240
|
+
function generateNonUniformBorderOuterClipRoundedPath(view, bounds) {
|
|
241
|
+
const background = view.style.backgroundInternal;
|
|
242
|
+
const nativeView = view.nativeViewProtected;
|
|
243
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
|
|
244
|
+
return generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
245
|
+
}
|
|
246
|
+
ios.generateNonUniformBorderOuterClipRoundedPath = generateNonUniformBorderOuterClipRoundedPath;
|
|
247
|
+
function generateNonUniformMultiColorBorderRoundedPaths(view, bounds) {
|
|
248
|
+
const background = view.style.backgroundInternal;
|
|
249
|
+
const nativeView = view.nativeViewProtected;
|
|
250
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
|
|
251
|
+
return generateNonUniformMultiColorBorderPaths(bounds, background, cappedOuterRadii);
|
|
252
|
+
}
|
|
253
|
+
ios.generateNonUniformMultiColorBorderRoundedPaths = generateNonUniformMultiColorBorderRoundedPaths;
|
|
74
254
|
})(ios || (ios = {}));
|
|
75
|
-
function
|
|
255
|
+
function maskLayerIfNeeded(nativeView, background) {
|
|
256
|
+
const layer = nativeView.layer;
|
|
257
|
+
// Check if layer should be masked
|
|
258
|
+
if (!(layer.mask instanceof CAShapeLayer)) {
|
|
259
|
+
// Since layers can only accept up to a single mask at a time, clip path is given more priority
|
|
260
|
+
if (background.clipPath) {
|
|
261
|
+
nativeView.maskType = iosViewUtils.LayerMask.CLIP_PATH;
|
|
262
|
+
}
|
|
263
|
+
else if (!background.hasUniformBorder() && background.hasBorderRadius()) {
|
|
264
|
+
nativeView.maskType = iosViewUtils.LayerMask.BORDER;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
nativeView.maskType = null;
|
|
268
|
+
}
|
|
269
|
+
if (nativeView.maskType != null) {
|
|
270
|
+
nativeView.originalMask = layer.mask;
|
|
271
|
+
layer.mask = CAShapeLayer.new();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function clearLayerMask(nativeView, background) {
|
|
276
|
+
if (nativeView.outerShadowContainerLayer) {
|
|
277
|
+
nativeView.outerShadowContainerLayer.mask = null;
|
|
278
|
+
}
|
|
279
|
+
nativeView.layer.mask = nativeView.originalMask;
|
|
280
|
+
nativeView.originalMask = null;
|
|
281
|
+
nativeView.maskType = null;
|
|
282
|
+
}
|
|
283
|
+
function onBackgroundViewScroll(args) {
|
|
76
284
|
const view = args.object;
|
|
77
285
|
const nativeView = view.nativeViewProtected;
|
|
78
286
|
if (nativeView instanceof UIScrollView) {
|
|
@@ -80,61 +288,62 @@ function onScroll(args) {
|
|
|
80
288
|
}
|
|
81
289
|
}
|
|
82
290
|
function adjustLayersForScrollView(nativeView) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
layer.setAffineTransform(transform);
|
|
98
|
-
if (nativeView.layer.mask) {
|
|
99
|
-
nativeView.layer.mask.setAffineTransform(transform);
|
|
100
|
-
}
|
|
101
|
-
CATransaction.commit();
|
|
291
|
+
// Compensates with transition for the background layers for scrolling in ScrollView based controls.
|
|
292
|
+
CATransaction.begin();
|
|
293
|
+
CATransaction.setDisableActions(true);
|
|
294
|
+
const offset = nativeView.contentOffset;
|
|
295
|
+
const transform = {
|
|
296
|
+
a: 1,
|
|
297
|
+
b: 0,
|
|
298
|
+
c: 0,
|
|
299
|
+
d: 1,
|
|
300
|
+
tx: offset.x,
|
|
301
|
+
ty: offset.y,
|
|
302
|
+
};
|
|
303
|
+
if (nativeView.layer.mask) {
|
|
304
|
+
nativeView.layer.mask.setAffineTransform(transform);
|
|
102
305
|
}
|
|
306
|
+
// Nested layers
|
|
307
|
+
if (nativeView.gradientLayer) {
|
|
308
|
+
nativeView.gradientLayer.setAffineTransform(transform);
|
|
309
|
+
}
|
|
310
|
+
if (nativeView.borderLayer) {
|
|
311
|
+
nativeView.borderLayer.setAffineTransform(transform);
|
|
312
|
+
}
|
|
313
|
+
if (nativeView.outerShadowContainerLayer) {
|
|
314
|
+
// Update bounds of shadow layer as it belongs to parent view
|
|
315
|
+
nativeView.outerShadowContainerLayer.bounds = nativeView.bounds;
|
|
316
|
+
nativeView.outerShadowContainerLayer.setAffineTransform(transform);
|
|
317
|
+
}
|
|
318
|
+
CATransaction.setDisableActions(false);
|
|
319
|
+
CATransaction.commit();
|
|
103
320
|
}
|
|
104
|
-
function
|
|
321
|
+
function unregisterAdjustLayersOnScrollListener(view) {
|
|
105
322
|
if (view.nativeViewProtected instanceof UIScrollView) {
|
|
106
|
-
view.off('scroll',
|
|
323
|
+
view.off('scroll', onBackgroundViewScroll);
|
|
107
324
|
}
|
|
108
325
|
}
|
|
109
|
-
function
|
|
326
|
+
function registerAdjustLayersOnScrollListener(view) {
|
|
110
327
|
if (view.nativeViewProtected instanceof UIScrollView) {
|
|
111
|
-
view.
|
|
328
|
+
view.off('scroll', onBackgroundViewScroll);
|
|
329
|
+
view.on('scroll', onBackgroundViewScroll);
|
|
112
330
|
adjustLayersForScrollView(view.nativeViewProtected);
|
|
113
331
|
}
|
|
114
332
|
}
|
|
333
|
+
function clearNonUniformColorBorders(nativeView) {
|
|
334
|
+
if (nativeView.borderLayer) {
|
|
335
|
+
nativeView.borderLayer.mask = null;
|
|
336
|
+
nativeView.borderLayer.sublayers = null;
|
|
337
|
+
}
|
|
338
|
+
nativeView.hasNonUniformBorderColor = false;
|
|
339
|
+
}
|
|
115
340
|
function clearNonUniformBorders(nativeView) {
|
|
116
341
|
if (nativeView.borderLayer) {
|
|
117
342
|
nativeView.borderLayer.removeFromSuperlayer();
|
|
343
|
+
nativeView.borderLayer = null;
|
|
118
344
|
}
|
|
119
|
-
|
|
120
|
-
nativeView.layer.mask = nativeView.borderOriginalMask;
|
|
121
|
-
nativeView.hasBorderMask = false;
|
|
122
|
-
nativeView.borderOriginalMask = null;
|
|
123
|
-
}
|
|
124
|
-
if (nativeView.topBorderLayer) {
|
|
125
|
-
nativeView.topBorderLayer.removeFromSuperlayer();
|
|
126
|
-
}
|
|
127
|
-
if (nativeView.rightBorderLayer) {
|
|
128
|
-
nativeView.rightBorderLayer.removeFromSuperlayer();
|
|
129
|
-
}
|
|
130
|
-
if (nativeView.bottomBorderLayer) {
|
|
131
|
-
nativeView.bottomBorderLayer.removeFromSuperlayer();
|
|
132
|
-
}
|
|
133
|
-
if (nativeView.leftBorderLayer) {
|
|
134
|
-
nativeView.leftBorderLayer.removeFromSuperlayer();
|
|
135
|
-
}
|
|
345
|
+
nativeView.hasNonUniformBorder = false;
|
|
136
346
|
}
|
|
137
|
-
const pattern = /url\(('|")(.*?)\1\)/;
|
|
138
347
|
function setUIColorFromImage(view, nativeView, callback, flip) {
|
|
139
348
|
const frame = nativeView.frame;
|
|
140
349
|
const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
|
|
@@ -146,7 +355,7 @@ function setUIColorFromImage(view, nativeView, callback, flip) {
|
|
|
146
355
|
const background = style.backgroundInternal;
|
|
147
356
|
let imageUri = background.image;
|
|
148
357
|
if (imageUri) {
|
|
149
|
-
const match = imageUri.match(
|
|
358
|
+
const match = imageUri.match(uriPattern);
|
|
150
359
|
if (match && match[2]) {
|
|
151
360
|
imageUri = match[2];
|
|
152
361
|
}
|
|
@@ -314,11 +523,11 @@ function getDrawParams(image, background, width, height) {
|
|
|
314
523
|
}
|
|
315
524
|
function uiColorFromImage(img, view, callback, flip) {
|
|
316
525
|
const background = view.style.backgroundInternal;
|
|
317
|
-
|
|
526
|
+
const nativeView = view.nativeViewProtected;
|
|
527
|
+
if (!img || !nativeView) {
|
|
318
528
|
callback(background.color && background.color.ios);
|
|
319
529
|
return;
|
|
320
530
|
}
|
|
321
|
-
const nativeView = view.nativeViewProtected;
|
|
322
531
|
const frame = nativeView.frame;
|
|
323
532
|
const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
|
|
324
533
|
const boundsHeight = view.scaleY ? frame.size.height / view.scaleY : frame.size.height;
|
|
@@ -384,29 +593,12 @@ function cssValueToDeviceIndependentPixels(source, total) {
|
|
|
384
593
|
return parseFloat(source);
|
|
385
594
|
}
|
|
386
595
|
}
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const { width, height } = layer.bounds.size;
|
|
394
|
-
const { x, y } = layer.bounds.origin;
|
|
395
|
-
const left = x;
|
|
396
|
-
const top = y;
|
|
397
|
-
const right = x + width;
|
|
398
|
-
const bottom = y + height;
|
|
399
|
-
const { min, max } = Math;
|
|
400
|
-
const borderTopWidth = max(0, layout.toDeviceIndependentPixels(background.borderTopWidth));
|
|
401
|
-
const borderRightWidth = max(0, layout.toDeviceIndependentPixels(background.borderRightWidth));
|
|
402
|
-
const borderBottomWidth = max(0, layout.toDeviceIndependentPixels(background.borderBottomWidth));
|
|
403
|
-
const borderLeftWidth = max(0, layout.toDeviceIndependentPixels(background.borderLeftWidth));
|
|
404
|
-
const borderVWidth = borderTopWidth + borderBottomWidth;
|
|
405
|
-
const borderHWidth = borderLeftWidth + borderRightWidth;
|
|
406
|
-
const cappedBorderTopWidth = borderTopWidth && borderTopWidth * min(1, height / borderVWidth);
|
|
407
|
-
const cappedBorderRightWidth = borderRightWidth && borderRightWidth * min(1, width / borderHWidth);
|
|
408
|
-
const cappedBorderBottomWidth = borderBottomWidth && borderBottomWidth * min(1, height / borderVWidth);
|
|
409
|
-
const cappedBorderLeftWidth = borderLeftWidth && borderLeftWidth * min(1, width / borderHWidth);
|
|
596
|
+
function getBorderCapRadius(a, b, c) {
|
|
597
|
+
return a && Math.min(a, Math.min(b, c));
|
|
598
|
+
}
|
|
599
|
+
function calculateNonUniformBorderCappedRadii(bounds, background) {
|
|
600
|
+
const { width, height } = bounds.size;
|
|
601
|
+
const { x, y } = bounds.origin;
|
|
410
602
|
const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
|
|
411
603
|
const outerTopRightRadius = layout.toDeviceIndependentPixels(background.borderTopRightRadius);
|
|
412
604
|
const outerBottomRightRadius = layout.toDeviceIndependentPixels(background.borderBottomRightRadius);
|
|
@@ -415,310 +607,427 @@ function drawUniformColorNonUniformBorders(nativeView, background) {
|
|
|
415
607
|
const rightRadii = outerTopRightRadius + outerBottomRightRadius;
|
|
416
608
|
const bottomRadii = outerBottomRightRadius + outerBottomLeftRadius;
|
|
417
609
|
const leftRadii = outerBottomLeftRadius + outerTopLeftRadius;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
nativeView.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
443
|
-
CGPathMoveToPoint(borderPath, null, left + cappedOuterTopLeftRadius, top + cappedBorderTopWidth);
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
CGPathMoveToPoint(borderPath, null, left, top);
|
|
447
|
-
}
|
|
448
|
-
if (cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0) {
|
|
449
|
-
const innerTopRightWRadius = max(0, cappedOuterTopRightRadius - cappedBorderRightWidth);
|
|
450
|
-
const innerTopRightHRadius = max(0, cappedOuterTopRightRadius - cappedBorderTopWidth);
|
|
451
|
-
const innerTopRightMaxRadius = max(innerTopRightWRadius, innerTopRightHRadius);
|
|
452
|
-
const innerTopRightTransform = CGAffineTransformMake(innerTopRightMaxRadius && innerTopRightWRadius / innerTopRightMaxRadius, 0, 0, innerTopRightMaxRadius && innerTopRightHRadius / innerTopRightMaxRadius, right - cappedBorderRightWidth - innerTopRightWRadius, top + cappedBorderTopWidth + innerTopRightHRadius);
|
|
453
|
-
CGPathAddArc(borderPath, innerTopRightTransform, 0, 0, innerTopRightMaxRadius, (Math.PI * 3) / 2, 0, false);
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
CGPathMoveToPoint(borderPath, null, right, top);
|
|
457
|
-
}
|
|
458
|
-
if (cappedBorderBottomWidth > 0 || cappedBorderRightWidth > 0) {
|
|
459
|
-
const innerBottomRightWRadius = max(0, cappedOuterBottomRightRadius - cappedBorderRightWidth);
|
|
460
|
-
const innerBottomRightHRadius = max(0, cappedOuterBottomRightRadius - cappedBorderBottomWidth);
|
|
461
|
-
const innerBottomRightMaxRadius = max(innerBottomRightWRadius, innerBottomRightHRadius);
|
|
462
|
-
const innerBottomRightTransform = CGAffineTransformMake(innerBottomRightMaxRadius && innerBottomRightWRadius / innerBottomRightMaxRadius, 0, 0, innerBottomRightMaxRadius && innerBottomRightHRadius / innerBottomRightMaxRadius, right - cappedBorderRightWidth - innerBottomRightWRadius, bottom - cappedBorderBottomWidth - innerBottomRightHRadius);
|
|
463
|
-
CGPathAddArc(borderPath, innerBottomRightTransform, 0, 0, innerBottomRightMaxRadius, 0, Math.PI / 2, false);
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
CGPathAddLineToPoint(borderPath, null, right, bottom);
|
|
467
|
-
}
|
|
468
|
-
if (cappedBorderBottomWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
469
|
-
const innerBottomLeftWRadius = max(0, cappedOuterBottomLeftRadius - cappedBorderLeftWidth);
|
|
470
|
-
const innerBottomLeftHRadius = max(0, cappedOuterBottomLeftRadius - cappedBorderBottomWidth);
|
|
471
|
-
const innerBottomLeftMaxRadius = max(innerBottomLeftWRadius, innerBottomLeftHRadius);
|
|
472
|
-
const innerBottomLeftTransform = CGAffineTransformMake(innerBottomLeftMaxRadius && innerBottomLeftWRadius / innerBottomLeftMaxRadius, 0, 0, innerBottomLeftMaxRadius && innerBottomLeftHRadius / innerBottomLeftMaxRadius, left + cappedBorderLeftWidth + innerBottomLeftWRadius, bottom - cappedBorderBottomWidth - innerBottomLeftHRadius);
|
|
473
|
-
CGPathAddArc(borderPath, innerBottomLeftTransform, 0, 0, innerBottomLeftMaxRadius, Math.PI / 2, Math.PI, false);
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
CGPathAddLineToPoint(borderPath, null, left, bottom);
|
|
610
|
+
const cappedOuterRadii = {
|
|
611
|
+
topLeft: getBorderCapRadius(outerTopLeftRadius, (outerTopLeftRadius / topRadii) * width, (outerTopLeftRadius / leftRadii) * height),
|
|
612
|
+
topRight: getBorderCapRadius(outerTopRightRadius, (outerTopRightRadius / topRadii) * width, (outerTopRightRadius / rightRadii) * height),
|
|
613
|
+
bottomLeft: getBorderCapRadius(outerBottomLeftRadius, (outerBottomLeftRadius / bottomRadii) * width, (outerBottomLeftRadius / leftRadii) * height),
|
|
614
|
+
bottomRight: getBorderCapRadius(outerBottomRightRadius, (outerBottomRightRadius / bottomRadii) * width, (outerBottomRightRadius / rightRadii) * height),
|
|
615
|
+
};
|
|
616
|
+
return cappedOuterRadii;
|
|
617
|
+
}
|
|
618
|
+
function drawNonUniformBorders(nativeView, background) {
|
|
619
|
+
const layer = nativeView.layer;
|
|
620
|
+
const layerBounds = layer.bounds;
|
|
621
|
+
layer.borderColor = null;
|
|
622
|
+
layer.borderWidth = 0;
|
|
623
|
+
layer.cornerRadius = 0;
|
|
624
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(layerBounds, background);
|
|
625
|
+
if (nativeView.maskType === iosViewUtils.LayerMask.BORDER && layer.mask instanceof CAShapeLayer) {
|
|
626
|
+
layer.mask.path = generateNonUniformBorderOuterClipPath(layerBounds, cappedOuterRadii);
|
|
627
|
+
}
|
|
628
|
+
if (background.hasBorderWidth()) {
|
|
629
|
+
if (!nativeView.hasNonUniformBorder) {
|
|
630
|
+
nativeView.borderLayer = CAShapeLayer.new();
|
|
631
|
+
nativeView.borderLayer.fillRule = kCAFillRuleEvenOdd;
|
|
632
|
+
layer.addSublayer(nativeView.borderLayer);
|
|
633
|
+
nativeView.hasNonUniformBorder = true;
|
|
477
634
|
}
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const innerTopLeftMaxRadius = max(innerTopLeftWRadius, innerTopLeftHRadius);
|
|
482
|
-
const innerTopLeftTransform = CGAffineTransformMake(innerTopLeftMaxRadius && innerTopLeftWRadius / innerTopLeftMaxRadius, 0, 0, innerTopLeftMaxRadius && innerTopLeftHRadius / innerTopLeftMaxRadius, left + cappedBorderLeftWidth + innerTopLeftWRadius, top + cappedBorderTopWidth + innerTopLeftHRadius);
|
|
483
|
-
CGPathAddArc(borderPath, innerTopLeftTransform, 0, 0, innerTopLeftMaxRadius, Math.PI, (Math.PI * 3) / 2, false);
|
|
635
|
+
if (background.hasUniformBorderColor()) {
|
|
636
|
+
nativeView.borderLayer.fillColor = background.borderTopColor?.ios?.CGColor || UIColor.blackColor.CGColor;
|
|
637
|
+
nativeView.borderLayer.path = generateNonUniformBorderInnerClipPath(layerBounds, background, cappedOuterRadii);
|
|
484
638
|
}
|
|
485
639
|
else {
|
|
486
|
-
|
|
640
|
+
// Non-uniform borders need more layers in order to display multiple colors at the same time
|
|
641
|
+
let borderTopLayer, borderRightLayer, borderBottomLayer, borderLeftLayer;
|
|
642
|
+
if (!nativeView.hasNonUniformBorderColor) {
|
|
643
|
+
const maskLayer = CAShapeLayer.new();
|
|
644
|
+
maskLayer.fillRule = kCAFillRuleEvenOdd;
|
|
645
|
+
nativeView.borderLayer.mask = maskLayer;
|
|
646
|
+
borderTopLayer = CAShapeLayer.new();
|
|
647
|
+
borderRightLayer = CAShapeLayer.new();
|
|
648
|
+
borderBottomLayer = CAShapeLayer.new();
|
|
649
|
+
borderLeftLayer = CAShapeLayer.new();
|
|
650
|
+
nativeView.borderLayer.addSublayer(borderTopLayer);
|
|
651
|
+
nativeView.borderLayer.addSublayer(borderRightLayer);
|
|
652
|
+
nativeView.borderLayer.addSublayer(borderBottomLayer);
|
|
653
|
+
nativeView.borderLayer.addSublayer(borderLeftLayer);
|
|
654
|
+
nativeView.hasNonUniformBorderColor = true;
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
borderTopLayer = nativeView.borderLayer.sublayers[0];
|
|
658
|
+
borderRightLayer = nativeView.borderLayer.sublayers[1];
|
|
659
|
+
borderBottomLayer = nativeView.borderLayer.sublayers[2];
|
|
660
|
+
borderLeftLayer = nativeView.borderLayer.sublayers[3];
|
|
661
|
+
}
|
|
662
|
+
const paths = generateNonUniformMultiColorBorderPaths(layerBounds, background, cappedOuterRadii);
|
|
663
|
+
borderTopLayer.fillColor = background.borderTopColor?.ios?.CGColor || UIColor.blackColor.CGColor;
|
|
664
|
+
borderTopLayer.path = paths[0];
|
|
665
|
+
borderRightLayer.fillColor = background.borderRightColor?.ios?.CGColor || UIColor.blackColor.CGColor;
|
|
666
|
+
borderRightLayer.path = paths[1];
|
|
667
|
+
borderBottomLayer.fillColor = background.borderBottomColor?.ios?.CGColor || UIColor.blackColor.CGColor;
|
|
668
|
+
borderBottomLayer.path = paths[2];
|
|
669
|
+
borderLeftLayer.fillColor = background.borderLeftColor?.ios?.CGColor || UIColor.blackColor.CGColor;
|
|
670
|
+
borderLeftLayer.path = paths[3];
|
|
671
|
+
// Clip inner area to create borders
|
|
672
|
+
if (nativeView.borderLayer.mask instanceof CAShapeLayer) {
|
|
673
|
+
nativeView.borderLayer.mask.path = generateNonUniformBorderInnerClipPath(layerBounds, background, cappedOuterRadii);
|
|
674
|
+
}
|
|
487
675
|
}
|
|
488
|
-
CGPathCloseSubpath(borderPath);
|
|
489
|
-
const borderLayer = CAShapeLayer.layer();
|
|
490
|
-
borderLayer.fillColor = (background.borderTopColor && background.borderTopColor.ios.CGColor) || UIColor.blackColor.CGColor;
|
|
491
|
-
borderLayer.fillRule = kCAFillRuleEvenOdd;
|
|
492
|
-
borderLayer.path = borderPath;
|
|
493
|
-
layer.addSublayer(borderLayer);
|
|
494
|
-
nativeView.borderLayer = borderLayer;
|
|
495
676
|
}
|
|
496
|
-
nativeView.hasNonUniformBorder = true;
|
|
497
677
|
}
|
|
498
|
-
function
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
678
|
+
function calculateInnerBorderClipRadius(radius, insetX, insetY) {
|
|
679
|
+
const innerXRadius = Math.max(0, radius - insetX);
|
|
680
|
+
const innerYRadius = Math.max(0, radius - insetY);
|
|
681
|
+
const innerMaxRadius = Math.max(innerXRadius, innerYRadius);
|
|
682
|
+
return {
|
|
683
|
+
xRadius: innerXRadius,
|
|
684
|
+
yRadius: innerYRadius,
|
|
685
|
+
maxRadius: innerMaxRadius,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Generates a path that represents the rounded view area.
|
|
690
|
+
*
|
|
691
|
+
* @param bounds
|
|
692
|
+
* @param cappedRadii
|
|
693
|
+
* @param offset
|
|
694
|
+
* @returns
|
|
695
|
+
*/
|
|
696
|
+
export function generateNonUniformBorderOuterClipPath(bounds, cappedRadii, offset = 0) {
|
|
697
|
+
const { width, height } = bounds.size;
|
|
698
|
+
const { x, y } = bounds.origin;
|
|
699
|
+
const left = x - offset;
|
|
700
|
+
const top = y - offset;
|
|
701
|
+
const right = x + width + offset;
|
|
702
|
+
const bottom = y + height + offset;
|
|
703
|
+
const clipPath = CGPathCreateMutable();
|
|
704
|
+
CGPathMoveToPoint(clipPath, null, left + cappedRadii.topLeft, top);
|
|
705
|
+
CGPathAddArcToPoint(clipPath, null, right, top, right, top + cappedRadii.topRight, cappedRadii.topRight);
|
|
706
|
+
CGPathAddArcToPoint(clipPath, null, right, bottom, right - cappedRadii.bottomRight, bottom, cappedRadii.bottomRight);
|
|
707
|
+
CGPathAddArcToPoint(clipPath, null, left, bottom, left, bottom - cappedRadii.bottomLeft, cappedRadii.bottomLeft);
|
|
708
|
+
CGPathAddArcToPoint(clipPath, null, left, top, left + cappedRadii.topLeft, top, cappedRadii.topLeft);
|
|
709
|
+
CGPathCloseSubpath(clipPath);
|
|
710
|
+
return clipPath;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Generates a path that represents the area inside borders.
|
|
714
|
+
*
|
|
715
|
+
* @param bounds
|
|
716
|
+
* @param background
|
|
717
|
+
* @param cappedOuterRadii
|
|
718
|
+
* @returns
|
|
719
|
+
*/
|
|
720
|
+
function generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRadii) {
|
|
721
|
+
const { width, height } = bounds.size;
|
|
722
|
+
const { x, y } = bounds.origin;
|
|
723
|
+
const position = {
|
|
724
|
+
left: x,
|
|
725
|
+
top: y,
|
|
726
|
+
bottom: y + height,
|
|
727
|
+
right: x + width,
|
|
728
|
+
};
|
|
729
|
+
const borderTopWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderTopWidth));
|
|
730
|
+
const borderRightWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderRightWidth));
|
|
731
|
+
const borderBottomWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderBottomWidth));
|
|
732
|
+
const borderLeftWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderLeftWidth));
|
|
733
|
+
const borderVWidth = borderTopWidth + borderBottomWidth;
|
|
734
|
+
const borderHWidth = borderLeftWidth + borderRightWidth;
|
|
735
|
+
const cappedBorderTopWidth = borderTopWidth && borderTopWidth * Math.min(1, height / borderVWidth);
|
|
736
|
+
const cappedBorderRightWidth = borderRightWidth && borderRightWidth * Math.min(1, width / borderHWidth);
|
|
737
|
+
const cappedBorderBottomWidth = borderBottomWidth && borderBottomWidth * Math.min(1, height / borderVWidth);
|
|
738
|
+
const cappedBorderLeftWidth = borderLeftWidth && borderLeftWidth * Math.min(1, width / borderHWidth);
|
|
739
|
+
const clipPath = CGPathCreateMutable();
|
|
740
|
+
CGPathAddRect(clipPath, null, CGRectMake(x, y, width, height));
|
|
741
|
+
// Inner clip paths
|
|
742
|
+
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
743
|
+
CGPathMoveToPoint(clipPath, null, position.left + cappedOuterRadii.topLeft, position.top + cappedBorderTopWidth);
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
CGPathMoveToPoint(clipPath, null, position.left, position.top);
|
|
747
|
+
}
|
|
748
|
+
if (cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0) {
|
|
749
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topRight, cappedBorderRightWidth, cappedBorderTopWidth);
|
|
750
|
+
const innerTopRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
751
|
+
CGPathAddArc(clipPath, innerTopRightTransform, 0, 0, maxRadius, (Math.PI * 3) / 2, 0, false);
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
CGPathAddLineToPoint(clipPath, null, position.right, position.top);
|
|
755
|
+
}
|
|
756
|
+
if (cappedBorderBottomWidth > 0 || cappedBorderRightWidth > 0) {
|
|
757
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomRight, cappedBorderRightWidth, cappedBorderBottomWidth);
|
|
758
|
+
const innerBottomRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
759
|
+
CGPathAddArc(clipPath, innerBottomRightTransform, 0, 0, maxRadius, 0, Math.PI / 2, false);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
CGPathAddLineToPoint(clipPath, null, position.right, position.bottom);
|
|
763
|
+
}
|
|
764
|
+
if (cappedBorderBottomWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
765
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomLeft, cappedBorderLeftWidth, cappedBorderBottomWidth);
|
|
766
|
+
const innerBottomLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
767
|
+
CGPathAddArc(clipPath, innerBottomLeftTransform, 0, 0, maxRadius, Math.PI / 2, Math.PI, false);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
CGPathAddLineToPoint(clipPath, null, position.left, position.bottom);
|
|
771
|
+
}
|
|
772
|
+
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
773
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topLeft, cappedBorderLeftWidth, cappedBorderTopWidth);
|
|
774
|
+
const innerTopLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
775
|
+
CGPathAddArc(clipPath, innerTopLeftTransform, 0, 0, maxRadius, Math.PI, (Math.PI * 3) / 2, false);
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
CGPathAddLineToPoint(clipPath, null, position.left, position.top);
|
|
779
|
+
}
|
|
780
|
+
CGPathCloseSubpath(clipPath);
|
|
781
|
+
return clipPath;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Generates paths for visualizing borders with different color per side.
|
|
785
|
+
* This is achieved by extending all borders enough to consume entire view size and
|
|
786
|
+
* use an inner path along with even-odd fill rule to render borders according to their corresponding width.
|
|
787
|
+
*
|
|
788
|
+
* @param bounds
|
|
789
|
+
* @param background
|
|
790
|
+
* @param cappedOuterRadii
|
|
791
|
+
* @returns
|
|
792
|
+
*/
|
|
793
|
+
function generateNonUniformMultiColorBorderPaths(bounds, background, cappedOuterRadii) {
|
|
794
|
+
const { width, height } = bounds.size;
|
|
795
|
+
const { x, y } = bounds.origin;
|
|
796
|
+
const position = {
|
|
797
|
+
left: x,
|
|
798
|
+
top: y,
|
|
799
|
+
bottom: y + height,
|
|
800
|
+
right: x + width,
|
|
513
801
|
};
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
const
|
|
802
|
+
const topWidth = layout.toDeviceIndependentPixels(background.borderTopWidth);
|
|
803
|
+
const rightWidth = layout.toDeviceIndependentPixels(background.borderRightWidth);
|
|
804
|
+
const bottomWidth = layout.toDeviceIndependentPixels(background.borderBottomWidth);
|
|
805
|
+
const leftWidth = layout.toDeviceIndependentPixels(background.borderLeftWidth);
|
|
806
|
+
// These values have 1 as fallback in order to handler borders with zero values
|
|
807
|
+
const safeTopWidth = Math.max(topWidth, 1);
|
|
808
|
+
const safeRightWidth = Math.max(rightWidth, 1);
|
|
809
|
+
const safeBottomWidth = Math.max(bottomWidth, 1);
|
|
810
|
+
const safeLeftWidth = Math.max(leftWidth, 1);
|
|
811
|
+
const paths = new Array(4);
|
|
518
812
|
const lto = {
|
|
519
|
-
x:
|
|
520
|
-
y:
|
|
813
|
+
x: position.left,
|
|
814
|
+
y: position.top,
|
|
521
815
|
}; // left-top-outside
|
|
522
816
|
const lti = {
|
|
523
|
-
x:
|
|
524
|
-
y:
|
|
817
|
+
x: position.left + safeLeftWidth,
|
|
818
|
+
y: position.top + safeTopWidth,
|
|
525
819
|
}; // left-top-inside
|
|
526
820
|
const rto = {
|
|
527
|
-
x:
|
|
528
|
-
y:
|
|
821
|
+
x: position.right,
|
|
822
|
+
y: position.top,
|
|
529
823
|
}; // right-top-outside
|
|
530
824
|
const rti = {
|
|
531
|
-
x:
|
|
532
|
-
y:
|
|
825
|
+
x: position.right - safeRightWidth,
|
|
826
|
+
y: position.top + safeTopWidth,
|
|
533
827
|
}; // right-top-inside
|
|
534
828
|
const rbo = {
|
|
535
|
-
x:
|
|
536
|
-
y:
|
|
829
|
+
x: position.right,
|
|
830
|
+
y: position.bottom,
|
|
537
831
|
}; // right-bottom-outside
|
|
538
832
|
const rbi = {
|
|
539
|
-
x:
|
|
540
|
-
y:
|
|
833
|
+
x: position.right - safeRightWidth,
|
|
834
|
+
y: position.bottom - safeBottomWidth,
|
|
541
835
|
}; // right-bottom-inside
|
|
542
836
|
const lbo = {
|
|
543
|
-
x:
|
|
544
|
-
y:
|
|
837
|
+
x: position.left,
|
|
838
|
+
y: position.bottom,
|
|
545
839
|
}; // left-bottom-outside
|
|
546
840
|
const lbi = {
|
|
547
|
-
x:
|
|
548
|
-
y:
|
|
841
|
+
x: position.left + safeLeftWidth,
|
|
842
|
+
y: position.bottom - safeBottomWidth,
|
|
549
843
|
}; // left-bottom-inside
|
|
550
|
-
|
|
844
|
+
const centerX = position.right / 2;
|
|
845
|
+
const centerY = position.bottom / 2;
|
|
846
|
+
// These values help calculate the size that each border shape should consume
|
|
847
|
+
const averageHorizontalBorderWidth = Math.max((leftWidth + rightWidth) / 2, 1);
|
|
848
|
+
const averageVerticalBorderWidth = Math.max((topWidth + bottomWidth) / 2, 1);
|
|
849
|
+
const viewRatioMultiplier = width > 0 && height > 0 ? width / height : 1;
|
|
551
850
|
const borderTopColor = background.borderTopColor;
|
|
552
|
-
|
|
851
|
+
const borderRightColor = background.borderRightColor;
|
|
852
|
+
const borderBottomColor = background.borderBottomColor;
|
|
853
|
+
const borderLeftColor = background.borderLeftColor;
|
|
854
|
+
let borderTopY = centerY * (safeTopWidth / averageHorizontalBorderWidth) * viewRatioMultiplier;
|
|
855
|
+
let borderRightX = position.right - (centerX * (safeRightWidth / averageVerticalBorderWidth)) / viewRatioMultiplier;
|
|
856
|
+
let borderBottomY = position.bottom - centerY * (safeBottomWidth / averageHorizontalBorderWidth) * viewRatioMultiplier;
|
|
857
|
+
let borderLeftX = (centerX * (safeLeftWidth / averageVerticalBorderWidth)) / viewRatioMultiplier;
|
|
858
|
+
// Adjust border triangle width in case of borders colliding between each other or borders being less than 4
|
|
859
|
+
const hasHorizontalIntersection = borderLeftX > borderRightX;
|
|
860
|
+
const hasVerticalIntersection = borderTopY > borderBottomY;
|
|
861
|
+
if (hasVerticalIntersection) {
|
|
862
|
+
borderTopY = extendPointsToTargetY(lto.y, lto.x, lti.y, lti.x, borderLeftX);
|
|
863
|
+
borderBottomY = extendPointsToTargetY(lbo.y, lbo.x, lbi.y, lbi.x, borderLeftX);
|
|
864
|
+
}
|
|
865
|
+
else if (hasHorizontalIntersection) {
|
|
866
|
+
borderLeftX = extendPointsToTargetY(lto.x, lto.y, lti.x, lti.y, borderTopY);
|
|
867
|
+
borderRightX = extendPointsToTargetY(rto.x, rto.y, rti.x, rti.y, borderTopY);
|
|
868
|
+
}
|
|
869
|
+
if (topWidth > 0 && borderTopColor?.ios) {
|
|
553
870
|
const topBorderPath = CGPathCreateMutable();
|
|
871
|
+
const borderTopLeftX = extendPointsToTargetY(lto.x, lto.y, lti.x, lti.y, borderTopY);
|
|
872
|
+
const borderTopRightX = extendPointsToTargetY(rto.x, rto.y, rti.x, rti.y, borderTopY);
|
|
554
873
|
CGPathMoveToPoint(topBorderPath, null, lto.x, lto.y);
|
|
555
874
|
CGPathAddLineToPoint(topBorderPath, null, rto.x, rto.y);
|
|
556
|
-
CGPathAddLineToPoint(topBorderPath, null,
|
|
557
|
-
|
|
875
|
+
CGPathAddLineToPoint(topBorderPath, null, borderTopRightX, borderTopY);
|
|
876
|
+
if (borderTopRightX !== borderTopLeftX) {
|
|
877
|
+
CGPathAddLineToPoint(topBorderPath, null, borderTopLeftX, borderTopY);
|
|
878
|
+
}
|
|
558
879
|
CGPathAddLineToPoint(topBorderPath, null, lto.x, lto.y);
|
|
559
|
-
|
|
560
|
-
topBorderLayer.fillColor = background.borderTopColor.ios.CGColor;
|
|
561
|
-
topBorderLayer.path = topBorderPath;
|
|
562
|
-
borderLayer.addSublayer(topBorderLayer);
|
|
563
|
-
nativeView.topBorderLayer = topBorderLayer;
|
|
564
|
-
hasNonUniformBorder = true;
|
|
880
|
+
paths[0] = topBorderPath;
|
|
565
881
|
}
|
|
566
|
-
|
|
567
|
-
if (right > 0 && borderRightColor && borderRightColor.ios) {
|
|
882
|
+
if (rightWidth > 0 && borderRightColor?.ios) {
|
|
568
883
|
const rightBorderPath = CGPathCreateMutable();
|
|
884
|
+
const borderRightBottomY = extendPointsToTargetY(rbo.y, rbo.x, rbi.y, rbi.x, borderRightX);
|
|
885
|
+
const borderRightTopY = extendPointsToTargetY(rto.y, rto.x, rti.y, rti.x, borderRightX);
|
|
569
886
|
CGPathMoveToPoint(rightBorderPath, null, rto.x, rto.y);
|
|
570
887
|
CGPathAddLineToPoint(rightBorderPath, null, rbo.x, rbo.y);
|
|
571
|
-
CGPathAddLineToPoint(rightBorderPath, null,
|
|
572
|
-
|
|
888
|
+
CGPathAddLineToPoint(rightBorderPath, null, borderRightX, borderRightBottomY);
|
|
889
|
+
if (borderRightBottomY !== borderRightTopY) {
|
|
890
|
+
CGPathAddLineToPoint(rightBorderPath, null, borderRightX, borderRightTopY);
|
|
891
|
+
}
|
|
573
892
|
CGPathAddLineToPoint(rightBorderPath, null, rto.x, rto.y);
|
|
574
|
-
|
|
575
|
-
rightBorderLayer.fillColor = background.borderRightColor.ios.CGColor;
|
|
576
|
-
rightBorderLayer.path = rightBorderPath;
|
|
577
|
-
borderLayer.addSublayer(rightBorderLayer);
|
|
578
|
-
nativeView.rightBorderLayer = rightBorderLayer;
|
|
579
|
-
hasNonUniformBorder = true;
|
|
893
|
+
paths[1] = rightBorderPath;
|
|
580
894
|
}
|
|
581
|
-
|
|
582
|
-
if (bottom > 0 && borderBottomColor && borderBottomColor.ios) {
|
|
895
|
+
if (bottomWidth > 0 && borderBottomColor?.ios) {
|
|
583
896
|
const bottomBorderPath = CGPathCreateMutable();
|
|
897
|
+
const borderBottomLeftX = extendPointsToTargetY(lbo.x, lbo.y, lbi.x, lbi.y, borderBottomY);
|
|
898
|
+
const borderBottomRightX = extendPointsToTargetY(rbo.x, rbo.y, rbi.x, rbi.y, borderBottomY);
|
|
584
899
|
CGPathMoveToPoint(bottomBorderPath, null, rbo.x, rbo.y);
|
|
585
900
|
CGPathAddLineToPoint(bottomBorderPath, null, lbo.x, lbo.y);
|
|
586
|
-
CGPathAddLineToPoint(bottomBorderPath, null,
|
|
587
|
-
|
|
901
|
+
CGPathAddLineToPoint(bottomBorderPath, null, borderBottomLeftX, borderBottomY);
|
|
902
|
+
if (borderBottomLeftX !== borderBottomRightX) {
|
|
903
|
+
CGPathAddLineToPoint(bottomBorderPath, null, borderBottomRightX, borderBottomY);
|
|
904
|
+
}
|
|
588
905
|
CGPathAddLineToPoint(bottomBorderPath, null, rbo.x, rbo.y);
|
|
589
|
-
|
|
590
|
-
bottomBorderLayer.fillColor = background.borderBottomColor.ios.CGColor;
|
|
591
|
-
bottomBorderLayer.path = bottomBorderPath;
|
|
592
|
-
borderLayer.addSublayer(bottomBorderLayer);
|
|
593
|
-
nativeView.bottomBorderLayer = bottomBorderLayer;
|
|
594
|
-
hasNonUniformBorder = true;
|
|
906
|
+
paths[2] = bottomBorderPath;
|
|
595
907
|
}
|
|
596
|
-
|
|
597
|
-
if (left > 0 && borderLeftColor && borderLeftColor.ios) {
|
|
908
|
+
if (leftWidth > 0 && borderLeftColor?.ios) {
|
|
598
909
|
const leftBorderPath = CGPathCreateMutable();
|
|
910
|
+
const borderLeftTopY = extendPointsToTargetY(lto.y, lto.x, lti.y, lti.x, borderLeftX);
|
|
911
|
+
const borderLeftBottomY = extendPointsToTargetY(lbo.y, lbo.x, lbi.y, lbi.x, borderLeftX);
|
|
599
912
|
CGPathMoveToPoint(leftBorderPath, null, lbo.x, lbo.y);
|
|
600
913
|
CGPathAddLineToPoint(leftBorderPath, null, lto.x, lto.y);
|
|
601
|
-
CGPathAddLineToPoint(leftBorderPath, null,
|
|
602
|
-
|
|
914
|
+
CGPathAddLineToPoint(leftBorderPath, null, borderLeftX, borderLeftTopY);
|
|
915
|
+
if (borderLeftTopY !== borderLeftBottomY) {
|
|
916
|
+
CGPathAddLineToPoint(leftBorderPath, null, borderLeftX, borderLeftBottomY);
|
|
917
|
+
}
|
|
603
918
|
CGPathAddLineToPoint(leftBorderPath, null, lbo.x, lbo.y);
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
borderLayer.addSublayer(leftBorderLayer);
|
|
608
|
-
nativeView.leftBorderLayer = leftBorderLayer;
|
|
609
|
-
hasNonUniformBorder = true;
|
|
610
|
-
}
|
|
611
|
-
nativeView.hasNonUniformBorder = hasNonUniformBorder;
|
|
919
|
+
paths[3] = leftBorderPath;
|
|
920
|
+
}
|
|
921
|
+
return paths;
|
|
612
922
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
layer
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
layer.shadowColor = boxShadow.color.ios.CGColor;
|
|
629
|
-
// prettier-ignore
|
|
630
|
-
layer.shadowOffset = CGSizeMake(Length.toDevicePixels(boxShadow.offsetX, 0.0), Length.toDevicePixels(boxShadow.offsetY, 0.0));
|
|
631
|
-
// this should match the view's border radius
|
|
632
|
-
let cornerRadius;
|
|
633
|
-
if (typeof view.style.borderRadius !== 'number') {
|
|
634
|
-
cornerRadius = Length.toDevicePixels(view.style.borderRadius, 0.0);
|
|
923
|
+
function drawBoxShadow(view) {
|
|
924
|
+
const background = view.style.backgroundInternal;
|
|
925
|
+
const nativeView = view.nativeViewProtected;
|
|
926
|
+
const layer = nativeView.layer;
|
|
927
|
+
// There is no parent to add shadow to
|
|
928
|
+
if (!layer.superlayer) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
const bounds = nativeView.bounds;
|
|
932
|
+
const viewFrame = nativeView.frame;
|
|
933
|
+
const boxShadow = background.getBoxShadow();
|
|
934
|
+
// Initialize outer shadows
|
|
935
|
+
let outerShadowContainerLayer;
|
|
936
|
+
if (nativeView.outerShadowContainerLayer) {
|
|
937
|
+
outerShadowContainerLayer = nativeView.outerShadowContainerLayer;
|
|
635
938
|
}
|
|
636
939
|
else {
|
|
637
|
-
|
|
940
|
+
outerShadowContainerLayer = CALayer.new();
|
|
941
|
+
// TODO: Make this dynamic when views get support for multiple shadows
|
|
942
|
+
const shadowLayer = CALayer.new();
|
|
943
|
+
// This mask is necessary to maintain transparent background
|
|
944
|
+
const maskLayer = CAShapeLayer.new();
|
|
945
|
+
maskLayer.fillRule = kCAFillRuleEvenOdd;
|
|
946
|
+
shadowLayer.mask = maskLayer;
|
|
947
|
+
outerShadowContainerLayer.addSublayer(shadowLayer);
|
|
948
|
+
// Instead of nesting it, add shadow container layer underneath view so that it's not affected by border masking
|
|
949
|
+
layer.superlayer.insertSublayerBelow(outerShadowContainerLayer, layer);
|
|
950
|
+
nativeView.outerShadowContainerLayer = outerShadowContainerLayer;
|
|
951
|
+
}
|
|
952
|
+
// Apply clip path to shadow
|
|
953
|
+
if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH && layer.mask instanceof CAShapeLayer) {
|
|
954
|
+
if (!outerShadowContainerLayer.mask) {
|
|
955
|
+
outerShadowContainerLayer.mask = CAShapeLayer.new();
|
|
956
|
+
}
|
|
957
|
+
if (outerShadowContainerLayer.mask instanceof CAShapeLayer) {
|
|
958
|
+
outerShadowContainerLayer.mask.path = layer.mask.path;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
outerShadowContainerLayer.bounds = bounds;
|
|
962
|
+
outerShadowContainerLayer.anchorPoint = layer.anchorPoint;
|
|
963
|
+
// Since shadow uses superlayer's coordinate system, we have to be more specific about shadow layer position
|
|
964
|
+
const { x: originX, y: originY } = outerShadowContainerLayer.anchorPoint;
|
|
965
|
+
outerShadowContainerLayer.position = CGPointMake(viewFrame.origin.x + viewFrame.size.width * originX, viewFrame.origin.y + viewFrame.size.height * originY);
|
|
966
|
+
// Inherit view visibility values
|
|
967
|
+
outerShadowContainerLayer.opacity = layer.opacity;
|
|
968
|
+
outerShadowContainerLayer.hidden = layer.hidden;
|
|
969
|
+
const outerShadowLayers = outerShadowContainerLayer.sublayers;
|
|
970
|
+
if (outerShadowLayers?.count) {
|
|
971
|
+
for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
|
|
972
|
+
const shadowLayer = outerShadowLayers[i];
|
|
973
|
+
const shadowRadius = layout.toDeviceIndependentPixels(boxShadow.blurRadius);
|
|
974
|
+
const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
|
|
975
|
+
const offsetX = layout.toDeviceIndependentPixels(boxShadow.offsetX);
|
|
976
|
+
const offsetY = layout.toDeviceIndependentPixels(boxShadow.offsetY);
|
|
977
|
+
const { maskPath, shadowPath } = ios.generateShadowLayerPaths(view, bounds);
|
|
978
|
+
shadowLayer.allowsEdgeAntialiasing = true;
|
|
979
|
+
shadowLayer.contentsScale = Screen.mainScreen.scale;
|
|
980
|
+
// Shadow opacity is handled on the shadow's color instance
|
|
981
|
+
shadowLayer.shadowOpacity = boxShadow.color?.a ? boxShadow.color.a / 255 : 1;
|
|
982
|
+
shadowLayer.shadowRadius = shadowRadius;
|
|
983
|
+
shadowLayer.shadowColor = boxShadow.color?.ios?.CGColor;
|
|
984
|
+
shadowLayer.shadowOffset = CGSizeMake(offsetX, offsetY);
|
|
985
|
+
// Apply spread radius by expanding shadow layer bounds (this has a nice glow with radii set to 0)
|
|
986
|
+
shadowLayer.shadowPath = shadowPath;
|
|
987
|
+
// A mask that ensures that view maintains transparent background
|
|
988
|
+
if (shadowLayer.mask instanceof CAShapeLayer) {
|
|
989
|
+
shadowLayer.mask.path = maskPath;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
638
992
|
}
|
|
639
|
-
// apply spreadRadius by expanding shadow layer bounds
|
|
640
|
-
// prettier-ignore
|
|
641
|
-
const bounds = CGRectInset(nativeView.bounds, -Length.toDevicePixels(boxShadow.spreadRadius, 0.0), -Length.toDevicePixels(boxShadow.spreadRadius, 0.0));
|
|
642
|
-
// This has the nice glow with box shadow of 0,0
|
|
643
|
-
layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(bounds, cornerRadius).CGPath;
|
|
644
993
|
}
|
|
645
994
|
function clearBoxShadow(nativeView) {
|
|
646
|
-
nativeView.
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return;
|
|
995
|
+
if (nativeView.outerShadowContainerLayer) {
|
|
996
|
+
nativeView.outerShadowContainerLayer.removeFromSuperlayer();
|
|
997
|
+
nativeView.outerShadowContainerLayer = null;
|
|
650
998
|
}
|
|
651
|
-
layer.masksToBounds = true;
|
|
652
|
-
layer.shadowOffset = CGSizeMake(0, 0);
|
|
653
|
-
layer.shadowColor = UIColor.clearColor.CGColor;
|
|
654
|
-
layer.cornerRadius = 0.0;
|
|
655
|
-
layer.shadowRadius = 0.0;
|
|
656
|
-
layer.shadowOpacity = 0.0;
|
|
657
999
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
break;
|
|
680
|
-
case 'inset':
|
|
681
|
-
path = insetPath(value, bounds);
|
|
682
|
-
break;
|
|
683
|
-
case 'circle':
|
|
684
|
-
path = circlePath(value, bounds);
|
|
685
|
-
break;
|
|
686
|
-
case 'ellipse':
|
|
687
|
-
path = ellipsePath(value, bounds);
|
|
688
|
-
break;
|
|
689
|
-
case 'polygon':
|
|
690
|
-
path = polygonPath(value, bounds);
|
|
691
|
-
break;
|
|
692
|
-
}
|
|
693
|
-
if (path) {
|
|
694
|
-
const shape = CAShapeLayer.layer();
|
|
695
|
-
shape.path = path;
|
|
696
|
-
layer.mask = shape;
|
|
697
|
-
nativeView.clipsToBounds = true;
|
|
698
|
-
const borderWidth = background.getUniformBorderWidth();
|
|
699
|
-
const borderColor = background.getUniformBorderColor();
|
|
700
|
-
if (borderWidth > 0 && borderColor instanceof Color) {
|
|
701
|
-
const borderLayer = CAShapeLayer.layer();
|
|
702
|
-
borderLayer.path = path;
|
|
703
|
-
borderLayer.lineWidth = borderWidth * 2;
|
|
704
|
-
borderLayer.strokeColor = borderColor.ios.CGColor;
|
|
705
|
-
borderLayer.fillColor = clearCGColor;
|
|
706
|
-
borderLayer.frame = nativeView.bounds;
|
|
707
|
-
layer.borderColor = undefined;
|
|
708
|
-
layer.borderWidth = 0;
|
|
709
|
-
layer.addSublayer(borderLayer);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Creates a mask that ensures no shadow will be displayed underneath transparent backgrounds.
|
|
1002
|
+
*
|
|
1003
|
+
* @param bounds
|
|
1004
|
+
* @param boxShadow
|
|
1005
|
+
* @param bordersClipPath
|
|
1006
|
+
* @returns
|
|
1007
|
+
*/
|
|
1008
|
+
function generateShadowMaskPath(bounds, boxShadow, innerClipPath) {
|
|
1009
|
+
const shadowRadius = layout.toDeviceIndependentPixels(boxShadow.blurRadius);
|
|
1010
|
+
const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
|
|
1011
|
+
const offsetX = layout.toDeviceIndependentPixels(boxShadow.offsetX);
|
|
1012
|
+
const offsetY = layout.toDeviceIndependentPixels(boxShadow.offsetY);
|
|
1013
|
+
// This value has to be large enough to avoid clipping shadow halo effect
|
|
1014
|
+
const outerRectRadius = shadowRadius * 3 + spreadRadius;
|
|
1015
|
+
const maskPath = CGPathCreateMutable();
|
|
1016
|
+
// Proper clip position and size
|
|
1017
|
+
const outerRect = CGRectOffset(CGRectInset(bounds, -outerRectRadius, -outerRectRadius), offsetX, offsetY);
|
|
1018
|
+
CGPathAddPath(maskPath, null, innerClipPath);
|
|
1019
|
+
CGPathAddRect(maskPath, null, outerRect);
|
|
1020
|
+
return maskPath;
|
|
712
1021
|
}
|
|
713
|
-
function rectPath(value,
|
|
1022
|
+
function rectPath(value, position) {
|
|
714
1023
|
const arr = value.split(/[\s]+/);
|
|
715
|
-
const top = cssValueToDeviceIndependentPixels(arr[0],
|
|
716
|
-
const right = cssValueToDeviceIndependentPixels(arr[1],
|
|
717
|
-
const bottom = cssValueToDeviceIndependentPixels(arr[2],
|
|
718
|
-
const left = cssValueToDeviceIndependentPixels(arr[3],
|
|
1024
|
+
const top = cssValueToDeviceIndependentPixels(arr[0], position.top);
|
|
1025
|
+
const right = cssValueToDeviceIndependentPixels(arr[1], position.right);
|
|
1026
|
+
const bottom = cssValueToDeviceIndependentPixels(arr[2], position.bottom);
|
|
1027
|
+
const left = cssValueToDeviceIndependentPixels(arr[3], position.left);
|
|
719
1028
|
return UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath;
|
|
720
1029
|
}
|
|
721
|
-
function insetPath(value,
|
|
1030
|
+
function insetPath(value, position) {
|
|
722
1031
|
const arr = value.split(/[\s]+/);
|
|
723
1032
|
let topString;
|
|
724
1033
|
let rightString;
|
|
@@ -742,40 +1051,40 @@ function insetPath(value, bounds) {
|
|
|
742
1051
|
bottomString = arr[2];
|
|
743
1052
|
leftString = arr[3];
|
|
744
1053
|
}
|
|
745
|
-
const top = cssValueToDeviceIndependentPixels(topString,
|
|
746
|
-
const right = cssValueToDeviceIndependentPixels('100%',
|
|
747
|
-
const bottom = cssValueToDeviceIndependentPixels('100%',
|
|
748
|
-
const left = cssValueToDeviceIndependentPixels(leftString,
|
|
1054
|
+
const top = cssValueToDeviceIndependentPixels(topString, position.bottom);
|
|
1055
|
+
const right = cssValueToDeviceIndependentPixels('100%', position.right) - cssValueToDeviceIndependentPixels(rightString, position.right);
|
|
1056
|
+
const bottom = cssValueToDeviceIndependentPixels('100%', position.bottom) - cssValueToDeviceIndependentPixels(bottomString, position.bottom);
|
|
1057
|
+
const left = cssValueToDeviceIndependentPixels(leftString, position.right);
|
|
749
1058
|
return UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath;
|
|
750
1059
|
}
|
|
751
|
-
function circlePath(value,
|
|
1060
|
+
function circlePath(value, position) {
|
|
752
1061
|
const arr = value.split(/[\s]+/);
|
|
753
|
-
const radius = cssValueToDeviceIndependentPixels(arr[0], (
|
|
754
|
-
const y = cssValueToDeviceIndependentPixels(arr[2],
|
|
755
|
-
const x = cssValueToDeviceIndependentPixels(arr[3],
|
|
1062
|
+
const radius = cssValueToDeviceIndependentPixels(arr[0], (position.right > position.bottom ? position.bottom : position.right) / 2);
|
|
1063
|
+
const y = cssValueToDeviceIndependentPixels(arr[2], position.bottom);
|
|
1064
|
+
const x = cssValueToDeviceIndependentPixels(arr[3], position.right);
|
|
756
1065
|
return UIBezierPath.bezierPathWithArcCenterRadiusStartAngleEndAngleClockwise(CGPointMake(x, y), radius, 0, 360, true).CGPath;
|
|
757
1066
|
}
|
|
758
|
-
function ellipsePath(value,
|
|
1067
|
+
function ellipsePath(value, position) {
|
|
759
1068
|
const arr = value.split(/[\s]+/);
|
|
760
|
-
const rX = cssValueToDeviceIndependentPixels(arr[0],
|
|
761
|
-
const rY = cssValueToDeviceIndependentPixels(arr[1],
|
|
762
|
-
const cX = cssValueToDeviceIndependentPixels(arr[3],
|
|
763
|
-
const cY = cssValueToDeviceIndependentPixels(arr[4],
|
|
1069
|
+
const rX = cssValueToDeviceIndependentPixels(arr[0], position.right);
|
|
1070
|
+
const rY = cssValueToDeviceIndependentPixels(arr[1], position.bottom);
|
|
1071
|
+
const cX = cssValueToDeviceIndependentPixels(arr[3], position.right);
|
|
1072
|
+
const cY = cssValueToDeviceIndependentPixels(arr[4], position.bottom);
|
|
764
1073
|
const left = cX - rX;
|
|
765
1074
|
const top = cY - rY;
|
|
766
1075
|
const width = rX * 2;
|
|
767
1076
|
const height = rY * 2;
|
|
768
1077
|
return UIBezierPath.bezierPathWithOvalInRect(CGRectMake(left, top, width, height)).CGPath;
|
|
769
1078
|
}
|
|
770
|
-
function polygonPath(value,
|
|
1079
|
+
function polygonPath(value, position) {
|
|
771
1080
|
const path = CGPathCreateMutable();
|
|
772
1081
|
let firstPoint;
|
|
773
1082
|
const arr = value.split(/[,]+/);
|
|
774
1083
|
for (let i = 0; i < arr.length; i++) {
|
|
775
1084
|
const xy = arr[i].trim().split(/[\s]+/);
|
|
776
1085
|
const point = {
|
|
777
|
-
x: cssValueToDeviceIndependentPixels(xy[0],
|
|
778
|
-
y: cssValueToDeviceIndependentPixels(xy[1],
|
|
1086
|
+
x: cssValueToDeviceIndependentPixels(xy[0], position.right),
|
|
1087
|
+
y: cssValueToDeviceIndependentPixels(xy[1], position.bottom),
|
|
779
1088
|
};
|
|
780
1089
|
if (!firstPoint) {
|
|
781
1090
|
firstPoint = point;
|