Implement non uniform border corner radiuses and fix blinking image-view radiuses (#4573)

* Image corners were blinking in #4322 and CSS border will now draw non uniform corner radiuses if the border color is uniform

* Implement per-side corner radiuses for iOS

* Update stretch-mode example

* Update matrix-mode example

* Update image resources

* Add clipping for non uniform radii without border width, don't throw for missing image resources in css
This commit is contained in:
Panayot Cankov
2017-07-27 15:36:47 +03:00
committed by GitHub
parent 2f6ca2524b
commit 43659799bc
57 changed files with 499 additions and 71 deletions

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -37,6 +37,8 @@ export function loadExamples() {
examples.set("padding-and-border", "css/padding-and-border");
examples.set("combinators", "css/combinators");
examples.set("styled-formatted-text", "css/styled-formatted-text");
examples.set("non-uniform-radius", "css/non-uniform-radius");
examples.set("missing-background-image", "css/missing-background-image");
return examples;
}

View File

@@ -0,0 +1,31 @@
.c1, .c2, .c3, .c4, .c5, .c6, .c7, .c8, .c9, .c10, .c11, .c12 {
width: 30;
height: 30;
background-size: 100% 100%;
background-color: yellow;
margin: 1;
}
.c1, .c3, .c5 {
background-image: url("~/ui-tests-app/the-never-found-unicorn.png");
}
.c2, .c4, .c6 {
background-image: url("~/ui-tests-app/resources/images/no-image.png");
}
.c7, .c9, .c11 {
background-image: url("res://theneverfoundunicorn");
}
.c8, .c10, .c12 {
background-image: url("res://testlogo");
}
.c3, .c4, .c9, .c10 {
border-radius: 10;
}
.c5, .c6, .c11, .c12 {
border-radius: 5 10 15 20;
}

View File

@@ -0,0 +1,23 @@
<Page navigatingTo="navigatingTo">
<GridLayout>
<WrapLayout width="220" horizontalAlignment="left" verticalAlignment="top">
<GridLayout class="c1" />
<GridLayout class="c2" />
<GridLayout class="c3" />
<GridLayout class="c4" />
<GridLayout class="c5" />
<GridLayout class="c6" />
<GridLayout class="c7" />
<GridLayout class="c8" />
<GridLayout class="c9" />
<GridLayout class="c10" />
<GridLayout class="c11" />
<GridLayout class="c12" />
</WrapLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,64 @@
GridLayout>GridLayout {
width: 60;
height: 60;
margin: 2;
border-color: rgba(0, 255, 0, 0.5);
background-color: rgba(0, 0, 255, 0.5);
}
.b1, .b2, .b3, .b4, .b5 {
background-image: url("res://tile");
}
.b1 {
background-repeat: no-repeat;
background-position: 50% 50%;
}
.b2 {
background-repeat: repeat-x;
background-position: 0% 0%;
}
.b3 {
background-repeat: repeat-y;
background-position: 0% 0%;
}
.b4 {
background-repeat: repeat;
background-position: 0% 0%;
}
.b5 {
background-repeat: no-repeat;
background-position: 100% 100%;
}
.g1 {
border-width: 15;
border-color: red;
border-radius: 0 10 20 30;
background-color: blue;
}
.g2 {
border-width: 15 10 5 0;
border-color: red;
border-radius: 0 10 20 30;
background-color: blue;
}
.g3 {
border-width: 5 10 5 10;
border-color: red;
border-radius: 20 20 20 20;
background-color: blue;
}
.g4 {
border-width: 10 5 10 5;
border-color: red;
border-radius: 20 20 20 20;
background-color: blue;
}

View File

@@ -0,0 +1,27 @@
<Page loaded="onLoaded">
<GridLayout rows="65,65,65,65,65,65" columns="65,65,65">
<GridLayout row="0" col="0" borderWidth="3" borderRadius="10" class="b1" />
<GridLayout row="0" col="1" borderWidth="0 3" borderRadius="10" class="b2" />
<GridLayout row="0" col="2" borderWidth="3 0" borderRadius="10" class="b3" />
<GridLayout row="1" col="0" borderWidth="2 4 6 8" borderRadius="10" class="b4" />
<GridLayout row="1" col="1" borderWidth="5 10 15 20" borderRadius="0 10 20 30" class="b5"/>
<GridLayout row="1" col="2" borderWidth="20 15 10 5" borderRadius="30 20 10 0" class="b1" />
<GridLayout row="2" col="0" borderWidth="3 3 3 0" borderRadius="0 10 0 10" class="b2" />
<GridLayout row="2" col="1" borderWidth="0 3" borderRadius="0 10 0 10" class="b3" />
<GridLayout row="2" col="2" borderWidth="3" borderRadius="0 10 0 10" class="b4" />
<GridLayout row="3" col="0" borderWidth="3" borderRadius="100 100 100 0" class="b5" />
<GridLayout row="3" col="1" borderWidth="3" borderRadius="100 100 0 0" class="b1" />
<GridLayout row="3" col="2" borderWidth="3" borderRadius="40 0 0 0" class="b2" />
<GridLayout row="4" col="0" borderWidth="20" borderRadius="0 10 20 30" class="b3" />
<GridLayout row="4" col="1" borderWidth="20" borderRadius="20 30 40 50" class="b4" />
<GridLayout row="4" col="2" class="g1" />
<GridLayout row="5" col="0" class="g2" />
<GridLayout row="5" col="1" class="g3" />
<GridLayout row="5" col="2" class="g4" />
</GridLayout>
</Page>

View File

@@ -13,6 +13,9 @@ export function loadExamples() {
const examples = new Map<string, string>();
examples.set("roundbtn", "image-view/rounded-buttons");
examples.set("roundimg", "image-view/rounded-images");
examples.set("mode-matrix", "image-view/mode-matrix");
examples.set("stretch-modes", "image-view/stretch-modes");
examples.set("missing-image", "image-view/missing-image");
return examples;
}

View File

@@ -0,0 +1,23 @@
<Page navigatingTo="navigatingTo">
<GridLayout>
<WrapLayout width="220" horizontalAlignment="left" verticalAlignment="top">
<Image src="~/ui-tests-app/the-never-found-unicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" />
<Image src="~/ui-tests-app/resources/images/no-image.png" margin="1" width="30" height="30" backgroundColor="yellow" />
<Image src="~/ui-tests-app/the-never-found-unicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="10" />
<Image src="~/ui-tests-app/resources/images/no-image.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="10" />
<Image src="~/ui-tests-app/the-never-found-unicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="5 10 15 20" />
<Image src="~/ui-tests-app/resources/images/no-image.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="5 10 15 20" />
<Image src="res://theneverfoundunicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" />
<Image src="res://testlogo" margin="1" width="30" height="30" backgroundColor="yellow" />
<Image src="res://theneverfoundunicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="10" />
<Image src="res://testlogo" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="10" />
<Image src="res://theneverfoundunicorn.png" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="5 10 15 20" />
<Image src="res://testlogo" margin="1" width="30" height="30" backgroundColor="yellow" borderRadius="5 10 15 20" />
</WrapLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,9 @@
Image {
margin: 1;
}
GridLayout {
background-image: url("res://tile");
background-repeat: repeat;
background-size: 78 78;
}

View File

@@ -0,0 +1,57 @@
import { Image, Stretch } from "tns-core-modules/ui/image";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout";
import { Color } from "tns-core-modules/color";
import * as imageSource from "tns-core-modules/image-source";
const sources = [
{ src: "up", rotation: 0 },
{ src: "upcw", rotation: -90 },
{ src: "upflip", rotation: 180 },
{ src: "upccw", rotation: 90 }
].map(({ src, rotation }) => ({ src: `res://${src}`, rotation }));
const stretchModes: Stretch[] = ["none", "aspectFill", "aspectFit", "fill"];
export function navigatingTo(args) {
const grid: GridLayout = args.object.getViewById("root");
for (let x = 0; x < 12; x++) {
for (let y = 0; y < 16; y++) {
const image = new Image();
const img = sources[x % 4];
const src = imageSource.fromFileOrResource(img.src);
src.rotationAngle = img.rotation;
image.src = src;
image.stretch = stretchModes[y % 4];
image.row = y;
image.col = x;
grid.addChild(image);
switch(Math.floor(x / 4)) {
case 1:
image.borderWidth = "3";
break;
case 2:
image.borderWidth = "3";
image.borderColor = "blue";
break;
}
switch(Math.floor(y / 4)) {
case 1:
image.borderRadius = "12";
break;
case 2:
image.borderRadius = "6 18 6 18";
break;
case 3:
image.borderWidth = "0 2 4 6";
image.borderRadius = "6 18 6 18";
break;
}
image.backgroundColor = new Color(0x6600FFFF);
}
}
}

View File

@@ -0,0 +1,8 @@
<Page navigatingTo="navigatingTo">
<GridLayout id="root"
rows="26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26"
columns="26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26"
horizontalAlignment="left" verticalAlignment="top"
margin="6">
</GridLayout>
</Page>

View File

@@ -0,0 +1,52 @@
import { Image, Stretch } from "tns-core-modules/ui/image";
import { Label } from "tns-core-modules/ui/label";
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
import { Color } from "tns-core-modules/color";
import * as imageSource from "tns-core-modules/image-source";
import { isAndroid } from "tns-core-modules/platform";
const sources = [
{ w: 32, h: 18, src: "i32x18" },
{ w: 32, h: 32, src: "i32x32" },
{ w: 18, h: 32, src: "i18x32" }
].map(({w, h, src}) => ({w, h, src: `res://${src}` }));
const stretchModes: Stretch[] = ["none", "aspectFill", "aspectFit", "fill"];
const widths = [ +8, 0, -8 ]
const heights = [ +8, 0, -8 ]
export function navigatingTo(args) {
const variants: { src: string, stretch: Stretch, width: number, height: number }[] = [];
// Better way for cartesian product?
sources.forEach(src =>
stretchModes.forEach(stretch =>
widths.forEach(width =>
heights.forEach(height =>
variants.push({ src: src.src, stretch, width: src.w + width, height: src.h + height })))));
const grid: LayoutBase = args.object.getViewById("root");
const label: Label = args.object.getViewById("label");
let lastTap = null;
variants.forEach(({ src, stretch, width, height}) => {
const image = new Image();
image.src = src;
image.backgroundColor = "yellow";
image.width = <any>(width + "px");
image.height = <any>(height + "px");
image.stretch = stretch;
image.borderColor = "yellow";
image.margin = "1px";
(<any>image).tag = `${width} ${height} ${stretch} ${src.substr(src.lastIndexOf("/") + 1)}`;
image.addEventListener("tap", (args: any) => {
if (lastTap) {
lastTap.borderColor = "yellow";
}
label.text = args.object.tag;
args.object.borderColor = "red";
lastTap = args.object;
});
grid.addChild(image);
});
}

View File

@@ -0,0 +1,7 @@
<Page navigatingTo="navigatingTo">
<GridLayout rows="auto, *">
<WrapLayout id="root" width="300" backgroundColor="orange" horizontalAlignment="left" verticalAlignment="top" margin="6">
</WrapLayout>
<Label id="label" row="1" />
</GridLayout>
</Page>

View File

@@ -303,7 +303,7 @@ export function test_AnimateBackgroundColor(done) {
label.animate({ backgroundColor: red, duration: 20 })
.then(() => {
TKUnit.assert(label.backgroundColor.equals(red));
TKUnit.assert((<colorModule.Color>label.backgroundColor).equals(red));
done();
})
.catch((e) => {
@@ -318,7 +318,7 @@ export function test_AnimateBackgroundColor_FromString(done) {
label.animate({ backgroundColor: <any>expected, duration: 20 })
.then(() => {
TKUnit.assert(label.backgroundColor.equals(clr));
TKUnit.assert((<colorModule.Color>label.backgroundColor).equals(clr));
done();
})
.catch((e) => {

View File

@@ -362,7 +362,7 @@ export function test_ExecuteCSSAnimation() {
TKUnit.waitUntilReady(() => label.isLoaded);
label.className = "l";
let green = new color.Color("green");
TKUnit.waitUntilReady(() => green.equals(label.backgroundColor), 1);
TKUnit.waitUntilReady(() => green.equals(<color.Color>label.backgroundColor), 1);
TKUnit.assertEqual(label.backgroundColor, green);
}

View File

@@ -74,7 +74,7 @@ if (platform.device.os === platform.platformNames.ios) {
progress.backgroundColor = new color.Color("red");
function testAction(views: Array<viewModule.View>) {
TKUnit.assertEqual(progress.backgroundColor.ios.CGColor, progress.ios.trackTintColor.CGColor, "progress.color");
TKUnit.assertEqual((<color.Color>progress.backgroundColor).ios.CGColor, progress.ios.trackTintColor.CGColor, "progress.color");
};
helper.buildUIAndRunTest(progress, testAction);

View File

@@ -118,7 +118,7 @@ if (isIOS) {
slider.backgroundColor = new Color("red");
function testAction(views: Array<View>) {
TKUnit.assertEqual(slider.backgroundColor.ios.CGColor, slider.ios.minimumTrackTintColor.CGColor, "slider.backgroundColor");
TKUnit.assertEqual((<Color>slider.backgroundColor).ios.CGColor, slider.ios.minimumTrackTintColor.CGColor, "slider.backgroundColor");
};
helper.buildUIAndRunTest(slider, testAction);

View File

@@ -11,6 +11,7 @@ import * as types from "tns-core-modules/utils/types";
import * as viewModule from "tns-core-modules/ui/core/view";
import { resolveFileNameFromUrl } from "tns-core-modules/ui/styling/style-scope";
import { unsetValue } from "tns-core-modules/ui/core/view";
import * as color from "tns-core-modules/color";
export function test_css_dataURI_is_applied_to_backgroundImageSource() {
const stack = new stackModule.StackLayout();
@@ -164,7 +165,7 @@ export function test_type_selector() {
stack.addChild(btn);
TKUnit.assert(btn.backgroundColor, "backgroundColor property not applied correctly.");
TKUnit.assertEqual(btn.backgroundColor.hex, "#FF0000", "backgroundColor");
TKUnit.assertEqual((<color.Color>btn.backgroundColor).hex, "#FF0000", "backgroundColor");
TKUnit.assertNull(label.backgroundColor, "backgroundColor should not have a value");
}

View File

@@ -60,7 +60,7 @@ if (platform.device.os === platform.platformNames.ios) {
mySwitch.backgroundColor = new color.Color("red");
function testAction(views: Array<viewModule.View>) {
TKUnit.assert(CGColorEqualToColor(mySwitch.backgroundColor.ios.CGColor, mySwitch.ios.onTintColor.CGColor), "mySwitch.color");
TKUnit.assert(CGColorEqualToColor((<color.Color>mySwitch.backgroundColor).ios.CGColor, mySwitch.ios.onTintColor.CGColor), "mySwitch.color");
};
helper.buildUIAndRunTest(mySwitch, testAction);

View File

@@ -880,35 +880,7 @@ export function testSetInlineStyle() {
helper.buildUIAndRunTest(lbl, function (views: Array<View>) {
TKUnit.assertEqual(lbl.color.hex, expectedColor);
TKUnit.assertEqual(lbl.backgroundColor.hex, expectedBackgroundColor);
});
};
export function testBorderWidth() {
helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array<View>) {
const lbl = views[0];
helper.waitUntilLayoutReady(lbl);
const expectedValue = Math.round(<number>lbl.borderWidth * utils.layout.getDisplayDensity());
const actualValue = definition.getUniformNativeBorderWidth(lbl);
TKUnit.assertAreClose(actualValue, expectedValue, 0.01, "borderWidth");
});
};
export function testCornerRadius() {
helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array<View>) {
const lbl = views[0];
helper.waitUntilLayoutReady(lbl);
const expectedValue = Math.round(<number>lbl.borderRadius * utils.layout.getDisplayDensity());
const actualValue = definition.getUniformNativeCornerRadius(lbl);
TKUnit.assertAreClose(actualValue, expectedValue, 0.01, "borderRadius");
});
};
export function testBorderColor() {
helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array<View>) {
const lbl = views[0];
helper.waitUntilLayoutReady(lbl);
TKUnit.assertEqual(definition.checkUniformNativeBorderColor(lbl), true, "BorderColor not applied correctly!");
TKUnit.assertEqual((<Color>lbl.backgroundColor).hex, expectedBackgroundColor);
});
};

View File

@@ -286,7 +286,7 @@ export function getUniformNativeCornerRadius(v: view.View): number {
export function checkNativeBackgroundColor(v: view.View): boolean {
const bkg = <org.nativescript.widgets.BorderDrawable>(<android.view.View>v.android).getBackground();
return v.backgroundColor && bkg && bkg.getBackgroundColor() === v.backgroundColor.android;
return v.backgroundColor && bkg && bkg.getBackgroundColor() === (<Color>v.backgroundColor).android;
}
export function checkNativeBackgroundImage(v: view.View): boolean {

View File

@@ -37,11 +37,11 @@ export function getUniformNativeCornerRadius(v: view.View): number {
export function checkNativeBackgroundColor(v: view.View): boolean {
if (v.ios instanceof UILabel) {
var cgColor1 = (<UILabel>v.ios).layer.backgroundColor;
var cgColor2 = (<UIColor>v.backgroundColor.ios).CGColor;
var cgColor2 = (<UIColor>(<color.Color>v.backgroundColor).ios).CGColor;
return v.backgroundColor && !!CGColorEqualToColor(cgColor1, cgColor2);
}
return v.backgroundColor && (<UIView>v.ios).backgroundColor.isEqual(v.backgroundColor.ios);
return v.backgroundColor && (<UIView>v.ios).backgroundColor.isEqual((<color.Color>v.backgroundColor).ios);
}
export function checkNativeBackgroundImage(v: view.View): boolean {

View File

@@ -218,7 +218,7 @@ export class ActionBar extends ActionBarBase {
navBar.tintColor = null;
}
let bgColor = this.backgroundColor;
let bgColor = <Color>this.backgroundColor;
navBar.barTintColor = bgColor ? bgColor.ios : null;
}

View File

@@ -168,7 +168,7 @@ export abstract class View extends ViewBase implements ApplyXmlAttributes {
/**
* Gets or sets the background color of the view.
*/
backgroundColor: Color;
backgroundColor: string | Color;
/**
* Gets or sets the background image of the view.

View File

@@ -9,6 +9,12 @@ export * from "./background-common";
interface NativeView extends UIView {
hasNonUniformBorder: boolean;
borderLayer: CAShapeLayer;
hasBorderMask: boolean;
borderOriginalMask: CALayer;
topBorderLayer: CAShapeLayer;
rightBorderLayer: CAShapeLayer;
bottomBorderLayer: CAShapeLayer;
@@ -30,11 +36,13 @@ export module ios {
const background = view.style.backgroundInternal;
const nativeView = <NativeView>view.nativeView;
if (background.hasUniformBorder()) {
if (nativeView.hasNonUniformBorder) {
clearNonUniformBorders(nativeView);
}
if (background.hasUniformBorderColor() && background.hasBorderRadius()) {
drawUniformColorNonUniformBorders(nativeView, background);
} else if (background.hasUniformBorder()) {
const layer = nativeView.layer;
const borderColor = background.getUniformBorderColor();
layer.borderColor = !borderColor ? undefined : borderColor.ios.CGColor;
@@ -42,9 +50,8 @@ export module ios {
const renderSize = view.getActualSize() || { width: 0, height: 0 };
const cornerRadius = layout.toDeviceIndependentPixels(background.getUniformBorderRadius());
layer.cornerRadius = Math.min(Math.min(renderSize.width / 2, renderSize.height / 2), cornerRadius);
}
else {
drawNonUniformBorders(nativeView, background);
} else {
drawNoRadiusNonUniformBorders(nativeView, background);
}
// Clip-path should be called after borders are applied.
@@ -63,6 +70,16 @@ export module ios {
}
function clearNonUniformBorders(nativeView: NativeView): void {
if (nativeView.borderLayer) {
nativeView.borderLayer.removeFromSuperlayer();
}
if (nativeView.hasBorderMask) {
nativeView.layer.mask = nativeView.borderOriginalMask;
nativeView.hasBorderMask = false;
nativeView.borderOriginalMask = null;
}
if (nativeView.topBorderLayer) {
nativeView.topBorderLayer.removeFromSuperlayer();
}
@@ -103,10 +120,12 @@ function setUIColorFromImage(view: View, nativeView: UIView, callback: (uiColor:
if (isDataURI(imageUri)) {
const base64Data = imageUri.split(",")[1];
if (base64Data !== undefined) {
bitmap = fromBase64(base64Data).ios
const imageSource = fromBase64(base64Data);
bitmap = imageSource && imageSource.ios
}
} else if (isFileOrResourcePath(imageUri)) {
bitmap = fromFileOrResource(imageUri).ios;
const imageSource = fromFileOrResource(imageUri);
bitmap = imageSource && imageSource.ios;
} else if (imageUri.indexOf("http") !== -1) {
style[symbolUrl] = imageUri;
fromUrl(imageUri).then((r) => {
@@ -260,13 +279,14 @@ function getDrawParams(this: void, image: UIImage, background: BackgroundDefinit
}
function uiColorFromImage(img: UIImage, view: View, callback: (uiColor: UIColor) => void, flip?: boolean): void {
const background = view.style.backgroundInternal;
if (!img) {
callback(null);
callback(background.color && background.color.ios);
return;
}
const nativeView = view.nativeView as UIView;
const background = view.style.backgroundInternal;
const frame = nativeView.frame;
const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
const boundsHeight = view.scaleY ? frame.size.height / view.scaleY : frame.size.height;
@@ -342,7 +362,151 @@ function cssValueToDeviceIndependentPixels(source: string, total: number): numbe
}
}
function drawNonUniformBorders(nativeView: NativeView, background: BackgroundDefinition) {
function drawUniformColorNonUniformBorders(nativeView: NativeView, background: BackgroundDefinition) {
const layer = nativeView.layer;
layer.backgroundColor = undefined;
layer.borderColor = undefined;
layer.borderWidth = 0;
layer.cornerRadius = 0;
const { width, height } = layer.bounds.size;
const { x, y } = layer.bounds.origin;
const left = x;
const top = y;
const right = x + width;
const bottom = y + height;
const { min, max } = Math;
const borderTopWidth = max(0, layout.toDeviceIndependentPixels(background.borderTopWidth));
const borderRightWidth = max(0, layout.toDeviceIndependentPixels(background.borderRightWidth));
const borderBottomWidth = max(0, layout.toDeviceIndependentPixels(background.borderBottomWidth));
const borderLeftWidth = max(0, layout.toDeviceIndependentPixels(background.borderLeftWidth));
const borderVWidth = borderTopWidth + borderBottomWidth;
const borderHWidth = borderLeftWidth + borderRightWidth;
const cappedBorderTopWidth = borderTopWidth && borderTopWidth * min(1, height / borderVWidth)
const cappedBorderRightWidth = borderRightWidth && borderRightWidth * min(1, width / borderHWidth);
const cappedBorderBottomWidth = borderBottomWidth && borderBottomWidth * min(1, height / borderVWidth);
const cappedBorderLeftWidth = borderLeftWidth && borderLeftWidth * min(1, width / borderHWidth);
const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
const outerTopRightRadius = layout.toDeviceIndependentPixels(background.borderTopRightRadius);
const outerBottomRightRadius = layout.toDeviceIndependentPixels(background.borderBottomRightRadius);
const outerBottomLeftRadius = layout.toDeviceIndependentPixels(background.borderBottomLeftRadius);
const topRadii = outerTopLeftRadius + outerTopRightRadius;
const rightRadii = outerTopRightRadius + outerBottomRightRadius;
const bottomRadii = outerBottomRightRadius + outerBottomLeftRadius;
const leftRadii = outerBottomLeftRadius + outerTopLeftRadius;
function capRadius(a: number, b: number, c: number): number {
return a && Math.min(a, Math.min(b, c));
}
const cappedOuterTopLeftRadius = capRadius(outerTopLeftRadius, outerTopLeftRadius / topRadii * width, outerTopLeftRadius / leftRadii * height);
const cappedOuterTopRightRadius = capRadius(outerTopRightRadius, outerTopRightRadius / topRadii * width, outerTopRightRadius / rightRadii * height);
const cappedOuterBottomRightRadius = capRadius(outerBottomRightRadius, outerBottomRightRadius / bottomRadii * width, outerBottomRightRadius / rightRadii * height);
const cappedOuterBottomLeftRadius = capRadius(outerBottomLeftRadius, outerBottomLeftRadius / bottomRadii * width, outerBottomLeftRadius / leftRadii * height);
// Outer contour
const clipPath = CGPathCreateMutable();
CGPathMoveToPoint(clipPath, null, left + cappedOuterTopLeftRadius, top);
CGPathAddArcToPoint(clipPath, null, right, top, right, top + cappedOuterTopRightRadius, cappedOuterTopRightRadius);
CGPathAddArcToPoint(clipPath, null, right, bottom, right - cappedOuterBottomRightRadius, bottom, cappedOuterBottomRightRadius);
CGPathAddArcToPoint(clipPath, null, left, bottom, left, bottom - cappedOuterBottomLeftRadius, cappedOuterBottomLeftRadius);
CGPathAddArcToPoint(clipPath, null, left, top, left + cappedOuterTopLeftRadius, top, cappedOuterTopLeftRadius);
CGPathCloseSubpath(clipPath);
nativeView.borderOriginalMask = layer.mask;
const clipShapeLayer = CAShapeLayer.layer();
clipShapeLayer.path = clipPath;
layer.mask = clipShapeLayer;
nativeView.hasBorderMask = true;
if (cappedBorderLeftWidth > 0 || cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0 || cappedBorderBottomWidth > 0) {
const borderPath = CGPathCreateMutable();
CGPathAddRect(borderPath, null, CGRectMake(left, top, width, height));
// Inner contour
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
CGPathMoveToPoint(borderPath, null, left + cappedOuterTopLeftRadius, top + cappedBorderTopWidth);
} else {
CGPathMoveToPoint(borderPath, null, left, top);
}
if (cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0) {
const innerTopRightWRadius = max(0, cappedOuterTopRightRadius - cappedBorderRightWidth);
const innerTopRightHRadius = max(0, cappedOuterTopRightRadius - cappedBorderTopWidth);
const innerTopRightMaxRadius = max(innerTopRightWRadius, innerTopRightHRadius);
const innerTopRightTransform: any = CGAffineTransformMake(
innerTopRightMaxRadius && innerTopRightWRadius / innerTopRightMaxRadius, 0,
0, innerTopRightMaxRadius && innerTopRightHRadius / innerTopRightMaxRadius,
right - cappedBorderRightWidth - innerTopRightWRadius, top + cappedBorderTopWidth + innerTopRightHRadius
);
CGPathAddArc(borderPath, innerTopRightTransform, 0, 0, innerTopRightMaxRadius, Math.PI * 3 / 2, 0, false);
} else {
CGPathMoveToPoint(borderPath, null, right, top);
}
if (cappedBorderBottomWidth > 0 || cappedBorderRightWidth > 0) {
const innerBottomRightWRadius = max(0, cappedOuterBottomRightRadius - cappedBorderRightWidth);
const innerBottomRightHRadius = max(0, cappedOuterBottomRightRadius - cappedBorderBottomWidth);
const innerBottomRightMaxRadius = max(innerBottomRightWRadius, innerBottomRightHRadius);
const innerBottomRightTransform: any = CGAffineTransformMake(
innerBottomRightMaxRadius && innerBottomRightWRadius / innerBottomRightMaxRadius, 0,
0, innerBottomRightMaxRadius && innerBottomRightHRadius / innerBottomRightMaxRadius,
right - cappedBorderRightWidth - innerBottomRightWRadius, bottom - cappedBorderBottomWidth - innerBottomRightHRadius
);
CGPathAddArc(borderPath, innerBottomRightTransform, 0, 0, innerBottomRightMaxRadius, 0, Math.PI / 2, false);
} else {
CGPathAddLineToPoint(borderPath, null, right, bottom);
}
if (cappedBorderBottomWidth > 0 || cappedBorderLeftWidth > 0) {
const innerBottomLeftWRadius = max(0, cappedOuterBottomLeftRadius - cappedBorderLeftWidth);
const innerBottomLeftHRadius = max(0, cappedOuterBottomLeftRadius - cappedBorderBottomWidth);
const innerBottomLeftMaxRadius = max(innerBottomLeftWRadius, innerBottomLeftHRadius);
const innerBottomLeftTransform: any = CGAffineTransformMake(
innerBottomLeftMaxRadius && innerBottomLeftWRadius / innerBottomLeftMaxRadius, 0,
0, innerBottomLeftMaxRadius && innerBottomLeftHRadius / innerBottomLeftMaxRadius,
left + cappedBorderLeftWidth + innerBottomLeftWRadius, bottom - cappedBorderBottomWidth - innerBottomLeftHRadius
);
CGPathAddArc(borderPath, innerBottomLeftTransform, 0, 0, innerBottomLeftMaxRadius, Math.PI / 2, Math.PI, false);
} else {
CGPathAddLineToPoint(borderPath, null, left, bottom);
}
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
const innerTopLeftWRadius = max(0, cappedOuterTopLeftRadius - cappedBorderLeftWidth);
const innerTopLeftHRadius = max(0, cappedOuterTopLeftRadius - cappedBorderTopWidth);
const innerTopLeftMaxRadius = max(innerTopLeftWRadius, innerTopLeftHRadius);
const innerTopLeftTransform: any = CGAffineTransformMake(
innerTopLeftMaxRadius && innerTopLeftWRadius / innerTopLeftMaxRadius, 0,
0, innerTopLeftMaxRadius && innerTopLeftHRadius / innerTopLeftMaxRadius,
left + cappedBorderLeftWidth + innerTopLeftWRadius, top + cappedBorderTopWidth + innerTopLeftHRadius
);
CGPathAddArc(borderPath, innerTopLeftTransform, 0, 0, innerTopLeftMaxRadius, Math.PI, Math.PI * 3 / 2, false);
} else {
CGPathAddLineToPoint(borderPath, null, left, top);
}
CGPathCloseSubpath(borderPath);
const borderLayer = CAShapeLayer.layer();
borderLayer.fillColor = background.borderTopColor && background.borderTopColor.ios.CGColor || UIColor.blackColor.CGColor;
borderLayer.fillRule = kCAFillRuleEvenOdd;
borderLayer.path = borderPath;
layer.addSublayer(borderLayer);
nativeView.borderLayer = borderLayer;
}
nativeView.hasNonUniformBorder = true;
}
function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: BackgroundDefinition) {
const layer = nativeView.layer;
layer.borderColor = undefined;
layer.borderWidth = 0;
@@ -378,21 +542,6 @@ function drawNonUniformBorders(nativeView: NativeView, background: BackgroundDef
let hasNonUniformBorder: boolean;
// TODO: This is inefficient.
// We need to know what caused the change and reuse as much as possible.
if (nativeView.topBorderLayer) {
nativeView.topBorderLayer.removeFromSuperlayer();
}
if (nativeView.rightBorderLayer) {
nativeView.rightBorderLayer.removeFromSuperlayer();
}
if (nativeView.bottomBorderLayer) {
nativeView.bottomBorderLayer.removeFromSuperlayer();
}
if (nativeView.leftBorderLayer) {
nativeView.leftBorderLayer.removeFromSuperlayer();
}
const borderTopColor = background.borderTopColor;
if (top > 0 && borderTopColor && borderTopColor.ios) {
const topBorderPath = CGPathCreateMutable();

View File

@@ -246,9 +246,9 @@ export class TextBase extends TextBaseCommon {
}
// We don't use isSet function here because defaultValue for backgroundColor is null.
const backgroundColor = style.backgroundColor
const backgroundColor = <Color>(style.backgroundColor
|| (<FormattedString>span.parent).backgroundColor
|| (<TextBase>(<FormattedString>span.parent).parent).backgroundColor;
|| (<TextBase>(<FormattedString>span.parent).parent).backgroundColor);
if (backgroundColor) {
attrDict[NSBackgroundColorAttributeName] = backgroundColor.ios;
}