Skip to content

Commit

Permalink
Css gradients (#5534)
Browse files Browse the repository at this point in the history
* feat(ios): Added support for css gradients.

* feat(android): Added support for css gradients.

* fix: Fixed gradient borders on ios

* fix(gradient): added backgroundGradient to View and Style.

* fix(ios-gradients): fixed ios gradients covering view content.

* test(gradient): Added ui app tests for background gradients.

* test(gradient): Added a test ensuring background gradient property is applied to style.

* style(gradient): Fixed tslint errors.

* fix(gradient): Removed the background-gradient property and added the gradient to background-image.

* style: fixed a consecutive blank line tslint error.

* fix(tests): fixed the bug that was causing tests to fail.

* chore(linear-gradient): fix equality comparer

* test(gradient): add linear gradients test app

* chore(tslint): update with latest tslint rules
  • Loading branch information
vultix authored and SvetoslavTsenov committed May 3, 2018
1 parent 72fa5c9 commit 5a83a1c
Show file tree
Hide file tree
Showing 19 changed files with 397 additions and 24 deletions.
5 changes: 5 additions & 0 deletions apps/app/ui-tests-app/button/border-playground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export function onToggle(args: EventData) {
target.backgroundImage = target.backgroundImage ? undefined : `~/ui-tests-app/resources/images/test2.png`;
debugConsole.text += `> background-image: ${target.backgroundImage}\n`;
}
else if (button.text === "BGGradient") {
const gradient = "linear-gradient(to right, purple, red)";
target.backgroundImage = typeof target.backgroundImage === "object" ? undefined : gradient;
debugConsole.text += `> background-image: ${gradient} \n`;
}

scrollView.scrollToVerticalOffset(scrollView.scrollableHeight, true);
}
1 change: 1 addition & 0 deletions apps/app/ui-tests-app/button/border-playground.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<StackLayout id="target" row="1" col="1">
<Button text="BGColor" tap="onToggle"/>
<Button text="BGImage" tap="onToggle"/>
<Button text="BGGradient" tap="onToggle"/>
</StackLayout>
<StackLayout id="right" class="button-container" row="1" col="2">
<Button text="Color" id="borderRightColor" tap="onToggle"/>
Expand Down
41 changes: 41 additions & 0 deletions apps/app/ui-tests-app/css/background-image-linear-gradient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as pages from "tns-core-modules/ui/page";
import { EventData } from "tns-core-modules/data/observable";
import * as button from "tns-core-modules/ui/button";

import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout";

let testIndex = 0;
const tests = [
{ name: "black-blue only", backgroundImage: "linear-gradient(to bottom, black, blue)"},
{ name: "to bottom green-blue", backgroundImage: "linear-gradient(to bottom, green, blue)"},
{ name: "to left yellow-blue", backgroundImage: "linear-gradient(to left, yellow, green)"},
{ name: "to right yellow-blue", backgroundImage: "linear-gradient(to right, yellow, green)"},
{ name: "-45deg green-blue", backgroundImage: "linear-gradient(-45deg, green, blue)"},
{ name: "45deg green-blue", backgroundImage: "linear-gradient(45deg, green, blue)"},

{ name: "black-blue-pink only", backgroundImage: "linear-gradient(to bottom, black, blue, pink)"},
{ name: "to bottom green-blue-pink", backgroundImage: "linear-gradient(to bottom, green, blue, pink)"},
{ name: "to left yellow-blue-pink", backgroundImage: "linear-gradient(to left, yellow, green, pink)"},
{ name: "to right yellow-blue-pink", backgroundImage: "linear-gradient(to right, yellow, green, pink)"},
{ name: "-45deg green-blue-pink", backgroundImage: "linear-gradient(-45deg, green, blue, pink)"},
{ name: "45deg green-blue-pink", backgroundImage: "linear-gradient(45deg, green, blue, pink)"},
]

export function onLoaded(args) {
applyNextStyle(args);
}

export function onButtonTap(args) {
applyNextStyle(args);
}

function applyNextStyle(args) {
let page = <pages.Page>args.object.page;
let btn = <button.Button>args.object;
let gridElement = <GridLayout>page.getViewById("Container");

btn.text = tests[testIndex].name;
gridElement.backgroundImage = tests[testIndex].backgroundImage;

testIndex = testIndex < tests.length - 1 ? ++testIndex : 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
<GridLayout rows="*, 7*">
<Button row="0" text="a1" tap="onButtonTap"/>

<GridLayout id="Container" fontSize="12" borderColor="black" margin="5" borderWidth="1" row="1"/>
</GridLayout>
</Page>
4 changes: 3 additions & 1 deletion apps/app/ui-tests-app/css/background-shorthand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const tests = [
{ name: "black hex color only", background: "#000000" },
{ name: "yellow hex color only", background: "#ffff00" },
{ name: "blue color only", background: "blue" },

{ name: "repeat image only", background: "url(\"~/ui-tests-app/resources/images/icon.png\")" },
{ name: "linear gradient red-blue only", background: "linear-gradient(to bottom, red, blue)"},

{ name: "[straight] image only, no-repeat ", background: "url(\"~/ui-tests-app/resources/images/icon.png\") no-repeat" },
{ name: "[straight] green color, image, no-repeat", background: "green url(\"~/ui-tests-app/resources/images/icon.png\") no-repeat" },
Expand All @@ -18,7 +20,7 @@ const tests = [
{ name: "[straight] orange hex color, image, no-repeat, position percents", background: "#F9791F url(\"~/ui-tests-app/resources/images/icon.png\") no-repeat 100% 100%" },
{ name: "[straight] green color, image, repeat-x, position percents", background: "green url(\"~/ui-tests-app/resources/images/icon.png\") repeat-x 100% 100%" },
{ name: "[straight] blue color, image, repeat-x, position", background: "blue url(\"~/ui-tests-app/resources/images/icon.png\") repeat-x 150 150" },

{ name: "[shuffle] no-repeat, image only", background: "no-repeat url(\"~/ui-tests-app/resources/images/icon.png\")" },
{ name: "[shuffle] no-repeat, green color, image, ", background: "no-repeat green url(\"~/ui-tests-app/resources/images/icon.png\")" },
{ name: "[shuffle] yellow hex color, position pixels, image, no-repeat", background: "#ffff00 200px 200px url(\"~/ui-tests-app/resources/images/icon.png\") no-repeat" },
Expand Down
77 changes: 77 additions & 0 deletions apps/app/ui-tests-app/css/gradient-border.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
StackLayout {
width: 80;
height: 80;
background: linear-gradient(to top, green, blue);
}

#s0 {
border-width: 5;
}

#s1 {
border-width: 5; border-color: red;
}

#s2 {
border-width: 5; border-color: red red red green;
}

#s3 {
border-width: 5; border-color: red; border-radius: 5;
}

#s4 {
border-width: 5; border-color: red; border-radius: 50;
}

#s5 {
border-width: 5 10 15 20; border-color: red;
}

#s6 {
border-width: 5; border-color: red green blue yellow;
}

#s7 {
border-width: 5 10 15 20; border-color: red green blue yellow;
}

#s8 {
border-width: 5 10; border-color: red green;
}

#s9 {
border-width: 15 10 5; border-color: red green blue;
}

#s10 {
border-width: 5 0; border-color: black;
}

#s11 {
background-color: magenta;
}

#s12 {
border-width: 5 10 15 20; border-color: red green blue yellow; border-radius: 5 10 15 20;
}

#s13 {
border-width: 5 10 15 20; border-color: red green blue yellow; border-radius: 5;
}

#s14 {
border-width: 5 10 15 20; border-color: red green blue yellow; background-color: magenta;
}

#s15 {
border-width: 5 10 15 20; border-color: red green blue yellow; background-image: url('~/ui-tests-app/resources/images/test2.png');
}

#s16 {
border-width: 5; border-color: red; padding: 5;
}

#s17 {
border-width: 5 6 7 8; border-color: red green blue yellow; padding: 5 6 7 8;
}
22 changes: 22 additions & 0 deletions apps/app/ui-tests-app/css/gradient-border.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Page>
<GridLayout rows="*,*,*,*,*,*" columns="*,*,*">
<StackLayout id="s0" row="0" col="0"/>
<StackLayout id="s1" row="0" col="1"/>
<StackLayout id="s2" row="0" col="2"/>
<StackLayout id="s3" row="1" col="0"/>
<StackLayout id="s4" row="1" col="1"/>
<StackLayout id="s5" row="1" col="2"/>
<StackLayout id="s6" row="2" col="0"/>
<StackLayout id="s7" row="2" col="1"/>
<StackLayout id="s8" row="2" col="2"/>
<StackLayout id="s9" row="3" col="0"/>
<StackLayout id="s10" row="3" col="1"/>
<StackLayout id="s11" row="3" col="2"/>
<StackLayout id="s12" row="4" col="0"/>
<StackLayout id="s13" row="4" col="1"/>
<StackLayout id="s14" row="4" col="2"/>
<StackLayout id="s15" row="5" col="0"/>
<StackLayout id="s16" row="5" col="1"/>
<StackLayout id="s17" row="5" col="2"/>
</GridLayout>
</Page>
2 changes: 2 additions & 0 deletions apps/app/ui-tests-app/css/main-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function loadExamples() {
examples.set("label-border", "css/label-border");
examples.set("text-view-border", "css/text-view-border");
examples.set("image-border", "css/image-border");
examples.set("gradient-border", "css/gradient-border");
examples.set("layouts-border-overlap", "css/layouts-border-overlap");
examples.set("measure-tests", "css/measure-tests");
examples.set("all-uniform-border", "css/all-uniform-border");
Expand All @@ -41,5 +42,6 @@ export function loadExamples() {
examples.set("non-uniform-radius", "css/non-uniform-radius");
examples.set("missing-background-image", "css/missing-background-image");
examples.set("background-shorthand", "css/background-shorthand");
examples.set("background-image-linear-gradient", "css/background-image-linear-gradient");
return examples;
}
6 changes: 4 additions & 2 deletions tns-core-modules/ui/core/view/view-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import {

import { createViewFromEntry } from "../../builder";
import { StyleScope } from "../../styling/style-scope";
import { LinearGradient } from "../../styling/linear-gradient";

export * from "../../styling/style-properties";
export * from "../view-base";
export { LinearGradient };

import * as am from "../../animation";
let animationModule: typeof am;
Expand Down Expand Up @@ -435,10 +437,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
this.style.backgroundColor = value;
}

get backgroundImage(): string {
get backgroundImage(): string | LinearGradient {
return this.style.backgroundImage;
}
set backgroundImage(value: string) {
set backgroundImage(value: string | LinearGradient) {
this.style.backgroundImage = value;
}

Expand Down
4 changes: 3 additions & 1 deletion tns-core-modules/ui/core/view/view.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { ViewBase, Property, EventData, Color } from "../view-base";
import { Animation, AnimationDefinition, AnimationPromise } from "../../animation";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
import { GestureTypes, GestureEventData, GesturesObserver } from "../../gestures";
import { LinearGradient } from "../../styling/linear-gradient";

export * from "../view-base";
export * from "../../styling/style-properties";
export { LinearGradient };

export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator;

Expand Down Expand Up @@ -219,7 +221,7 @@ export abstract class View extends ViewBase {
/**
* Gets or sets the background image of the view.
*/
backgroundImage: string;
backgroundImage: string | LinearGradient;

/**
* Gets or sets the minimum width the view may grow to.
Expand Down
15 changes: 11 additions & 4 deletions tns-core-modules/ui/styling/background-common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Deifinitions.
import { Background as BackgroundDefinition } from "./background";
import { BackgroundRepeat } from "../core/view";
import { BackgroundRepeat, LinearGradient } from "../core/view";

// Types.
import { Color } from "../../color";
Expand All @@ -9,7 +9,7 @@ export class Background implements BackgroundDefinition {
public static default = new Background();

public color: Color;
public image: string;
public image: string | LinearGradient;
public repeat: BackgroundRepeat;
public position: string;
public size: string;
Expand Down Expand Up @@ -58,7 +58,7 @@ export class Background implements BackgroundDefinition {
return clone;
}

public withImage(value: string): Background {
public withImage(value: string | LinearGradient): Background {
const clone = this.clone();
clone.image = value;
return clone;
Expand Down Expand Up @@ -179,8 +179,15 @@ export class Background implements BackgroundDefinition {
return false;
}

let imagesEqual = false;
if (value1 instanceof LinearGradient && value2 instanceof LinearGradient) {
imagesEqual = LinearGradient.equals(value1, value2);
} else {
imagesEqual = value1.image === value2.image;
}

return Color.equals(value1.color, value2.color)
&& value1.image === value2.image
&& imagesEqual
&& value1.position === value2.position
&& value1.repeat === value2.repeat
&& value1.size === value2.size
Expand Down
45 changes: 42 additions & 3 deletions tns-core-modules/ui/styling/background.android.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { View } from "../core/view";
import { View, LinearGradient } from "../core/view";
import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX } from "../../utils/utils";
import { parse } from "../../css-value";
import { path, knownFolders } from "../../file-system";
Expand Down Expand Up @@ -104,6 +104,38 @@ function fromBase64(source: string): android.graphics.Bitmap {
return android.graphics.BitmapFactory.decodeByteArray(bytes, 0, bytes.length)
}

function fromGradient(gradient: LinearGradient): org.nativescript.widgets.LinearGradientDefinition {
const colors = Array.create("int", gradient.colorStops.length);
const stops = Array.create("float", gradient.colorStops.length);
let hasStops = false;
gradient.colorStops.forEach((stop, index) => {
colors[index] = stop.color.android;
if (stop.offset) {
stops[index] = stop.offset.value;
hasStops = true;
}
});

const alpha = gradient.angle / (Math.PI * 2);
const startX = Math.pow(
Math.sin(Math.PI * (alpha + 0.75)),
2
);
const startY = Math.pow(
Math.sin(Math.PI * (alpha + 0.5)),
2
);
const endX = Math.pow(
Math.sin(Math.PI * (alpha + 0.25)),
2
);
const endY = Math.pow(
Math.sin(Math.PI * alpha),
2
);
return new org.nativescript.widgets.LinearGradientDefinition(startX, startY, endX, endY, colors, hasStops ? stops : null);
}

const pattern: RegExp = /url\(('|")(.*?)\1\)/;
function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativescript.widgets.BorderDrawable) {
const nativeView = <android.view.View>view.nativeViewProtected;
Expand All @@ -115,8 +147,9 @@ function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativ
const backgroundSizeParsedCSSValues = createNativeCSSValueArray(background.size);
const blackColor = -16777216; //android.graphics.Color.BLACK;

let imageUri = background.image;
if (imageUri) {
let imageUri: string;
if (background.image && typeof background.image === "string") {
imageUri = background.image;
const match = imageUri.match(pattern);
if (match && match[2]) {
imageUri = match[2];
Expand All @@ -141,6 +174,11 @@ function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativ
}
}

let gradient: org.nativescript.widgets.LinearGradientDefinition = null;
if (background.image && background.image instanceof LinearGradient) {
gradient = fromGradient(background.image);
}

borderDrawable.refresh(
background.borderTopColor ? background.borderTopColor.android : blackColor,
background.borderRightColor ? background.borderRightColor.android : blackColor,
Expand All @@ -162,6 +200,7 @@ function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativ
background.color ? background.color.android : 0,
imageUri,
bitmap,
gradient,
context,
background.repeat,
background.position,
Expand Down
6 changes: 3 additions & 3 deletions tns-core-modules/ui/styling/background.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
*/ /** */

import { Color } from "../../color";
import { View, BackgroundRepeat } from "../core/view";
import { View, BackgroundRepeat, LinearGradient } from "../core/view";

export class Background {
public static default: Background;
public color: Color;
public image: string;
public image: string | LinearGradient;
public repeat: BackgroundRepeat;
public position: string;
public size: string;
Expand All @@ -27,7 +27,7 @@ export class Background {
public clipPath: string;

public withColor(value: Color): Background;
public withImage(value: string): Background;
public withImage(value: string | LinearGradient): Background;
public withRepeat(value: BackgroundRepeat): Background;
public withPosition(value: string): Background;
public withSize(value: string): Background;
Expand Down

0 comments on commit 5a83a1c

Please sign in to comment.