diff --git a/apps/app/App_Resources/Android/drawable-nodpi/i18x32.png b/apps/app/App_Resources/Android/drawable-nodpi/i18x32.png new file mode 100644 index 000000000..a23217419 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/i18x32.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/i32x18.png b/apps/app/App_Resources/Android/drawable-nodpi/i32x18.png new file mode 100644 index 000000000..f0ba2f305 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/i32x18.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/i32x32.png b/apps/app/App_Resources/Android/drawable-nodpi/i32x32.png new file mode 100644 index 000000000..0f778f86e Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/i32x32.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/tile.png b/apps/app/App_Resources/Android/drawable-nodpi/tile.png new file mode 100644 index 000000000..7ebde26a0 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/tile.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/up.png b/apps/app/App_Resources/Android/drawable-nodpi/up.png new file mode 100644 index 000000000..04dca794a Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/up.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/upccw.png b/apps/app/App_Resources/Android/drawable-nodpi/upccw.png new file mode 100644 index 000000000..6ad441c51 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/upccw.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/upcw.png b/apps/app/App_Resources/Android/drawable-nodpi/upcw.png new file mode 100644 index 000000000..583e71897 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/upcw.png differ diff --git a/apps/app/App_Resources/Android/drawable-nodpi/upflip.png b/apps/app/App_Resources/Android/drawable-nodpi/upflip.png new file mode 100644 index 000000000..af39cee60 Binary files /dev/null and b/apps/app/App_Resources/Android/drawable-nodpi/upflip.png differ diff --git a/apps/app/App_Resources/iOS/i18x32.png b/apps/app/App_Resources/iOS/i18x32.png new file mode 100644 index 000000000..a23217419 Binary files /dev/null and b/apps/app/App_Resources/iOS/i18x32.png differ diff --git a/apps/app/App_Resources/iOS/i18x32@2x.png b/apps/app/App_Resources/iOS/i18x32@2x.png new file mode 100644 index 000000000..a23217419 Binary files /dev/null and b/apps/app/App_Resources/iOS/i18x32@2x.png differ diff --git a/apps/app/App_Resources/iOS/i18x32@3x.png b/apps/app/App_Resources/iOS/i18x32@3x.png new file mode 100644 index 000000000..a23217419 Binary files /dev/null and b/apps/app/App_Resources/iOS/i18x32@3x.png differ diff --git a/apps/app/App_Resources/iOS/i32x18.png b/apps/app/App_Resources/iOS/i32x18.png new file mode 100644 index 000000000..f0ba2f305 Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x18.png differ diff --git a/apps/app/App_Resources/iOS/i32x18@2x.png b/apps/app/App_Resources/iOS/i32x18@2x.png new file mode 100644 index 000000000..f0ba2f305 Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x18@2x.png differ diff --git a/apps/app/App_Resources/iOS/i32x18@3x.png b/apps/app/App_Resources/iOS/i32x18@3x.png new file mode 100644 index 000000000..f0ba2f305 Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x18@3x.png differ diff --git a/apps/app/App_Resources/iOS/i32x32.png b/apps/app/App_Resources/iOS/i32x32.png new file mode 100644 index 000000000..0f778f86e Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x32.png differ diff --git a/apps/app/App_Resources/iOS/i32x32@2x.png b/apps/app/App_Resources/iOS/i32x32@2x.png new file mode 100644 index 000000000..0f778f86e Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x32@2x.png differ diff --git a/apps/app/App_Resources/iOS/i32x32@3x.png b/apps/app/App_Resources/iOS/i32x32@3x.png new file mode 100644 index 000000000..0f778f86e Binary files /dev/null and b/apps/app/App_Resources/iOS/i32x32@3x.png differ diff --git a/apps/app/App_Resources/iOS/tile.png b/apps/app/App_Resources/iOS/tile.png new file mode 100644 index 000000000..7ebde26a0 Binary files /dev/null and b/apps/app/App_Resources/iOS/tile.png differ diff --git a/apps/app/App_Resources/iOS/tile@2x.png b/apps/app/App_Resources/iOS/tile@2x.png new file mode 100644 index 000000000..7ebde26a0 Binary files /dev/null and b/apps/app/App_Resources/iOS/tile@2x.png differ diff --git a/apps/app/App_Resources/iOS/tile@3x.png b/apps/app/App_Resources/iOS/tile@3x.png new file mode 100644 index 000000000..7ebde26a0 Binary files /dev/null and b/apps/app/App_Resources/iOS/tile@3x.png differ diff --git a/apps/app/App_Resources/iOS/up.png b/apps/app/App_Resources/iOS/up.png new file mode 100644 index 000000000..04dca794a Binary files /dev/null and b/apps/app/App_Resources/iOS/up.png differ diff --git a/apps/app/App_Resources/iOS/up@2x.png b/apps/app/App_Resources/iOS/up@2x.png new file mode 100644 index 000000000..04dca794a Binary files /dev/null and b/apps/app/App_Resources/iOS/up@2x.png differ diff --git a/apps/app/App_Resources/iOS/up@3x.png b/apps/app/App_Resources/iOS/up@3x.png new file mode 100644 index 000000000..04dca794a Binary files /dev/null and b/apps/app/App_Resources/iOS/up@3x.png differ diff --git a/apps/app/App_Resources/iOS/upccw.png b/apps/app/App_Resources/iOS/upccw.png new file mode 100644 index 000000000..6ad441c51 Binary files /dev/null and b/apps/app/App_Resources/iOS/upccw.png differ diff --git a/apps/app/App_Resources/iOS/upccw@2x.png b/apps/app/App_Resources/iOS/upccw@2x.png new file mode 100644 index 000000000..6ad441c51 Binary files /dev/null and b/apps/app/App_Resources/iOS/upccw@2x.png differ diff --git a/apps/app/App_Resources/iOS/upccw@3x.png b/apps/app/App_Resources/iOS/upccw@3x.png new file mode 100644 index 000000000..6ad441c51 Binary files /dev/null and b/apps/app/App_Resources/iOS/upccw@3x.png differ diff --git a/apps/app/App_Resources/iOS/upcw.png b/apps/app/App_Resources/iOS/upcw.png new file mode 100644 index 000000000..583e71897 Binary files /dev/null and b/apps/app/App_Resources/iOS/upcw.png differ diff --git a/apps/app/App_Resources/iOS/upcw@2x.png b/apps/app/App_Resources/iOS/upcw@2x.png new file mode 100644 index 000000000..583e71897 Binary files /dev/null and b/apps/app/App_Resources/iOS/upcw@2x.png differ diff --git a/apps/app/App_Resources/iOS/upcw@3x.png b/apps/app/App_Resources/iOS/upcw@3x.png new file mode 100644 index 000000000..583e71897 Binary files /dev/null and b/apps/app/App_Resources/iOS/upcw@3x.png differ diff --git a/apps/app/App_Resources/iOS/upflip.png b/apps/app/App_Resources/iOS/upflip.png new file mode 100644 index 000000000..af39cee60 Binary files /dev/null and b/apps/app/App_Resources/iOS/upflip.png differ diff --git a/apps/app/App_Resources/iOS/upflip@2x.png b/apps/app/App_Resources/iOS/upflip@2x.png new file mode 100644 index 000000000..af39cee60 Binary files /dev/null and b/apps/app/App_Resources/iOS/upflip@2x.png differ diff --git a/apps/app/App_Resources/iOS/upflip@3x.png b/apps/app/App_Resources/iOS/upflip@3x.png new file mode 100644 index 000000000..af39cee60 Binary files /dev/null and b/apps/app/App_Resources/iOS/upflip@3x.png differ diff --git a/apps/app/ui-tests-app/css/main-page.ts b/apps/app/ui-tests-app/css/main-page.ts index a5bf105c0..85baa75c3 100644 --- a/apps/app/ui-tests-app/css/main-page.ts +++ b/apps/app/ui-tests-app/css/main-page.ts @@ -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; } \ No newline at end of file diff --git a/apps/app/ui-tests-app/css/missing-background-image.css b/apps/app/ui-tests-app/css/missing-background-image.css new file mode 100644 index 000000000..3ae94f0bf --- /dev/null +++ b/apps/app/ui-tests-app/css/missing-background-image.css @@ -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; +} diff --git a/apps/app/ui-tests-app/css/missing-background-image.xml b/apps/app/ui-tests-app/css/missing-background-image.xml new file mode 100644 index 000000000..c6506a036 --- /dev/null +++ b/apps/app/ui-tests-app/css/missing-background-image.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/css/non-uniform-radius.css b/apps/app/ui-tests-app/css/non-uniform-radius.css new file mode 100644 index 000000000..9e3eb6b89 --- /dev/null +++ b/apps/app/ui-tests-app/css/non-uniform-radius.css @@ -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; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/css/non-uniform-radius.xml b/apps/app/ui-tests-app/css/non-uniform-radius.xml new file mode 100644 index 000000000..2f0ca5c65 --- /dev/null +++ b/apps/app/ui-tests-app/css/non-uniform-radius.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/main-page.ts b/apps/app/ui-tests-app/image-view/main-page.ts index d29438eb8..2e714d423 100644 --- a/apps/app/ui-tests-app/image-view/main-page.ts +++ b/apps/app/ui-tests-app/image-view/main-page.ts @@ -13,6 +13,9 @@ export function loadExamples() { const examples = new Map(); 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; } \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/missing-image.xml b/apps/app/ui-tests-app/image-view/missing-image.xml new file mode 100644 index 000000000..9f13b3408 --- /dev/null +++ b/apps/app/ui-tests-app/image-view/missing-image.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/mode-matrix.css b/apps/app/ui-tests-app/image-view/mode-matrix.css new file mode 100644 index 000000000..7d5c0b661 --- /dev/null +++ b/apps/app/ui-tests-app/image-view/mode-matrix.css @@ -0,0 +1,9 @@ +Image { + margin: 1; +} + +GridLayout { + background-image: url("res://tile"); + background-repeat: repeat; + background-size: 78 78; +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/mode-matrix.ts b/apps/app/ui-tests-app/image-view/mode-matrix.ts new file mode 100644 index 000000000..72c73029d --- /dev/null +++ b/apps/app/ui-tests-app/image-view/mode-matrix.ts @@ -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); + } + } +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/mode-matrix.xml b/apps/app/ui-tests-app/image-view/mode-matrix.xml new file mode 100644 index 000000000..873f76983 --- /dev/null +++ b/apps/app/ui-tests-app/image-view/mode-matrix.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/stretch-modes.ts b/apps/app/ui-tests-app/image-view/stretch-modes.ts new file mode 100644 index 000000000..c75ab9a56 --- /dev/null +++ b/apps/app/ui-tests-app/image-view/stretch-modes.ts @@ -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 = (width + "px"); + image.height = (height + "px"); + + image.stretch = stretch; + image.borderColor = "yellow"; + image.margin = "1px"; + (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); + }); +} \ No newline at end of file diff --git a/apps/app/ui-tests-app/image-view/stretch-modes.xml b/apps/app/ui-tests-app/image-view/stretch-modes.xml new file mode 100644 index 000000000..a40448aba --- /dev/null +++ b/apps/app/ui-tests-app/image-view/stretch-modes.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/tests/app/ui/animation/animation-tests.ts b/tests/app/ui/animation/animation-tests.ts index 0efa5b4cb..235114943 100644 --- a/tests/app/ui/animation/animation-tests.ts +++ b/tests/app/ui/animation/animation-tests.ts @@ -303,7 +303,7 @@ export function test_AnimateBackgroundColor(done) { label.animate({ backgroundColor: red, duration: 20 }) .then(() => { - TKUnit.assert(label.backgroundColor.equals(red)); + TKUnit.assert((label.backgroundColor).equals(red)); done(); }) .catch((e) => { @@ -318,7 +318,7 @@ export function test_AnimateBackgroundColor_FromString(done) { label.animate({ backgroundColor: expected, duration: 20 }) .then(() => { - TKUnit.assert(label.backgroundColor.equals(clr)); + TKUnit.assert((label.backgroundColor).equals(clr)); done(); }) .catch((e) => { diff --git a/tests/app/ui/animation/css-animation-tests.ts b/tests/app/ui/animation/css-animation-tests.ts index d43b23a57..e78ec0db6 100644 --- a/tests/app/ui/animation/css-animation-tests.ts +++ b/tests/app/ui/animation/css-animation-tests.ts @@ -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(label.backgroundColor), 1); TKUnit.assertEqual(label.backgroundColor, green); } diff --git a/tests/app/ui/progress/progress-tests.ts b/tests/app/ui/progress/progress-tests.ts index c068b9c2e..4ad5705a8 100644 --- a/tests/app/ui/progress/progress-tests.ts +++ b/tests/app/ui/progress/progress-tests.ts @@ -74,7 +74,7 @@ if (platform.device.os === platform.platformNames.ios) { progress.backgroundColor = new color.Color("red"); function testAction(views: Array) { - TKUnit.assertEqual(progress.backgroundColor.ios.CGColor, progress.ios.trackTintColor.CGColor, "progress.color"); + TKUnit.assertEqual((progress.backgroundColor).ios.CGColor, progress.ios.trackTintColor.CGColor, "progress.color"); }; helper.buildUIAndRunTest(progress, testAction); diff --git a/tests/app/ui/slider/slider-tests.ts b/tests/app/ui/slider/slider-tests.ts index a8e537c2f..7a18954d1 100644 --- a/tests/app/ui/slider/slider-tests.ts +++ b/tests/app/ui/slider/slider-tests.ts @@ -118,7 +118,7 @@ if (isIOS) { slider.backgroundColor = new Color("red"); function testAction(views: Array) { - TKUnit.assertEqual(slider.backgroundColor.ios.CGColor, slider.ios.minimumTrackTintColor.CGColor, "slider.backgroundColor"); + TKUnit.assertEqual((slider.backgroundColor).ios.CGColor, slider.ios.minimumTrackTintColor.CGColor, "slider.backgroundColor"); }; helper.buildUIAndRunTest(slider, testAction); diff --git a/tests/app/ui/styling/style-tests.ts b/tests/app/ui/styling/style-tests.ts index d204830c6..a6c9aa17b 100644 --- a/tests/app/ui/styling/style-tests.ts +++ b/tests/app/ui/styling/style-tests.ts @@ -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((btn.backgroundColor).hex, "#FF0000", "backgroundColor"); TKUnit.assertNull(label.backgroundColor, "backgroundColor should not have a value"); } diff --git a/tests/app/ui/switch/switch-tests.ts b/tests/app/ui/switch/switch-tests.ts index 0d4ead850..6d80f20a7 100644 --- a/tests/app/ui/switch/switch-tests.ts +++ b/tests/app/ui/switch/switch-tests.ts @@ -60,7 +60,7 @@ if (platform.device.os === platform.platformNames.ios) { mySwitch.backgroundColor = new color.Color("red"); function testAction(views: Array) { - TKUnit.assert(CGColorEqualToColor(mySwitch.backgroundColor.ios.CGColor, mySwitch.ios.onTintColor.CGColor), "mySwitch.color"); + TKUnit.assert(CGColorEqualToColor((mySwitch.backgroundColor).ios.CGColor, mySwitch.ios.onTintColor.CGColor), "mySwitch.color"); }; helper.buildUIAndRunTest(mySwitch, testAction); diff --git a/tests/app/ui/view/view-tests-common.ts b/tests/app/ui/view/view-tests-common.ts index 690b65e87..c96016a48 100644 --- a/tests/app/ui/view/view-tests-common.ts +++ b/tests/app/ui/view/view-tests-common.ts @@ -880,35 +880,7 @@ export function testSetInlineStyle() { helper.buildUIAndRunTest(lbl, function (views: Array) { TKUnit.assertEqual(lbl.color.hex, expectedColor); - TKUnit.assertEqual(lbl.backgroundColor.hex, expectedBackgroundColor); - }); -}; - -export function testBorderWidth() { - helper.buildUIAndRunTest(_createLabelWithBorder(), function (views: Array) { - const lbl = views[0]; - helper.waitUntilLayoutReady(lbl); - const expectedValue = Math.round(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) { - const lbl = views[0]; - helper.waitUntilLayoutReady(lbl); - const expectedValue = Math.round(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) { - const lbl = views[0]; - helper.waitUntilLayoutReady(lbl); - TKUnit.assertEqual(definition.checkUniformNativeBorderColor(lbl), true, "BorderColor not applied correctly!"); + TKUnit.assertEqual((lbl.backgroundColor).hex, expectedBackgroundColor); }); }; diff --git a/tests/app/ui/view/view-tests.android.ts b/tests/app/ui/view/view-tests.android.ts index b688227f4..47af9c9b0 100644 --- a/tests/app/ui/view/view-tests.android.ts +++ b/tests/app/ui/view/view-tests.android.ts @@ -286,7 +286,7 @@ export function getUniformNativeCornerRadius(v: view.View): number { export function checkNativeBackgroundColor(v: view.View): boolean { const bkg = (v.android).getBackground(); - return v.backgroundColor && bkg && bkg.getBackgroundColor() === v.backgroundColor.android; + return v.backgroundColor && bkg && bkg.getBackgroundColor() === (v.backgroundColor).android; } export function checkNativeBackgroundImage(v: view.View): boolean { diff --git a/tests/app/ui/view/view-tests.ios.ts b/tests/app/ui/view/view-tests.ios.ts index d83f943ab..d8c442cbf 100644 --- a/tests/app/ui/view/view-tests.ios.ts +++ b/tests/app/ui/view/view-tests.ios.ts @@ -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 = (v.ios).layer.backgroundColor; - var cgColor2 = (v.backgroundColor.ios).CGColor; + var cgColor2 = ((v.backgroundColor).ios).CGColor; return v.backgroundColor && !!CGColorEqualToColor(cgColor1, cgColor2); } - return v.backgroundColor && (v.ios).backgroundColor.isEqual(v.backgroundColor.ios); + return v.backgroundColor && (v.ios).backgroundColor.isEqual((v.backgroundColor).ios); } export function checkNativeBackgroundImage(v: view.View): boolean { diff --git a/tns-core-modules/ui/action-bar/action-bar.ios.ts b/tns-core-modules/ui/action-bar/action-bar.ios.ts index 3d8f68438..6b7102d34 100644 --- a/tns-core-modules/ui/action-bar/action-bar.ios.ts +++ b/tns-core-modules/ui/action-bar/action-bar.ios.ts @@ -218,7 +218,7 @@ export class ActionBar extends ActionBarBase { navBar.tintColor = null; } - let bgColor = this.backgroundColor; + let bgColor = this.backgroundColor; navBar.barTintColor = bgColor ? bgColor.ios : null; } diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index 725d83f32..9af4011cd 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -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. diff --git a/tns-core-modules/ui/styling/background.ios.ts b/tns-core-modules/ui/styling/background.ios.ts index 3aadcd891..455083352 100644 --- a/tns-core-modules/ui/styling/background.ios.ts +++ b/tns-core-modules/ui/styling/background.ios.ts @@ -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 = 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(); diff --git a/tns-core-modules/ui/text-base/text-base.ios.ts b/tns-core-modules/ui/text-base/text-base.ios.ts index 2d85ae595..d018f94cb 100644 --- a/tns-core-modules/ui/text-base/text-base.ios.ts +++ b/tns-core-modules/ui/text-base/text-base.ios.ts @@ -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 = (style.backgroundColor || (span.parent).backgroundColor - || ((span.parent).parent).backgroundColor; + || ((span.parent).parent).backgroundColor); if (backgroundColor) { attrDict[NSBackgroundColorAttributeName] = backgroundColor.ios; }