Skip to content

Commit 065eadb

Browse files
committed
feat: android shape suport
1 parent 2fe631f commit 065eadb

8 files changed

+134
-19
lines changed

src/button/button-common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export abstract class ButtonBase extends Button {
99
@cssProperty dynamicElevationOffset: number;
1010
@cssProperty rippleColor: Color;
1111
@cssProperty verticalTextAlignment: VerticalTextAlignment;
12+
@cssProperty shape: string;
1213

1314
public imageSource: ImageSource;
1415
public src: string | ImageSource;

src/button/button.android.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { VerticalTextAlignment, verticalTextAlignmentProperty } from '@nativescript-community/text';
2-
import { dynamicElevationOffsetProperty, elevationProperty, rippleColorProperty } from '@nativescript-community/ui-material-core';
2+
import { dynamicElevationOffsetProperty, elevationProperty, rippleColorProperty, shapeProperty, themer } from '@nativescript-community/ui-material-core';
33
import { createStateListAnimator, getColorStateList, getLayout, isPostLollipop } from '@nativescript-community/ui-material-core/android/utils';
44
import {
55
Background,
@@ -90,6 +90,10 @@ export class Button extends ButtonBase {
9090
[rippleColorProperty.setNative](color: Color) {
9191
this.nativeViewProtected.setRippleColor(getColorStateList(color.android));
9292
}
93+
[shapeProperty.setNative](shape: string) {
94+
const appearanceModel = themer.getShape(shape);
95+
this.nativeViewProtected.setShapeAppearanceModel(appearanceModel);
96+
}
9397

9498
getDefaultElevation(): number {
9599
return 2; // 2dp @dimen/mtrl_btn_elevation
@@ -152,8 +156,12 @@ export class Button extends ButtonBase {
152156
if (value.color) {
153157
view.setBackgroundColor(value.color.android);
154158
}
155-
this.setCornerRadius(value.borderTopLeftRadius);
156-
view.setStrokeWidth(value.borderTopWidth);
159+
if (value.borderTopLeftRadius !== 0) {
160+
this.setCornerRadius(value.borderTopLeftRadius);
161+
}
162+
if (value.borderTopWidth !== 0) {
163+
view.setStrokeWidth(value.borderTopWidth);
164+
}
157165
if (value.borderTopColor) {
158166
view.setStrokeColor(getColorStateList(value.borderTopColor.android));
159167
}

src/cardview/cardview-common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export abstract class CardViewBase extends ContentView {
66
@cssProperty elevation: number;
77
@cssProperty dynamicElevationOffset: number;
88
@cssProperty rippleColor: Color;
9+
@cssProperty shape: string;
910
}

src/cardview/cardview.android.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { dynamicElevationOffsetProperty, elevationProperty, getRippleColor, rippleColorProperty, themer } from '@nativescript-community/ui-material-core';
1+
import { dynamicElevationOffsetProperty, elevationProperty, getRippleColor, rippleColorProperty, shapeProperty, themer } from '@nativescript-community/ui-material-core';
22
import { createStateListAnimator, getAttrColor, isPostLollipop } from '@nativescript-community/ui-material-core/android/utils';
33
import { Color, Length, backgroundInternalProperty } from '@nativescript/core';
44
import { CardViewBase } from './cardview-common';
@@ -253,7 +253,7 @@ export class CardView extends CardViewBase {
253253
view.setClickable(this.isUserInteractionEnabled);
254254
// view.setUseCompatPadding(true)
255255
// store the default radius
256-
this._borderRadius = view.getRadius();
256+
// this._borderRadius = view.getRadius();
257257

258258
// set the view outline
259259
// view.setClipToOutline(false);
@@ -303,7 +303,10 @@ export class CardView extends CardViewBase {
303303
if (value instanceof android.graphics.drawable.Drawable) {
304304
this.nativeViewProtected.setBackgroundDrawable(value);
305305
} else {
306-
this._strokeWidth = value.borderTopWidth;
306+
if (this._strokeWidth !== value.borderTopWidth) {
307+
this._strokeWidth = value.borderTopWidth;
308+
this._strokeWidth = value.borderTopWidth;
309+
}
307310
this.nativeViewProtected.setStrokeWidth(this._strokeWidth);
308311
if (value.color) {
309312
this.nativeViewProtected.setCardBackgroundColor(value.color.android);
@@ -312,7 +315,6 @@ export class CardView extends CardViewBase {
312315
this.nativeViewProtected.setStrokeColor(value.borderTopColor.android);
313316
// this.fgDrawable.setStroke(this._strokeWidth, value.borderTopColor.android);
314317
}
315-
316318
if (this._borderRadius !== value.borderTopLeftRadius) {
317319
this._borderRadius = value.borderTopLeftRadius;
318320
this.nativeViewProtected.setRadius(this._borderRadius);
@@ -322,6 +324,11 @@ export class CardView extends CardViewBase {
322324
}
323325
}
324326

327+
[shapeProperty.setNative](shape: string) {
328+
const appearanceModel = themer.getShape(shape);
329+
this.nativeViewProtected.setShapeAppearanceModel(appearanceModel);
330+
}
331+
325332
[elevationProperty.setNative](value: number) {
326333
if (!this.nativeViewProtected) {
327334
return;

src/core/cssproperties.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Color, CssProperty, InheritedCssProperty, Length, Style, makeParser, makeValidator } from '@nativescript/core';
22

33
function createGetter(key) {
4-
return function() {
4+
return function () {
55
return this.style[key];
66
};
77
}
88
function createSetter(key) {
9-
return function(newVal) {
9+
return function (newVal) {
1010
this.style[key] = newVal;
1111
};
1212
}
@@ -24,7 +24,7 @@ export const rippleColorProperty = new CssProperty<Style, Color>({
2424
name: 'rippleColor',
2525
cssName: 'ripple-color',
2626
equalityComparer: Color.equals,
27-
valueConverter: v => new Color(v)
27+
valueConverter: (v) => new Color(v)
2828
});
2929
rippleColorProperty.register(Style);
3030
export const elevationProperty = new CssProperty<Style, Length>({
@@ -47,3 +47,9 @@ export const variantProperty = new CssProperty<Style, string>({
4747
cssName: 'variant'
4848
});
4949
variantProperty.register(Style);
50+
51+
export const shapeProperty = new CssProperty<Style, string>({
52+
name: 'shape',
53+
cssName: 'shape'
54+
});
55+
shapeProperty.register(Style);

src/core/index.android.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import { Application, Background, Button, Color, Length, View, androidDynamicElevationOffsetProperty, androidElevationProperty, backgroundInternalProperty, profile } from '@nativescript/core';
22
import { createRippleDrawable, createStateListAnimator, getAttrColor, getColorStateList, handleClearFocus, isPostLollipop, isPostLollipopMR1, isPostMarshmallow } from './android/utils';
3-
import { applyMixins } from './index.common';
3+
import { CornerFamily, applyMixins } from './index.common';
44
import { cssProperty, dynamicElevationOffsetProperty, elevationProperty, rippleColorProperty } from './cssproperties';
55
import { ad } from '@nativescript/core/utils';
6+
import { ShapeProperties } from '.';
67
export * from './cssproperties';
78
export { applyMixins };
89

10+
function createCornerTreatment(cornerFamily: CornerFamily): com.google.android.material.shape.CornerTreatment {
11+
switch (cornerFamily) {
12+
case CornerFamily.CUT:
13+
return new com.google.android.material.shape.CutCornerTreatment();
14+
default:
15+
case CornerFamily.ROUNDED:
16+
return new com.google.android.material.shape.RoundedCornerTreatment();
17+
}
18+
}
19+
920
// stub class as we don't use this on android
1021
export class Themer {
1122
primaryColor: string | Color;
@@ -79,6 +90,67 @@ export class Themer {
7990
}
8091
return this.controlHighlightColor;
8192
}
93+
94+
_shapes: {
95+
[k: string]: com.google.android.material.shape.ShapeAppearanceModel;
96+
} = {};
97+
getShape(key: string) {
98+
return this._shapes[key] || null;
99+
}
100+
createShape(key: string, options: ShapeProperties) {
101+
const builder = com.google.android.material.shape.ShapeAppearanceModel.builder();
102+
if (options.cornerFamily) {
103+
builder.setAllCorners(createCornerTreatment(options.cornerFamily));
104+
}
105+
if (options.cornerFamilyBottomRight) {
106+
builder.setBottomRightCorner(createCornerTreatment(options.cornerFamilyBottomRight));
107+
}
108+
if (options.cornerFamilyBottomLeft) {
109+
builder.setBottomLeftCorner(createCornerTreatment(options.cornerFamilyBottomLeft));
110+
}
111+
if (options.cornerFamilyTopLeft) {
112+
builder.setTopLeftCorner(createCornerTreatment(options.cornerFamilyTopLeft));
113+
}
114+
if (options.cornerFamilyTopRight) {
115+
builder.setTopRightCorner(createCornerTreatment(options.cornerFamilyTopRight));
116+
}
117+
if (options.cornerSize !== undefined) {
118+
if (typeof options.cornerSize === 'object') {
119+
builder.setAllCornerSizes(new com.google.android.material.shape.RelativeCornerSize(options.cornerSize.value));
120+
} else {
121+
builder.setAllCornerSizes(options.cornerSize);
122+
}
123+
}
124+
if (options.cornerSizeBottomLeft !== undefined) {
125+
if (typeof options.cornerSizeBottomLeft === 'object') {
126+
builder.setBottomLeftCornerSize(new com.google.android.material.shape.RelativeCornerSize(options.cornerSizeBottomLeft.value));
127+
} else {
128+
builder.setBottomLeftCornerSize(options.cornerSizeBottomLeft);
129+
}
130+
}
131+
if (options.cornerSizeBottomRight !== undefined) {
132+
if (typeof options.cornerSizeBottomRight === 'object') {
133+
builder.setBottomRightCornerSize(new com.google.android.material.shape.RelativeCornerSize(options.cornerSizeBottomRight.value));
134+
} else {
135+
builder.setBottomRightCornerSize(options.cornerSizeBottomRight);
136+
}
137+
}
138+
if (options.cornerSizeTopRight !== undefined) {
139+
if (typeof options.cornerSizeTopRight === 'object') {
140+
builder.setTopRightCornerSize(new com.google.android.material.shape.RelativeCornerSize(options.cornerSizeTopRight.value));
141+
} else {
142+
builder.setTopRightCornerSize(options.cornerSizeTopRight);
143+
}
144+
}
145+
if (options.cornerSizeTopLeft !== undefined) {
146+
if (typeof options.cornerSizeTopLeft === 'object') {
147+
builder.setTopLeftCornerSize(new com.google.android.material.shape.RelativeCornerSize(options.cornerSizeTopLeft.value));
148+
} else {
149+
builder.setTopLeftCornerSize(options.cornerSizeTopLeft);
150+
}
151+
}
152+
this._shapes[key] = builder.build();
153+
}
82154
}
83155

84156
export const themer = new Themer();

src/core/index.common.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
export enum CornerFamily {
2+
CUT = 'cut',
3+
ROUNDED = 'rounded'
4+
}
5+
16
export function applyMixins(
27
derivedCtor: any,
38
baseCtors: any[],
@@ -8,8 +13,8 @@ export function applyMixins(
813
}
914
) {
1015
const omits = options && options.omit ? options.omit : [];
11-
baseCtors.forEach(baseCtor => {
12-
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
16+
baseCtors.forEach((baseCtor) => {
17+
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
1318
if (omits.indexOf(name) !== -1) {
1419
return;
1520
}
@@ -23,7 +28,7 @@ export function applyMixins(
2328
if (!oldImpl) {
2429
derivedCtor.prototype[name] = baseCtor.prototype[name];
2530
} else {
26-
derivedCtor.prototype[name] = function(...args) {
31+
derivedCtor.prototype[name] = function (...args) {
2732
if (options) {
2833
if (!!options.override) {
2934
return baseCtor.prototype[name].apply(this, args);
@@ -42,15 +47,15 @@ export function applyMixins(
4247
}
4348
}
4449
});
45-
Object.getOwnPropertySymbols(baseCtor.prototype).forEach(symbol => {
50+
Object.getOwnPropertySymbols(baseCtor.prototype).forEach((symbol) => {
4651
if (omits.indexOf(symbol) !== -1) {
4752
return;
4853
}
4954
const oldImpl: Function = derivedCtor.prototype[symbol];
5055
if (!oldImpl) {
5156
derivedCtor.prototype[symbol] = baseCtor.prototype[symbol];
5257
} else {
53-
derivedCtor.prototype[symbol] = function(...args) {
58+
derivedCtor.prototype[symbol] = function (...args) {
5459
if (options) {
5560
if (!!options.override) {
5661
return baseCtor.prototype[symbol].apply(this, args);

src/core/index.d.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Color } from '@nativescript/core';
1+
import { Color, LengthPercentUnit, PercentLength } from '@nativescript/core';
22

33
declare module '@nativescript/core/ui/core/view' {
44
interface View {
@@ -12,8 +12,22 @@ export interface TypographyOptions {
1212
fontSize?: number;
1313
}
1414

15+
import { CornerFamily } from './index.common';
16+
export { CornerFamily };
17+
export interface ShapeProperties {
18+
cornerSize?: number | LengthPercentUnit;
19+
cornerSizeTopRight?: number | LengthPercentUnit;
20+
cornerSizeBottomLeft?: number | LengthPercentUnit;
21+
cornerSizeTopLeft?: number | LengthPercentUnit;
22+
cornerSizeBottomRight?: number | LengthPercentUnit;
23+
cornerFamily?: CornerFamily;
24+
cornerFamilyTopLeft?: CornerFamily;
25+
cornerFamilyTopRight?: CornerFamily;
26+
cornerFamilyBottomRight?: CornerFamily;
27+
cornerFamilyBottomLeft?: CornerFamily;
28+
}
29+
1530
export class Themer {
16-
// appColorScheme: MDCSemanticColorScheme;
1731
getOrcreateAppColorScheme();
1832
getAppColorScheme();
1933
setPrimaryColor(value: string | Color);
@@ -28,6 +42,8 @@ export class Themer {
2842
getSurfaceColor(): string | Color;
2943
setOnSurfaceColor(value: string | Color);
3044
getOnSurfaceColor(): string | Color;
45+
createShape(key: string, options: ShapeProperties);
46+
getShape(key: string): any;
3147
}
3248

3349
export let themer: Themer;
@@ -39,6 +55,5 @@ export function install();
3955
export function installMixins();
4056
export function getRippleColor(color: string | Color): any;
4157

42-
4358
type Constructor<T = {}> = new (...args: any[]) => T;
4459
export function mixin<T1 extends Constructor, T2 extends Constructor>(mix1: T1, mix2: T2): (new (...args: any[]) => InstanceType<T1> & InstanceType<T2>) & T1 & T2;

0 commit comments

Comments
 (0)