chore: cleanup background handling

This commit is contained in:
Igor Randjelovic
2021-03-16 17:37:57 +01:00
committed by Nathan Walker
parent 21da31562c
commit 7c60735d14
21 changed files with 174 additions and 51 deletions

1
apps/automated/.npmrc Normal file
View File

@ -0,0 +1 @@
legacy-peer-deps=true

1
apps/toolbox/.npmrc Normal file
View File

@ -0,0 +1 @@
legacy-peer-deps=true

View File

@ -7,13 +7,13 @@
"url": "https://github.com/NativeScript/NativeScript.git" "url": "https://github.com/NativeScript/NativeScript.git"
}, },
"dependencies": { "dependencies": {
"nativescript-theme-core": "file:../../node_modules/nativescript-theme-core", "@nativescript/core": "file:../../packages/core",
"@nativescript/core": "file:../../packages/core" "nativescript-theme-core": "file:../../node_modules/nativescript-theme-core"
}, },
"devDependencies": { "devDependencies": {
"@nativescript/android": "7.0.1", "@nativescript/android": "7.0.1",
"@nativescript/ios": "7.2.0", "@nativescript/ios": "7.2.0",
"@nativescript/webpack": "file:../../dist/packages/nativescript-webpack.tgz", "@nativescript/webpack": "file:../../dist/packages/nativescript-webpack.tgz",
"typescript": "file:../../node_modules/typescript" "typescript": "4.0.7"
} }
} }

View File

@ -10,6 +10,7 @@
<Button text="box-shadow" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/> <Button text="box-shadow" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="root-layout" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/> <Button text="root-layout" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="a11y" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/> <Button text="a11y" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<Button text="css-playground" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</StackLayout> </StackLayout>

View File

@ -0,0 +1,41 @@
import { Observable, EventData, Page, Label } from '@nativescript/core';
import { addTaggedAdditionalCSS, removeTaggedAdditionalCSS } from '@nativescript/core/ui/styling/style-scope';
let page: Page;
let playLabel: Label;
let CSSTag = 'css-playground';
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new CssPlaygroundModel();
playLabel = page.getViewById('play');
}
export class CssPlaygroundModel extends Observable {
currentCSS = [
`width: 200;`,
`font-size: 20;`,
`background: #65adf1;`,
`color: white;`,
`box-shadow: 5 5;`,
// `text-shadow: 2 2 red;`,
`padding: 16;`,
].join('\n');
onTextChange(args) {
this.currentCSS = args.value;
}
resetCSS() {
console.log('reset css...');
removeTaggedAdditionalCSS(CSSTag);
playLabel._onCssStateChange();
}
applyCSS(args) {
this.resetCSS();
addTaggedAdditionalCSS(`#play { ${this.currentCSS}`, CSSTag);
playLabel._onCssStateChange();
}
}

View File

@ -0,0 +1,19 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="CSS Playground" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="*, *">
<ContentView>
<Label id="play" text="I'm a test label." verticalAlignment="center"/>
</ContentView>
<GridLayout row="1" rows="*, auto, auto" borderTopWidth="1">
<TextView text="{{ currentCSS }}" textChange="{{ onTextChange }}"/>
<Button class="btn btn-primary" text="Apply" row="1" tap="{{ applyCSS }}"/>
<Button class="btn btn-primary btn-orange" text="Reset" row="2" tap="{{ resetCSS }}"/>
</GridLayout>
</GridLayout>
</Page>

View File

@ -1,5 +1,4 @@
import { EventData, Page, Observable, RootLayoutOptions, getRootLayout, StackLayout, View } from '@nativescript/core'; import { EventData, Page, Observable, RootLayoutOptions, getRootLayout, StackLayout, View, CoreTypes } from '@nativescript/core';
import { Enums } from '@nativescript/core';
export function navigatingTo(args: EventData) { export function navigatingTo(args: EventData) {
const page = <Page>args.object; const page = <Page>args.object;

1
apps/ui/.npmrc Normal file
View File

@ -0,0 +1 @@
legacy-peer-deps=true

View File

@ -12,7 +12,7 @@ const possibleValues = [
]; ];
let currentIndex = 0; let currentIndex = 0;
export function butonTap(args: EventData) { export function buttonTap(args: EventData) {
let page = (<TextBase>args.object).page; let page = (<TextBase>args.object).page;
let lbl = <TextBase>page.getViewById('Label'); let lbl = <TextBase>page.getViewById('Label');
let btn = <TextBase>page.getViewById('Button'); let btn = <TextBase>page.getViewById('Button');
@ -20,7 +20,7 @@ export function butonTap(args: EventData) {
let textView = <TextBase>page.getViewById('TextView'); let textView = <TextBase>page.getViewById('TextView');
let newIndex = currentIndex++ % possibleValues.length; let newIndex = currentIndex++ % possibleValues.length;
let newValue = <any>possibleValues[newIndex]; let newValue = possibleValues[newIndex];
lbl.textShadow = newValue; lbl.textShadow = newValue;
btn.textShadow = newValue; btn.textShadow = newValue;

View File

@ -1,6 +1,6 @@
<Page> <Page>
<StackLayout> <StackLayout>
<Button id="Change" automationText="Change" text="Change" tap="butonTap" /> <Button id="Change" automationText="Change" text="Change" tap="buttonTap" />
<Label id="Label" automationText="Label" text="Label" /> <Label id="Label" automationText="Label" text="Label" />
<Button id="Button" automationText="Button" text="Button" /> <Button id="Button" automationText="Button" text="Button" />
<TextField id="TextField" automationText="TextField" text="TextField" /> <TextField id="TextField" automationText="TextField" text="TextField" />

View File

@ -6,6 +6,21 @@ import { LinearGradient } from './linear-gradient';
import { Color } from '../../color'; import { Color } from '../../color';
import { CSSShadow } from './css-shadow'; import { CSSShadow } from './css-shadow';
/**
* Flags used to hint the background handler if it has to clear a specific property
*
* Flags can be combined with the | operator
* for example: BackgroundClearFlags.CLEAR_BACKGROUND_COLOR | BackgroundClearFlags.CLEAR_BOX_SHADOW
*
* Flags can be checked for using the & operator
* for example: if(clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW) { ...clear box shadow... }
*/
export const enum BackgroundClearFlags {
NONE = 0,
CLEAR_BACKGROUND_COLOR = 1 << 0,
CLEAR_BOX_SHADOW = 2 << 0,
}
export class Background implements BackgroundDefinition { export class Background implements BackgroundDefinition {
public static default = new Background(); public static default = new Background();
@ -28,6 +43,7 @@ export class Background implements BackgroundDefinition {
public borderBottomRightRadius = 0; public borderBottomRightRadius = 0;
public clipPath: string; public clipPath: string;
public boxShadow: CSSShadow; public boxShadow: CSSShadow;
public clearFlags: number = BackgroundClearFlags.NONE;
private clone(): Background { private clone(): Background {
const clone = new Background(); const clone = new Background();
@ -51,6 +67,7 @@ export class Background implements BackgroundDefinition {
clone.borderBottomLeftRadius = this.borderBottomLeftRadius; clone.borderBottomLeftRadius = this.borderBottomLeftRadius;
clone.clipPath = this.clipPath; clone.clipPath = this.clipPath;
clone.boxShadow = this.boxShadow; clone.boxShadow = this.boxShadow;
clone.clearFlags = this.clearFlags;
return clone; return clone;
} }
@ -58,6 +75,9 @@ export class Background implements BackgroundDefinition {
public withColor(value: Color): Background { public withColor(value: Color): Background {
const clone = this.clone(); const clone = this.clone();
clone.color = value; clone.color = value;
if (!value) {
clone.clearFlags |= BackgroundClearFlags.CLEAR_BACKGROUND_COLOR;
}
return clone; return clone;
} }
@ -184,6 +204,9 @@ export class Background implements BackgroundDefinition {
public withBoxShadow(value: CSSShadow): Background { public withBoxShadow(value: CSSShadow): Background {
const clone = this.clone(); const clone = this.clone();
clone.boxShadow = value; clone.boxShadow = value;
if (!value) {
clone.clearFlags |= BackgroundClearFlags.CLEAR_BOX_SHADOW;
}
return clone; return clone;
} }
@ -229,6 +252,7 @@ export class Background implements BackgroundDefinition {
value1.borderBottomRightRadius === value2.borderBottomRightRadius && value1.borderBottomRightRadius === value2.borderBottomRightRadius &&
value1.borderBottomLeftRadius === value2.borderBottomLeftRadius && value1.borderBottomLeftRadius === value2.borderBottomLeftRadius &&
value1.clipPath === value2.clipPath value1.clipPath === value2.clipPath
// && value1.clearFlags === value2.clearFlags
); );
} }

View File

@ -8,6 +8,7 @@ import * as application from '../../application';
import { profile } from '../../profiling'; import { profile } from '../../profiling';
import { CSSShadow } from './css-shadow'; import { CSSShadow } from './css-shadow';
import { Length } from './style-properties'; import { Length } from './style-properties';
import { BackgroundClearFlags } from './background-common';
export * from './background-common'; export * from './background-common';
interface AndroidView { interface AndroidView {
@ -43,6 +44,13 @@ export namespace ad {
} }
const background = view.style.backgroundInternal; const background = view.style.backgroundInternal;
if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) {
// clear background if we're clearing the box shadow
// or the background has been removed
nativeView.setBackground(null);
}
let drawable = nativeView.getBackground(); let drawable = nativeView.getBackground();
const androidView = (<any>view) as AndroidView; const androidView = (<any>view) as AndroidView;
// use undefined as not set. getBackground will never return undefined only Drawable or null; // use undefined as not set. getBackground will never return undefined only Drawable or null;
@ -112,8 +120,6 @@ export namespace ad {
if (background.hasBoxShadow()) { if (background.hasBoxShadow()) {
drawBoxShadow(nativeView, view, background.getBoxShadow()); drawBoxShadow(nativeView, view, background.getBoxShadow());
} else {
clearBoxShadow(nativeView);
} }
// TODO: Can we move BorderWidths as separate native setter? // TODO: Can we move BorderWidths as separate native setter?
@ -124,6 +130,9 @@ export namespace ad {
const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom); const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom);
nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
// reset clear flags
background.clearFlags = BackgroundClearFlags.NONE;
} }
} }
@ -256,10 +265,6 @@ function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: CSS
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config)); org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
} }
function clearBoxShadow(nativeView: android.view.View) {
org.nativescript.widgets.Utils.clearBoxShadow(nativeView);
}
export enum CacheMode { export enum CacheMode {
none, none,
memory, memory,

View File

@ -31,6 +31,7 @@ export declare class Background {
public borderBottomLeftRadius: number; public borderBottomLeftRadius: number;
public clipPath: string; public clipPath: string;
public boxShadow: string | CSSShadow; public boxShadow: string | CSSShadow;
public clearFlags: number;
public withColor(value: Color): Background; public withColor(value: Color): Background;
public withImage(value: string | LinearGradient): Background; public withImage(value: string | LinearGradient): Background;

View File

@ -9,6 +9,7 @@ import { ImageSource } from '../../image-source';
import { CSSValue, parse as cssParse } from '../../css-value'; import { CSSValue, parse as cssParse } from '../../css-value';
import { CSSShadow } from './css-shadow'; import { CSSShadow } from './css-shadow';
import { Length } from './style-properties'; import { Length } from './style-properties';
import { BackgroundClearFlags } from './background-common';
export * from './background-common'; export * from './background-common';
@ -48,6 +49,12 @@ export namespace ios {
const background = view.style.backgroundInternal; const background = view.style.backgroundInternal;
const nativeView = <NativeView>view.nativeViewProtected; const nativeView = <NativeView>view.nativeViewProtected;
if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW) {
// clear box shadow if it has been removed!
view.setProperty('clipToBounds', true);
clearBoxShadow(nativeView);
}
if (nativeView.hasNonUniformBorder) { if (nativeView.hasNonUniformBorder) {
unsubscribeFromScrollNotifications(view); unsubscribeFromScrollNotifications(view);
clearNonUniformBorders(nativeView); clearNonUniformBorders(nativeView);
@ -91,10 +98,10 @@ export namespace ios {
if (background.hasBoxShadow()) { if (background.hasBoxShadow()) {
drawBoxShadow(nativeView, view, background.getBoxShadow(), background); drawBoxShadow(nativeView, view, background.getBoxShadow(), background);
} else {
view.setProperty('clipToBounds', true);
clearBoxShadow(nativeView);
} }
// reset clear flags
background.clearFlags = BackgroundClearFlags.NONE;
} }
} }

View File

@ -390,7 +390,13 @@ export class TextBase extends TextBaseCommon {
} }
[textShadowProperty.setNative](value: CSSShadow) { [textShadowProperty.setNative](value: CSSShadow) {
this.nativeViewProtected.setShadowLayer(Length.toDevicePixels(value.blurRadius, 0), Length.toDevicePixels(value.offsetX, 0), Length.toDevicePixels(value.offsetY, 0), value.color.android); // prettier-ignore
this.nativeViewProtected.setShadowLayer(
Length.toDevicePixels(value.blurRadius, java.lang.Float.MIN_VALUE),
Length.toDevicePixels(value.offsetX, 0),
Length.toDevicePixels(value.offsetY, 0),
value.color.android
);
} }
[letterSpacingProperty.getDefault](): number { [letterSpacingProperty.getDefault](): number {

View File

@ -365,16 +365,17 @@ export class TextBase extends TextBaseCommon {
return; return;
} }
if (value.color) { // shadow opacity is handled on the shadow's color instance
layer.shadowOpacity = value.color.a / 255; layer.shadowOpacity = value.color?.a ? value.color?.a / 255 : 1;
layer.shadowColor = value.color.ios.CGColor; layer.shadowColor = value.color.ios.CGColor;
} layer.shadowRadius = Length.toDevicePixels(value.blurRadius, 0.0);
if (value.blurRadius) { // prettier-ignore
layer.shadowRadius = Length.toDevicePixels(value.blurRadius); layer.shadowOffset = CGSizeMake(
} Length.toDevicePixels(value.offsetX, 0.0),
Length.toDevicePixels(value.offsetY, 0.0)
);
layer.shadowOffset = CGSizeMake(Length.toDevicePixels(value.offsetX), Length.toDevicePixels(value.offsetY));
layer.masksToBounds = false; layer.masksToBounds = false;
// NOTE: generally should not need shouldRasterize // NOTE: generally should not need shouldRasterize

View File

@ -4,7 +4,6 @@
export class Utils { export class Utils {
public static drawBoxShadow(view: android.view.View, value: string): void; public static drawBoxShadow(view: android.view.View, value: string): void;
public static clearBoxShadow(view: android.view.View): void;
} }
export class BoxShadowDrawable { export class BoxShadowDrawable {

View File

@ -14,17 +14,28 @@ public class Utils {
} }
Log.d("BoxShadowDrawable", "drawBoxShadow"); Log.d("BoxShadowDrawable", "drawBoxShadow");
Drawable wrap = view.getBackground(); Drawable currentBg = view.getBackground();
if(wrap == null) {
wrap = new ColorDrawable(Color.TRANSPARENT); if(currentBg != null) {
} else if(wrap instanceof BoxShadowDrawable) { Log.d("BoxShadowDrawable", "current BG is: " + currentBg.getClass().getName());
wrap = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable(); }
Log.d("BoxShadowDrawable", "already a BoxShadowDrawable, getting wrapped drawable:" + wrap.getClass().getName());
if(currentBg == null) {
Log.d("BoxShadowDrawable", "view had no background!");
currentBg = new ColorDrawable(Color.TRANSPARENT);
} else if(currentBg instanceof BoxShadowDrawable) {
currentBg = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable();
Log.d("BoxShadowDrawable", "already a BoxShadowDrawable, getting wrapped drawable:" + currentBg.getClass().getName());
} }
// replace background // replace background
Log.d("BoxShadowDrawable", "replacing background with new BoxShadowDrawable..."); Log.d("BoxShadowDrawable", "replacing background with new BoxShadowDrawable...");
view.setBackground(new BoxShadowDrawable(wrap, value)); view.setBackground(new BoxShadowDrawable(currentBg, value));
Drawable bg = view.getBackground();
if(bg != null) {
Log.d("BoxShadowDrawable", "new current bg: " + bg.getClass().getName());
}
int count = 0; int count = 0;
while (view.getParent() != null && view.getParent() instanceof ViewGroup) { while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
@ -39,17 +50,21 @@ public class Utils {
} }
} }
public static void clearBoxShadow(View view) { // public static void clearBoxShadow(View view) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { // if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
return; // return;
} // }
Log.d("BoxShadowDrawable", "clearBoxShadow."); // Log.d("BoxShadowDrawable", "clearBoxShadow.");
//
Drawable bg = view.getBackground(); // Drawable bg = view.getBackground();
if(bg instanceof BoxShadowDrawable) { // if(bg != null) {
Drawable original = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable(); // Log.d("BoxShadowDrawable", "current bg: " + bg.getClass().getName());
Log.d("BoxShadowDrawable", "BoxShadowDrawable found, resetting to original: " + original.getClass().getName()); // }
view.setBackground(original); // if(bg instanceof BoxShadowDrawable) {
} // Drawable original = ((BoxShadowDrawable) view.getBackground()).getWrappedDrawable();
} // Log.d("BoxShadowDrawable", "BoxShadowDrawable found, resetting to original: " + original.getClass().getName());
// view.setBackground(null);
//// view.setBackground(original);
// }
// }
} }

View File

@ -1,4 +1,6 @@
import { SpecReporter } from 'jasmine-spec-reporter'; import { SpecReporter } from 'jasmine-spec-reporter';
jasmine.getEnv().clearReporters(); jasmine.getEnv().clearReporters();
// @ts-ignore https://github.com/bcaudan/jasmine-spec-reporter/issues/588
jasmine.getEnv().addReporter(new SpecReporter()); jasmine.getEnv().addReporter(new SpecReporter());

View File

@ -51,6 +51,7 @@
}, },
"dependencies": { "dependencies": {
"@angular-devkit/core": "~10.0.0", "@angular-devkit/core": "~10.0.0",
"@nativescript/hook": "~2.0.0",
"clean-webpack-plugin": "~3.0.0", "clean-webpack-plugin": "~3.0.0",
"copy-webpack-plugin": "4.6.0", "copy-webpack-plugin": "4.6.0",
"css": "~3.0.0", "css": "~3.0.0",
@ -60,7 +61,6 @@
"global-modules-path": "~2.3.0", "global-modules-path": "~2.3.0",
"loader-utils": "~2.0.0", "loader-utils": "~2.0.0",
"minimatch": "~3.0.4", "minimatch": "~3.0.4",
"@nativescript/hook": "~2.0.0",
"nativescript-worker-loader": "~0.12.0", "nativescript-worker-loader": "~0.12.0",
"properties-reader": "~2.0.0", "properties-reader": "~2.0.0",
"proxy-lib": "0.4.0", "proxy-lib": "0.4.0",
@ -86,7 +86,7 @@
"@istanbuljs/nyc-config-typescript": "^1.0.0", "@istanbuljs/nyc-config-typescript": "^1.0.0",
"@ngtools/webpack": "~10.0.0", "@ngtools/webpack": "~10.0.0",
"@types/css": "~0.0.31", "@types/css": "~0.0.31",
"@types/jasmine": "^3.5.11", "@types/jasmine": "3.6.6",
"@types/loader-utils": "^2.0.0", "@types/loader-utils": "^2.0.0",
"@types/node": "~14.0.0", "@types/node": "~14.0.0",
"@types/proxyquire": "~1.3.28", "@types/proxyquire": "~1.3.28",
@ -94,8 +94,8 @@
"@types/semver": "^7.3.0", "@types/semver": "^7.3.0",
"@types/webpack": "^4.41.21", "@types/webpack": "^4.41.21",
"conventional-changelog-cli": "~2.0.34", "conventional-changelog-cli": "~2.0.34",
"jasmine": "^3.6.1", "jasmine": "3.6.4",
"jasmine-spec-reporter": "^5.0.2", "jasmine-spec-reporter": "6.0.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"proxyquire": "~2.1.0", "proxyquire": "~2.1.0",
"source-map-support": "^0.5.13", "source-map-support": "^0.5.13",