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
BIN
apps/app/App_Resources/Android/drawable-nodpi/i18x32.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/i32x18.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/i32x32.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/tile.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/up.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/upccw.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/upcw.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/Android/drawable-nodpi/upflip.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
apps/app/App_Resources/iOS/i18x32.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/i18x32@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/i18x32@3x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/i32x18.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/i32x18@2x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/i32x18@3x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/i32x32.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/i32x32@2x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/i32x32@3x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
apps/app/App_Resources/iOS/tile.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/tile@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/tile@3x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/app/App_Resources/iOS/up.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
apps/app/App_Resources/iOS/up@2x.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
apps/app/App_Resources/iOS/up@3x.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
apps/app/App_Resources/iOS/upccw.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upccw@2x.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upccw@3x.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upcw.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upcw@2x.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upcw@3x.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/app/App_Resources/iOS/upflip.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
apps/app/App_Resources/iOS/upflip@2x.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
apps/app/App_Resources/iOS/upflip@3x.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
@@ -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;
|
||||
}
|
||||
31
apps/app/ui-tests-app/css/missing-background-image.css
Normal 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;
|
||||
}
|
||||
23
apps/app/ui-tests-app/css/missing-background-image.xml
Normal 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>
|
||||
64
apps/app/ui-tests-app/css/non-uniform-radius.css
Normal 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;
|
||||
}
|
||||
27
apps/app/ui-tests-app/css/non-uniform-radius.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
23
apps/app/ui-tests-app/image-view/missing-image.xml
Normal 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>
|
||||
9
apps/app/ui-tests-app/image-view/mode-matrix.css
Normal file
@@ -0,0 +1,9 @@
|
||||
Image {
|
||||
margin: 1;
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
background-image: url("res://tile");
|
||||
background-repeat: repeat;
|
||||
background-size: 78 78;
|
||||
}
|
||||
57
apps/app/ui-tests-app/image-view/mode-matrix.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
apps/app/ui-tests-app/image-view/mode-matrix.xml
Normal 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>
|
||||
52
apps/app/ui-tests-app/image-view/stretch-modes.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
7
apps/app/ui-tests-app/image-view/stretch-modes.xml
Normal 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>
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
2
tns-core-modules/ui/core/view/view.d.ts
vendored
@@ -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.
|
||||
|
||||
@@ -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 (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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||