diff --git a/tests/app/ui/button/button-tests-native.android.ts b/tests/app/ui/button/button-tests-native.android.ts index ee1059502..078380fa4 100644 --- a/tests/app/ui/button/button-tests-native.android.ts +++ b/tests/app/ui/button/button-tests-native.android.ts @@ -23,8 +23,8 @@ export function getNativeColor(button: buttonModule.Button): colorModule.Color { export function getNativeBackgroundColor(button: buttonModule.Button): colorModule.Color { var bkg = button.android.getBackground(); - if (bkg instanceof background.ad.BorderDrawable) { - return (bkg).background.color; + if (bkg instanceof org.nativescript.widgets.BorderDrawable) { + return new colorModule.Color((bkg).getBackgroundColor()); } else { return new colorModule.Color(bkg.backgroundColor) diff --git a/tests/app/ui/label/label-tests-native.android.ts b/tests/app/ui/label/label-tests-native.android.ts index ee0956851..23f4f2239 100644 --- a/tests/app/ui/label/label-tests-native.android.ts +++ b/tests/app/ui/label/label-tests-native.android.ts @@ -23,8 +23,8 @@ export function getNativeTextAlignment(label: labelModule.Label): string { export function getNativeBackgroundColor(label: labelModule.Label): colorModule.Color { var bkg = label.android.getBackground(); - if (bkg instanceof background.ad.BorderDrawable) { - return (bkg).background.color; + if (bkg instanceof org.nativescript.widgets.BorderDrawable) { + return new colorModule.Color((bkg).getBackgroundColor()); } else { return new colorModule.Color(bkg.backgroundColor) diff --git a/tests/app/ui/label/label-tests.ts b/tests/app/ui/label/label-tests.ts index faa1e8184..e83cb1e5e 100644 --- a/tests/app/ui/label/label-tests.ts +++ b/tests/app/ui/label/label-tests.ts @@ -261,8 +261,8 @@ export class LabelTest extends testModule.UITest { normalColor = actualColors.getDefaultColor() TKUnit.assert(normalColor, "Expected: " + expColor + ", Actual: " + normalColor); - var bkg = (testLabel.android.getBackground()); - actualBackgroundColor = bkg.background.color.android; + var bkg = (testLabel.android.getBackground()); + actualBackgroundColor = bkg.getBackgroundColor(); expBackgroundColor = android.graphics.Color.parseColor(backgroundColor); TKUnit.assertEqual(actualBackgroundColor, expBackgroundColor); } diff --git a/tests/app/ui/text-field/text-field-tests-native.android.ts b/tests/app/ui/text-field/text-field-tests-native.android.ts index 235d83a19..f61286b80 100644 --- a/tests/app/ui/text-field/text-field-tests-native.android.ts +++ b/tests/app/ui/text-field/text-field-tests-native.android.ts @@ -28,8 +28,8 @@ export function getNativeColor(textField: textFieldModule.TextField): colorModul export function getNativeBackgroundColor(textField: textFieldModule.TextField): colorModule.Color { var bkg = textField.android.getBackground(); - if (bkg instanceof background.ad.BorderDrawable) { - return (bkg).background.color; + if (bkg instanceof org.nativescript.widgets.BorderDrawable) { + return new colorModule.Color((bkg).getBackgroundColor()); } else { return new colorModule.Color(bkg.backgroundColor) diff --git a/tests/app/ui/text-view/text-view-tests-native.android.ts b/tests/app/ui/text-view/text-view-tests-native.android.ts index f5ea9a7e9..d53e3bd99 100644 --- a/tests/app/ui/text-view/text-view-tests-native.android.ts +++ b/tests/app/ui/text-view/text-view-tests-native.android.ts @@ -32,8 +32,8 @@ export function getNativeColor(textView: textViewModule.TextView): colorModule.C export function getNativeBackgroundColor(textView: textViewModule.TextView): colorModule.Color { var bkg = textView.android.getBackground(); - if (bkg instanceof background.ad.BorderDrawable) { - return (bkg).background.color; + if (bkg instanceof org.nativescript.widgets.BorderDrawable) { + return new colorModule.Color((bkg).getBackgroundColor()); } else { return new colorModule.Color(bkg.backgroundColor) diff --git a/tests/app/ui/view/view-tests.android.ts b/tests/app/ui/view/view-tests.android.ts index 94e8e873b..13e7cb1e1 100644 --- a/tests/app/ui/view/view-tests.android.ts +++ b/tests/app/ui/view/view-tests.android.ts @@ -269,33 +269,33 @@ export var test_StylePropertiesDefaultValuesCache = function () { } export function getNativeBorderWidth(v: view.View): number { - var bkg = (v.android).getBackground(); + var bkg = v.android.getBackground(); - return bkg ? bkg.borderWidth : -1; + return bkg ? bkg.getBorderWidth() : -1; } export function getNativeCornerRadius(v: view.View): number { - var bkg = (v.android).getBackground(); + var bkg = v.android.getBackground(); - return bkg ? bkg.cornerRadius : -1; + return bkg ? bkg.getBorderRadius() : -1; } export function checkNativeBorderColor(v: view.View): boolean { - var bkg = (v.android).getBackground(); + var bkg = (v.android).getBackground(); - return v.borderColor && bkg && bkg.borderColor === v.borderColor.android; + return v.borderColor && bkg && bkg.getBorderColor() === v.borderColor.android; } export function checkNativeBackgroundColor(v: view.View): boolean { - var bkg = (v.android).getBackground(); + var bkg = (v.android).getBackground(); - return v.backgroundColor && bkg && bkg.background && bkg.background.color.equals(v.backgroundColor); + return v.backgroundColor && bkg && bkg.getBackgroundColor() === v.backgroundColor.android; } export function checkNativeBackgroundImage(v: view.View): boolean { - var bkg = (v.android).getBackground(); + var bkg = (v.android).getBackground(); - return bkg && bkg.background && !types.isNullOrUndefined(bkg.background.image); + return bkg && !types.isNullOrUndefined(bkg.getBackgroundImage()); } let SDK: number; diff --git a/tns-core-modules/org.nativescript.widgets.d.ts b/tns-core-modules/org.nativescript.widgets.d.ts index c78583efb..8d2766208 100644 --- a/tns-core-modules/org.nativescript.widgets.d.ts +++ b/tns-core-modules/org.nativescript.widgets.d.ts @@ -1,6 +1,44 @@ declare module org { module nativescript { module widgets { + export class BorderDrawable extends android.graphics.drawable.ColorDrawable { + constructor(density: number); + public refresh( + borderWidth: number, + borderColor: number, + borderRadius: number, + clipPath: string, + backgroundColor: number, + backgroundImage: android.graphics.Bitmap, + backgroundImageWidth: number, + backgroundImageHeight: number, + backgroundRepeat: string, + backgroundPosition: string, + backgroundPositionParsedCSSValues: native.Array, + backgroundSize: string, + backgroundSizeParsedCSSValues: native.Array + ); + public getBorderWidth(): number; + public getBorderColor(): number; + public getBorderRadius(): number; + public getClipPath(): string; + public getBackgroundColor(): number; + public getBackgroundImage(): android.graphics.Bitmap; + public getBackgroundImageWidth(): number; + public getBackgroundImageHeight(): number; + public getBackgroundRepeat(): string; + public getBackgroundPosition(): string; + public getBackgroundSize(): string; + } + + export class CSSValue { + constructor(type: string, str: string, unit: string, value: number); + public getType(): string; + public getString(): string; + public getUnit(): string; + public getValue(): number; + } + export class CommonLayoutParams extends android.widget.FrameLayout.LayoutParams { constructor(); diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index e31168305..d67b39ea7 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -6,19 +6,7 @@ import types = require("utils/types"); import * as styleModule from "./style"; import * as buttonModule from "ui/button"; import { CacheLayerType } from "utils/utils"; - -//@private -declare module "ui/styling/background" { - // We are using "ad" here to avoid namespace collision with the global android object - export module ad { - export class BorderDrawable extends android.graphics.drawable.ColorDrawable { - borderWidth: number; - cornerRadius: number; - borderColor: number; - background: Background; - } - } -} +import cssValue = require("css-value"); var button: typeof buttonModule; var style: typeof styleModule; @@ -37,197 +25,6 @@ global.moduleMerge(common, exports); // We are using "ad" here to avoid namespace collision with the global android object export module ad { - Object.defineProperty(ad, "BorderDrawable", { - get: function () { - ensureBorderDrawable(); - return BorderDrawableClass; - }, - configurable: true - }); - - var BorderDrawableClass; - function ensureBorderDrawable() { - if (BorderDrawableClass) { - return; - } - - class BorderDrawable extends android.graphics.drawable.ColorDrawable implements definition.ad.BorderDrawable { - private _density = utils.layout.getDisplayDensity(); - private _borderWidth: number; - private _cornerRadius: number; - private _borderColor: number; - private _clipPath: string; - - constructor() { - super(); - return global.__native(this); - } - - get clipPath(): string { - return this._clipPath; - } - set clipPath(value: string) { - if (this._clipPath !== value) { - this._clipPath = value; - this.invalidateSelf(); - } - } - - get borderWidth(): number { - return this._borderWidth; - } - set borderWidth(value: number) { - if (this._borderWidth !== value) { - this._borderWidth = value; - this.invalidateSelf(); - } - } - - get cornerRadius(): number { - return this._cornerRadius; - } - set cornerRadius(value: number) { - if (this._cornerRadius !== value) { - this._cornerRadius = value; - this.invalidateSelf(); - } - } - - get borderColor(): number { - return this._borderColor; - } - set borderColor(value: number) { - if (this._borderColor !== value) { - this._borderColor = value; - this.invalidateSelf(); - } - } - - private _background: common.Background - get background(): common.Background { - return this._background; - } - set background(value: common.Background) { - if (this._background !== value) { - this._background = value; - this.invalidateSelf(); - } - } - - public draw(canvas: android.graphics.Canvas): void { - let bounds = this.getBounds(); - let borderWidth = this._borderWidth * this._density; - let halfBorderWidth = borderWidth / 2; - - // We will inset background colors and images so antialiasing will not color pixels outside the border. - // If the border is transparent we will backoff less, and we will not backoff more than half a pixel or half the border width. - let normalizedBorderAlpha = android.graphics.Color.alpha(this._borderColor) / 255; - let backoffAntialias = Math.min(0.5, halfBorderWidth) * normalizedBorderAlpha; - let backgroundBoundsF = new android.graphics.RectF(bounds.left + backoffAntialias, bounds.top + backoffAntialias, bounds.right - backoffAntialias, bounds.bottom - backoffAntialias); - - let outerRadius = this._cornerRadius * this._density; - - // draw background - if (this.background.color && this.background.color.android) { - let backgroundColorPaint = new android.graphics.Paint(); - backgroundColorPaint.setStyle(android.graphics.Paint.Style.FILL); - backgroundColorPaint.setColor(this.background.color.android); - backgroundColorPaint.setAntiAlias(true); - - if (this.clipPath) { - drawClipPath(this.clipPath, canvas, backgroundColorPaint, backgroundBoundsF); - } else { - canvas.drawRoundRect(backgroundBoundsF, outerRadius, outerRadius, backgroundColorPaint); - } - } - - // draw image - if (this.background.image) { - let bitmap = this.background.image.android; - let params = this.background.getDrawParams(bounds.width(), bounds.height()); - - let transform = new android.graphics.Matrix(); - if (params.sizeX > 0 && params.sizeY > 0) { - let scaleX = params.sizeX / bitmap.getWidth(); - let scaleY = params.sizeY / bitmap.getHeight(); - transform.setScale(scaleX, scaleY, 0, 0); - } else { - params.sizeX = bitmap.getWidth(); - params.sizeY = bitmap.getHeight(); - } - transform.postTranslate(params.posX - backoffAntialias, params.posY - backoffAntialias); - - let shader = new android.graphics.BitmapShader(bitmap, android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT); - shader.setLocalMatrix(transform); - - let backgroundImagePaint = new android.graphics.Paint(); - backgroundImagePaint.setShader(shader); - - let imageWidth = params.repeatX ? bounds.width() : params.sizeX; - let imageHeight = params.repeatY ? bounds.height() : params.sizeY; - params.posX = params.repeatX ? 0 : params.posX; - params.posY = params.repeatY ? 0 : params.posY; - - if (this.clipPath) { - drawClipPath(this.clipPath, canvas, backgroundImagePaint, backgroundBoundsF); - } else { - let supportsPathOp = android.os.Build.VERSION.SDK_INT >= 19; - if (supportsPathOp) { - // Path.Op can be used in API level 19+ to achieve the perfect geometry. - let backgroundPath = new android.graphics.Path(); - backgroundPath.addRoundRect(backgroundBoundsF, outerRadius, outerRadius, android.graphics.Path.Direction.CCW); - let backgroundNoRepeatPath = new android.graphics.Path(); - backgroundNoRepeatPath.addRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight, android.graphics.Path.Direction.CCW); - (backgroundPath).op(backgroundNoRepeatPath, (android).graphics.Path.Op.INTERSECT); - canvas.drawPath(backgroundPath, backgroundImagePaint); - } else { - // Clipping here will not be antialiased but at least it won't shine through the rounded corners. - canvas.save(); - canvas.clipRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight); - canvas.drawRoundRect(backgroundBoundsF, outerRadius, outerRadius, backgroundImagePaint); - canvas.restore(); - } - } - } - - // draw border - if (borderWidth > 0 && this._borderColor) { - let middleBoundsF = new android.graphics.RectF(bounds.left + halfBorderWidth, bounds.top + halfBorderWidth, bounds.right - halfBorderWidth, bounds.bottom - halfBorderWidth); - let borderPaint = new android.graphics.Paint(); - borderPaint.setColor(this._borderColor); - borderPaint.setAntiAlias(true); - - if (this.clipPath) { - borderPaint.setStyle(android.graphics.Paint.Style.STROKE); - borderPaint.setStrokeWidth(borderWidth); - drawClipPath(this.clipPath, canvas, borderPaint, backgroundBoundsF); - } else { - if (outerRadius <= 0) { - borderPaint.setStyle(android.graphics.Paint.Style.STROKE); - borderPaint.setStrokeWidth(borderWidth); - canvas.drawRect(middleBoundsF, borderPaint); - } else if (outerRadius >= borderWidth) { - borderPaint.setStyle(android.graphics.Paint.Style.STROKE); - borderPaint.setStrokeWidth(borderWidth); - let middleRadius = Math.max(0, outerRadius - halfBorderWidth); - canvas.drawRoundRect(middleBoundsF, middleRadius, middleRadius, borderPaint); - } else { - let borderPath = new android.graphics.Path(); - let borderOuterBoundsF = new android.graphics.RectF(bounds.left, bounds.top, bounds.right, bounds.bottom); - borderPath.addRoundRect(borderOuterBoundsF, outerRadius, outerRadius, android.graphics.Path.Direction.CCW); - let borderInnerBoundsF = new android.graphics.RectF(bounds.left + borderWidth, bounds.top + borderWidth, bounds.right - borderWidth, bounds.bottom - borderWidth); - borderPath.addRect(borderInnerBoundsF, android.graphics.Path.Direction.CW); - borderPaint.setStyle(android.graphics.Paint.Style.FILL); - canvas.drawPath(borderPath, borderPaint); - } - } - } - } - } - - BorderDrawableClass = BorderDrawable; - } - var SDK: number; function getSDK() { if (!SDK) { @@ -247,26 +44,27 @@ export module ad { return; } - ensureBorderDrawable(); ensureLazyRequires(); var clipPathValue = v.style._getValue(style.clipPathProperty); - var backgroundValue = v.style._getValue(style.backgroundInternalProperty); + var backgroundValue = v.style._getValue(style.backgroundInternalProperty); var borderWidth = v.borderWidth; - var bkg = nativeView.getBackground(); + var bkg = nativeView.getBackground(); + + var density = utils.layout.getDisplayDensity(); if (v instanceof button.Button && !types.isNullOrUndefined(bkg) && types.isFunction(bkg.setColorFilter) && v.borderWidth === 0 && v.borderRadius === 0 && !clipPathValue && types.isNullOrUndefined(v.style._getValue(style.backgroundImageProperty)) && !types.isNullOrUndefined(v.style._getValue(style.backgroundColorProperty))) { - let backgroundColor = bkg.backgroundColor = v.style._getValue(style.backgroundColorProperty).android; + let backgroundColor = (bkg).backgroundColor = v.style._getValue(style.backgroundColorProperty).android; bkg.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); - bkg.backgroundColor = backgroundColor; + (bkg).backgroundColor = backgroundColor; } else if (v.borderWidth || v.borderRadius || clipPathValue || !backgroundValue.isEmpty()) { - if (!(bkg instanceof BorderDrawableClass)) { - bkg = new BorderDrawableClass(); + if (!(bkg instanceof org.nativescript.widgets.BorderDrawable)) { + bkg = new org.nativescript.widgets.BorderDrawable(density); let viewClass = types.getClass(v); if (!(v instanceof button.Button) && !_defaultBackgrounds.has(viewClass)) { _defaultBackgrounds.set(viewClass, nativeView.getBackground()); @@ -274,12 +72,22 @@ export module ad { nativeView.setBackground(bkg); } - - bkg.borderWidth = v.borderWidth; - bkg.cornerRadius = v.borderRadius; - bkg.borderColor = v.borderColor ? v.borderColor.android : android.graphics.Color.TRANSPARENT; - bkg.background = backgroundValue; - bkg.clipPath = clipPathValue; + + (bkg).refresh( + v.borderWidth, + v.borderColor ? v.borderColor.android : 0, + v.borderRadius, + clipPathValue, + (backgroundValue.color && backgroundValue.color.android) ? backgroundValue.color.android : 0, + (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.android : null, + (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.width : 0, + (backgroundValue.image && backgroundValue.image.android) ? backgroundValue.image.height : 0, + backgroundValue.repeat, + backgroundValue.position, + backgroundValue.position ? createNativeCSSValueArray(backgroundValue.position) : null, + backgroundValue.size, + backgroundValue.size ? createNativeCSSValueArray(backgroundValue.size) : null + ); if ((v.borderWidth || v.borderRadius || clipPathValue) && getSDK() < 18) { // Switch to software because of unsupported canvas methods if hardware acceleration is on: @@ -307,7 +115,6 @@ export module ad { } } - var density = utils.layout.getDisplayDensity(); nativeView.setPadding( Math.round((borderWidth + v.style.paddingLeft) * density), Math.round((borderWidth + v.style.paddingTop) * density), @@ -317,6 +124,23 @@ export module ad { } } +function createNativeCSSValueArray(css: string): any{ + if (!css){ + return null; + } + let cssValues = cssValue(css); + let nativeArray = (Array).create(org.nativescript.widgets.CSSValue, cssValues.length); + for (let i = 0, length = cssValues.length; i < length; i++){ + nativeArray[i] = new org.nativescript.widgets.CSSValue( + cssValues[i].type, + cssValues[i].string, + cssValues[i].unit, + cssValues[i].value + ); + } + return nativeArray; +} + function drawClipPath(clipPath: string, canvas: android.graphics.Canvas, paint: android.graphics.Paint, bounds: android.graphics.RectF) { var functionName = clipPath.substring(0, clipPath.indexOf("(")); var value = clipPath.replace(`${functionName}(`, "").replace(")", "");