mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(core): text-shadow support (#8991)
This commit is contained in:
committed by
Nathan Walker
parent
304633d6b2
commit
a6b1bde655
@@ -17,6 +17,7 @@ export function loadExamples() {
|
|||||||
examples.set('line-height', 'css/line-height-page');
|
examples.set('line-height', 'css/line-height-page');
|
||||||
examples.set('decoration', 'css/text-decoration-page');
|
examples.set('decoration', 'css/text-decoration-page');
|
||||||
examples.set('transform', 'css/text-transform-page');
|
examples.set('transform', 'css/text-transform-page');
|
||||||
|
examples.set('shadow', 'css/text-shadow-page');
|
||||||
examples.set('whitespace', 'css/white-space-page');
|
examples.set('whitespace', 'css/white-space-page');
|
||||||
examples.set('progress-switch', 'css/progress-switch-page');
|
examples.set('progress-switch', 'css/progress-switch-page');
|
||||||
examples.set('zindex', 'css/zindex-page');
|
examples.set('zindex', 'css/zindex-page');
|
||||||
|
|||||||
26
apps/ui/src/css/text-shadow-page.ts
Normal file
26
apps/ui/src/css/text-shadow-page.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { EventData, TextBase } from '@nativescript/core';
|
||||||
|
|
||||||
|
const possibleValues = ['2 10 4 rgb(255, 100, 100)', '2 10 2 rgba(10, 10, 10, 0.5)', '1 1 1 #55a', '2 2 2 #aaa', ''];
|
||||||
|
let currentIndex = 0;
|
||||||
|
|
||||||
|
export function butonTap(args: EventData) {
|
||||||
|
let page = (<TextBase>args.object).page;
|
||||||
|
let lbl = <TextBase>page.getViewById('Label');
|
||||||
|
let btn = <TextBase>page.getViewById('Button');
|
||||||
|
let textField = <TextBase>page.getViewById('TextField');
|
||||||
|
let textView = <TextBase>page.getViewById('TextView');
|
||||||
|
|
||||||
|
let newIndex = currentIndex++ % possibleValues.length;
|
||||||
|
let newValue = <any>possibleValues[newIndex];
|
||||||
|
|
||||||
|
lbl.textShadow = newValue;
|
||||||
|
btn.textShadow = newValue;
|
||||||
|
textField.textShadow = newValue;
|
||||||
|
textView.textShadow = newValue;
|
||||||
|
|
||||||
|
if (lbl.text === 'Change text') {
|
||||||
|
lbl.text = btn.text = textField.text = textView.text = 'Text changed';
|
||||||
|
} else {
|
||||||
|
lbl.text = btn.text = textField.text = textView.text = 'Change text';
|
||||||
|
}
|
||||||
|
}
|
||||||
9
apps/ui/src/css/text-shadow-page.xml
Normal file
9
apps/ui/src/css/text-shadow-page.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Page>
|
||||||
|
<StackLayout>
|
||||||
|
<Button id="Change" automationText="Change" text="Change" tap="butonTap" />
|
||||||
|
<Label id="Label" automationText="Label" text="Label" />
|
||||||
|
<Button id="Button" automationText="Button" text="Button" />
|
||||||
|
<TextField id="TextField" automationText="TextField" text="TextField" />
|
||||||
|
<TextView id="TextView" automationText="TextView" text="TextView" />
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
||||||
@@ -76,7 +76,7 @@ export type { TabStripItemEventData } from './tab-navigation-base/tab-strip';
|
|||||||
export { TabStripItem } from './tab-navigation-base/tab-strip-item';
|
export { TabStripItem } from './tab-navigation-base/tab-strip-item';
|
||||||
export { TabView, TabViewItem } from './tab-view';
|
export { TabView, TabViewItem } from './tab-view';
|
||||||
export { Tabs } from './tabs';
|
export { Tabs } from './tabs';
|
||||||
export { TextBase, getTransformedText, letterSpacingProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, whiteSpaceProperty, lineHeightProperty } from './text-base';
|
export { TextBase, getTransformedText, letterSpacingProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, textShadowProperty, whiteSpaceProperty, lineHeightProperty } from './text-base';
|
||||||
export type { TextTransform } from './text-base';
|
export type { TextTransform } from './text-base';
|
||||||
export { FormattedString } from './text-base/formatted-string';
|
export { FormattedString } from './text-base/formatted-string';
|
||||||
export { Span } from './text-base/span';
|
export { Span } from './text-base/span';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Observable } from '../../../data/observable';
|
|||||||
|
|
||||||
import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from '../../layouts/flexbox-layout';
|
import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from '../../layouts/flexbox-layout';
|
||||||
import { Trace } from '../../../trace';
|
import { Trace } from '../../../trace';
|
||||||
import { TextAlignment, TextDecoration, TextTransform, WhiteSpace } from '../../text-base';
|
import { TextAlignment, TextDecoration, TextTransform, WhiteSpace, TextShadow } from '../../text-base';
|
||||||
import { BoxShadow } from '../box-shadow';
|
import { BoxShadow } from '../box-shadow';
|
||||||
|
|
||||||
export interface CommonLayoutParams {
|
export interface CommonLayoutParams {
|
||||||
@@ -157,6 +157,7 @@ export class Style extends Observable implements StyleDefinition {
|
|||||||
public textAlignment: TextAlignment;
|
public textAlignment: TextAlignment;
|
||||||
public textDecoration: TextDecoration;
|
public textDecoration: TextDecoration;
|
||||||
public textTransform: TextTransform;
|
public textTransform: TextTransform;
|
||||||
|
public textShadow: TextShadow;
|
||||||
public whiteSpace: WhiteSpace;
|
public whiteSpace: WhiteSpace;
|
||||||
|
|
||||||
public minWidth: Length;
|
public minWidth: Length;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Types
|
// Types
|
||||||
import { TextDecoration, TextAlignment, TextTransform, WhiteSpace, getClosestPropertyValue } from './text-base-common';
|
import { TextDecoration, TextAlignment, TextTransform, TextShadow, WhiteSpace, getClosestPropertyValue } from './text-base-common';
|
||||||
|
|
||||||
// Requires
|
// Requires
|
||||||
import { Font } from '../styling/font';
|
import { Font } from '../styling/font';
|
||||||
import { backgroundColorProperty, VerticalAlignment } from '../styling/style-properties';
|
import { backgroundColorProperty, VerticalAlignment } from '../styling/style-properties';
|
||||||
import { TextBaseCommon, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textProperty, textTransformProperty, letterSpacingProperty, whiteSpaceProperty, lineHeightProperty, isBold, resetSymbol } from './text-base-common';
|
import { TextBaseCommon, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textProperty, textTransformProperty, textShadowProperty, letterSpacingProperty, whiteSpaceProperty, lineHeightProperty, isBold, resetSymbol } from './text-base-common';
|
||||||
import { Color } from '../../color';
|
import { Color } from '../../color';
|
||||||
import { colorProperty, fontSizeProperty, fontInternalProperty, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../styling/style-properties';
|
import { colorProperty, fontSizeProperty, fontInternalProperty, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../styling/style-properties';
|
||||||
import { FormattedString } from './formatted-string';
|
import { FormattedString } from './formatted-string';
|
||||||
@@ -378,6 +378,19 @@ export class TextBase extends TextBaseCommon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[textShadowProperty.getDefault](value: number) {
|
||||||
|
return {
|
||||||
|
radius: this.nativeTextViewProtected.getShadowRadius(),
|
||||||
|
offsetX: this.nativeTextViewProtected.getShadowDx(),
|
||||||
|
offsetY: this.nativeTextViewProtected.getShadowDy(),
|
||||||
|
color: this.nativeTextViewProtected.getShadowColor(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[textShadowProperty.setNative](value: TextShadow) {
|
||||||
|
this.nativeViewProtected.setShadowLayer(Length.toDevicePixels(value.blurRadius, 0), Length.toDevicePixels(value.offsetX, 0), Length.toDevicePixels(value.offsetY, 0), value.color.android);
|
||||||
|
}
|
||||||
|
|
||||||
[letterSpacingProperty.getDefault](): number {
|
[letterSpacingProperty.getDefault](): number {
|
||||||
return org.nativescript.widgets.ViewHelper.getLetterspacing(this.nativeTextViewProtected);
|
return org.nativescript.widgets.ViewHelper.getLetterspacing(this.nativeTextViewProtected);
|
||||||
}
|
}
|
||||||
|
|||||||
12
packages/core/ui/text-base/index.d.ts
vendored
12
packages/core/ui/text-base/index.d.ts
vendored
@@ -51,6 +51,11 @@ export class TextBase extends View implements AddChildFromBuilder {
|
|||||||
*/
|
*/
|
||||||
textTransform: TextTransform;
|
textTransform: TextTransform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or sets text shadow style property.
|
||||||
|
*/
|
||||||
|
textShadow: TextShadow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or sets white space style property.
|
* Gets or sets white space style property.
|
||||||
*/
|
*/
|
||||||
@@ -120,10 +125,17 @@ export type WhiteSpace = 'initial' | 'normal' | 'nowrap';
|
|||||||
export type TextAlignment = 'initial' | 'left' | 'center' | 'right';
|
export type TextAlignment = 'initial' | 'left' | 'center' | 'right';
|
||||||
export type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
|
export type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
|
||||||
export type TextDecoration = 'none' | 'underline' | 'line-through' | 'underline line-through';
|
export type TextDecoration = 'none' | 'underline' | 'line-through' | 'underline line-through';
|
||||||
|
export type TextShadow = {
|
||||||
|
offsetX: Length;
|
||||||
|
offsetY: Length;
|
||||||
|
blurRadius: Length;
|
||||||
|
color: Color;
|
||||||
|
};
|
||||||
|
|
||||||
export const textAlignmentProperty: InheritedCssProperty<Style, TextAlignment>;
|
export const textAlignmentProperty: InheritedCssProperty<Style, TextAlignment>;
|
||||||
export const textDecorationProperty: CssProperty<Style, TextDecoration>;
|
export const textDecorationProperty: CssProperty<Style, TextDecoration>;
|
||||||
export const textTransformProperty: CssProperty<Style, TextTransform>;
|
export const textTransformProperty: CssProperty<Style, TextTransform>;
|
||||||
|
export const textShadowProperty: CssProperty<Style, TextShadow>;
|
||||||
export const whiteSpaceProperty: CssProperty<Style, WhiteSpace>;
|
export const whiteSpaceProperty: CssProperty<Style, WhiteSpace>;
|
||||||
export const letterSpacingProperty: CssProperty<Style, number>;
|
export const letterSpacingProperty: CssProperty<Style, number>;
|
||||||
export const lineHeightProperty: CssProperty<Style, number>;
|
export const lineHeightProperty: CssProperty<Style, number>;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Types
|
// Types
|
||||||
import { TextDecoration, TextAlignment, TextTransform, getClosestPropertyValue } from './text-base-common';
|
import { TextDecoration, TextAlignment, TextTransform, TextShadow, getClosestPropertyValue } from './text-base-common';
|
||||||
|
|
||||||
// Requires
|
// Requires
|
||||||
import { Font } from '../styling/font';
|
import { Font } from '../styling/font';
|
||||||
import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, letterSpacingProperty, lineHeightProperty, resetSymbol } from './text-base-common';
|
import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, textShadowProperty, letterSpacingProperty, lineHeightProperty, resetSymbol } from './text-base-common';
|
||||||
import { Color } from '../../color';
|
import { Color } from '../../color';
|
||||||
import { FormattedString } from './formatted-string';
|
import { FormattedString } from './formatted-string';
|
||||||
import { Span } from './span';
|
import { Span } from './span';
|
||||||
@@ -188,6 +188,10 @@ export class TextBase extends TextBaseCommon {
|
|||||||
this._setNativeText();
|
this._setNativeText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[textShadowProperty.setNative](value: TextShadow) {
|
||||||
|
this._setShadow(value);
|
||||||
|
}
|
||||||
|
|
||||||
_setNativeText(reset = false): void {
|
_setNativeText(reset = false): void {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
const nativeView = this.nativeTextViewProtected;
|
const nativeView = this.nativeTextViewProtected;
|
||||||
@@ -343,6 +347,32 @@ export class TextBase extends TextBaseCommon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setShadow(value: TextShadow): void {
|
||||||
|
let layer;
|
||||||
|
|
||||||
|
if (this.nativeTextViewProtected instanceof UITextView) {
|
||||||
|
layer = this.nativeTextViewProtected.layer.sublayers.objectAtIndex(1);
|
||||||
|
} else {
|
||||||
|
layer = this.nativeTextViewProtected.layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNullOrUndefined(value)) {
|
||||||
|
// clear the text shadow
|
||||||
|
layer.shadowOpacity = 0;
|
||||||
|
layer.shadowRadius = 0;
|
||||||
|
layer.shadowColor = UIColor.clearColor;
|
||||||
|
layer.shadowOffset = CGSizeMake(0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.shadowOpacity = 1;
|
||||||
|
layer.shadowRadius = value.blurRadius;
|
||||||
|
layer.shadowColor = value.color.ios.CGColor;
|
||||||
|
layer.shadowOffset = CGSizeMake(value.offsetX, value.offsetY);
|
||||||
|
layer.shouldRasterize = true;
|
||||||
|
layer.masksToBounds = false;
|
||||||
|
}
|
||||||
|
|
||||||
createNSMutableAttributedString(formattedString: FormattedString): NSMutableAttributedString {
|
createNSMutableAttributedString(formattedString: FormattedString): NSMutableAttributedString {
|
||||||
const mas = NSMutableAttributedString.alloc().init();
|
const mas = NSMutableAttributedString.alloc().init();
|
||||||
this._spanRanges = [];
|
this._spanRanges = [];
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import { Style } from '../styling/style';
|
|||||||
import { Length } from '../styling/style-properties';
|
import { Length } from '../styling/style-properties';
|
||||||
import { Observable } from '../../data/observable';
|
import { Observable } from '../../data/observable';
|
||||||
|
|
||||||
import { TextAlignment, TextDecoration, TextTransform, WhiteSpace } from './text-base-interfaces';
|
import { TextAlignment, TextDecoration, TextTransform, WhiteSpace, TextShadow } from './text-base-interfaces';
|
||||||
import { TextBase as TextBaseDefinition } from '.';
|
import { TextBase as TextBaseDefinition } from '.';
|
||||||
|
import { Color } from '../../color';
|
||||||
|
|
||||||
export * from './text-base-interfaces';
|
export * from './text-base-interfaces';
|
||||||
|
|
||||||
@@ -26,26 +27,6 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition
|
|||||||
public text: string;
|
public text: string;
|
||||||
public formattedText: FormattedString;
|
public formattedText: FormattedString;
|
||||||
|
|
||||||
/***
|
|
||||||
* In the NativeScript Core; by default the nativeTextViewProtected points to the same value as nativeViewProtected.
|
|
||||||
* At this point no internal NS components need this indirection functionality.
|
|
||||||
* This indirection is used to allow support usage by third party components so they don't have to duplicate functionality.
|
|
||||||
*
|
|
||||||
* A third party component can just override the `nativeTextViewProtected` getter and return a different internal view and that view would be
|
|
||||||
* what all TextView/TextInput class features would be applied to.
|
|
||||||
*
|
|
||||||
* A example is the Android MaterialDesign TextInput class, it has a wrapper view of a TextInputLayout
|
|
||||||
* https://developer.android.com/reference/com/google/android/material/textfield/TextInputLayout
|
|
||||||
* which wraps the actual TextInput. This wrapper layout (TextInputLayout) must be assigned to the nativeViewProtected as the entire
|
|
||||||
* NS Core uses nativeViewProtected for everything related to layout, so that it can be measured, added to the parent view as a child, ect.
|
|
||||||
*
|
|
||||||
* However, its internal view would be the actual TextView/TextInput and to allow that sub-view to have the normal TextView/TextInput
|
|
||||||
* class features, which we expose and to allow them to work on it, the internal TextView/TextInput is what the needs to have the class values applied to it.
|
|
||||||
*
|
|
||||||
* So all code that works on what is expected to be a TextView/TextInput should use `nativeTextViewProtected` so that any third party
|
|
||||||
* components that need to have two separate components can work properly without them having to duplicate all the TextBase (and decendants) functionality
|
|
||||||
* by just overriding the nativeTextViewProtected getter.
|
|
||||||
**/
|
|
||||||
get nativeTextViewProtected() {
|
get nativeTextViewProtected() {
|
||||||
return this.nativeViewProtected;
|
return this.nativeViewProtected;
|
||||||
}
|
}
|
||||||
@@ -113,6 +94,13 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition
|
|||||||
this.style.textTransform = value;
|
this.style.textTransform = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get textShadow(): TextShadow {
|
||||||
|
return this.style.textShadow;
|
||||||
|
}
|
||||||
|
set textShadow(value: TextShadow) {
|
||||||
|
this.style.textShadow = value;
|
||||||
|
}
|
||||||
|
|
||||||
get whiteSpace(): WhiteSpace {
|
get whiteSpace(): WhiteSpace {
|
||||||
return this.style.whiteSpace;
|
return this.style.whiteSpace;
|
||||||
}
|
}
|
||||||
@@ -259,6 +247,22 @@ export const textTransformProperty = new CssProperty<Style, TextTransform>({
|
|||||||
});
|
});
|
||||||
textTransformProperty.register(Style);
|
textTransformProperty.register(Style);
|
||||||
|
|
||||||
|
export const textShadowProperty = new CssProperty<Style, string | TextShadow>({
|
||||||
|
name: 'textShadow',
|
||||||
|
cssName: 'text-shadow',
|
||||||
|
affectsLayout: global.isIOS,
|
||||||
|
valueConverter: (value) => {
|
||||||
|
const params = value.split(' ');
|
||||||
|
return {
|
||||||
|
offsetX: Length.parse(params[0]),
|
||||||
|
offsetY: Length.parse(params[1]),
|
||||||
|
blurRadius: Length.parse(params[2]),
|
||||||
|
color: new Color(params.slice(3).join('')),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
textShadowProperty.register(Style);
|
||||||
|
|
||||||
const whiteSpaceConverter = makeParser<WhiteSpace>(makeValidator<WhiteSpace>('initial', 'normal', 'nowrap'));
|
const whiteSpaceConverter = makeParser<WhiteSpace>(makeValidator<WhiteSpace>('initial', 'normal', 'nowrap'));
|
||||||
export const whiteSpaceProperty = new CssProperty<Style, WhiteSpace>({
|
export const whiteSpaceProperty = new CssProperty<Style, WhiteSpace>({
|
||||||
name: 'whiteSpace',
|
name: 'whiteSpace',
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
export type WhiteSpace = 'initial' | 'normal' | 'nowrap';
|
import { Color } from 'color';
|
||||||
|
import { Length } from 'ui/styling/style-properties';
|
||||||
|
|
||||||
|
export type WhiteSpace = 'initial' | 'normal' | 'nowrap';
|
||||||
export type TextAlignment = 'initial' | 'left' | 'center' | 'right';
|
export type TextAlignment = 'initial' | 'left' | 'center' | 'right';
|
||||||
export type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
|
export type TextTransform = 'initial' | 'none' | 'capitalize' | 'uppercase' | 'lowercase';
|
||||||
export type TextDecoration = 'none' | 'underline' | 'line-through' | 'underline line-through';
|
export type TextDecoration = 'none' | 'underline' | 'line-through' | 'underline line-through';
|
||||||
|
export type TextShadow = {
|
||||||
|
offsetX: Length;
|
||||||
|
offsetY: Length;
|
||||||
|
blurRadius: Length;
|
||||||
|
color: Color;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user