fix: port boxshadow to background improvements

This commit is contained in:
Martin Guillon
2021-04-09 09:03:06 +02:00
parent 75809f647d
commit 3f5ec3440d
2 changed files with 49 additions and 141 deletions

View File

@@ -4,7 +4,7 @@ import type { GestureTypes, GestureEventData } from '../../gestures';
// Types.
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../../styling/style-properties';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties';
import { layout } from '../../../utils';
import { Trace } from '../../../trace';
import { ShowModalOptions, hiddenProperty } from '../view-base';
@@ -14,7 +14,7 @@ import { perspectiveProperty, visibilityProperty, opacityProperty, horizontalAli
import { CoreTypes } from '../../../core-types';
import { Background, ad as androidBackground } from '../../styling/background';
import { refreshBorderDrawable } from '../../styling/background.android';
import { BackgroundClearFlags, refreshBorderDrawable } from '../../styling/background.android';
import { profile } from '../../../profiling';
import { topmost } from '../../frame/frame-stack';
import { Screen } from '../../../platform';
@@ -24,6 +24,7 @@ import lazy from '../../../utils/lazy';
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility';
import * as Utils from '../../../utils';
import { CSSShadow } from 'ui/styling/css-shadow';
export * from './view-common';
// helpers (these are okay re-exported here)
@@ -49,6 +50,10 @@ const modalMap = new Map<number, DialogOptions>();
let TouchListener: TouchListener;
let DialogFragment: DialogFragment;
interface AndroidView {
_cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
}
interface DialogOptions {
owner: View;
fullscreen: boolean;
@@ -1114,6 +1119,19 @@ export class View extends ViewCommon {
nativeView.setBackground(cachedDrawable);
}
}
protected _drawBoxShadow(boxShadow: CSSShadow) {
const nativeView = this.nativeViewProtected;
const config = {
shadowColor: boxShadow.color.android,
cornerRadius: Length.toDevicePixels(this.borderRadius as CoreTypes.LengthType, 0.0),
spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0),
blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0),
offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0),
offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0),
};
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
}
protected onBackgroundOrBorderPropertyChanged() {
const nativeView = <android.view.View & { _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable }>this.nativeViewProtected;
if (!nativeView) {
@@ -1121,11 +1139,36 @@ export class View extends ViewCommon {
}
const background = this.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);
}
const drawable = nativeView.getBackground();
const androidView = (<any>this) as AndroidView;
// use undefined as not set. getBackground will never return undefined only Drawable or null;
if (androidView._cachedDrawable === undefined && drawable) {
const constantState = drawable.getConstantState();
androidView._cachedDrawable = constantState || drawable;
}
const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable;
const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color;
// prettier-ignore
const onlyColor = !background.hasBorderWidth()
&& !background.hasBorderRadius()
&& !background.hasBoxShadow()
&& !background.clipPath
&& !background.image
&& !!background.color;
this._applyBackground(background, isBorderDrawable, onlyColor, drawable);
if (background.hasBoxShadow()) {
this._drawBoxShadow(background.getBoxShadow());
}
// TODO: Can we move BorderWidths as separate native setter?
// This way we could skip setPadding if borderWidth is not changed.
const leftPadding = Math.ceil(this.effectiveBorderLeftWidth + this.effectivePaddingLeft);
@@ -1137,6 +1180,8 @@ export class View extends ViewCommon {
} else {
nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
}
// reset clear flags
background.clearFlags = BackgroundClearFlags.NONE;
}
_redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void {
if (value instanceof Background) {

View File

@@ -1,139 +1,14 @@
import { View } from '../core/view';
import { LinearGradient } from './linear-gradient';
import { CoreTypes } from '../../core-types';
import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils';
import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils';
import { parse } from '../../css-value';
import { path, knownFolders } from '../../file-system';
import * as application from '../../application';
import { profile } from '../../profiling';
import { CSSShadow } from './css-shadow';
import { Length } from './style-properties';
import { BackgroundClearFlags } from './background-common';
export * from './background-common';
interface AndroidView {
_cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
}
// TODO: Change this implementation to use
// We are using "ad" here to avoid namespace collision with the global android object
export namespace ad {
let SDK: number;
function getSDK() {
if (!SDK) {
SDK = android.os.Build.VERSION.SDK_INT;
}
return SDK;
}
function isSetColorFilterOnlyWidget(nativeView: android.view.View): boolean {
// prettier-ignore
return (
nativeView instanceof android.widget.Button
|| (nativeView instanceof androidx.appcompat.widget.Toolbar && getSDK() >= 21)
// There is an issue with the DrawableContainer which was fixed
// for API version 21 and above: https://code.google.com/p/android/issues/detail?id=60183
);
}
export function onBackgroundOrBorderPropertyChanged(view: View) {
const nativeView = <android.view.View>view.nativeViewProtected;
if (!nativeView) {
return;
}
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();
const androidView = (<any>view) as AndroidView;
// use undefined as not set. getBackground will never return undefined only Drawable or null;
if (androidView._cachedDrawable === undefined && drawable) {
const constantState = drawable.getConstantState();
androidView._cachedDrawable = constantState || drawable;
}
const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable;
// prettier-ignore
const onlyColor = !background.hasBorderWidth()
&& !background.hasBorderRadius()
&& !background.hasBoxShadow()
&& !background.clipPath
&& !background.image
&& !!background.color;
if (!isBorderDrawable && drawable instanceof android.graphics.drawable.ColorDrawable && onlyColor) {
drawable.setColor(background.color.android);
drawable.invalidateSelf();
} else if (isSetColorFilterOnlyWidget(nativeView) && drawable && onlyColor) {
if (isBorderDrawable && androidView._cachedDrawable) {
if (!(androidView._cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState)) {
return;
}
drawable = androidView._cachedDrawable.newDrawable(nativeView.getResources());
nativeView.setBackground(drawable);
}
const backgroundColor = ((<any>drawable).backgroundColor = background.color.android);
drawable.mutate();
drawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
drawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
(<any>drawable).backgroundColor = backgroundColor;
} else if (!isBorderDrawable && onlyColor) {
// this is the fastest way to change only background color
nativeView.setBackgroundColor(background.color.android);
} else if (!background.isEmpty()) {
let backgroundDrawable = drawable;
if (drawable instanceof org.nativescript.widgets.BoxShadowDrawable) {
// if we have BoxShadow's we have to get the underlying drawable
backgroundDrawable = drawable.getWrappedDrawable();
}
if (backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable) {
refreshBorderDrawable(view, backgroundDrawable);
} else {
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString());
refreshBorderDrawable(view, <org.nativescript.widgets.BorderDrawable>backgroundDrawable);
nativeView.setBackground(backgroundDrawable);
}
} else {
const cachedDrawable = androidView._cachedDrawable;
let defaultDrawable: android.graphics.drawable.Drawable = null;
if (cachedDrawable) {
if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) {
defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources());
} else if (cachedDrawable instanceof android.graphics.drawable.Drawable) {
defaultDrawable = cachedDrawable;
}
}
nativeView.setBackground(defaultDrawable);
}
if (background.hasBoxShadow()) {
drawBoxShadow(nativeView, view, background.getBoxShadow());
}
// TODO: Can we move BorderWidths as separate native setter?
// This way we could skip setPadding if borderWidth is not changed.
const leftPadding = Math.ceil(view.effectiveBorderLeftWidth + view.effectivePaddingLeft);
const topPadding = Math.ceil(view.effectiveBorderTopWidth + view.effectivePaddingTop);
const rightPadding = Math.ceil(view.effectiveBorderRightWidth + view.effectivePaddingRight);
const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom);
nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
// reset clear flags
background.clearFlags = BackgroundClearFlags.NONE;
}
}
function fromBase64(source: string): android.graphics.Bitmap {
@@ -253,18 +128,6 @@ function createNativeCSSValueArray(css: string): androidNative.Array<org.natives
return nativeArray;
}
function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: CSSShadow) {
const config = {
shadowColor: boxShadow.color.android,
cornerRadius: Length.toDevicePixels(view.borderRadius as CoreTypes.LengthType, 0.0),
spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0),
blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0),
offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0),
offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0),
};
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
}
export enum CacheMode {
none,
memory,