Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions apps/app/ui-tests-app/css/elevation.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Label, Button {
text-align: center;
padding: 10;
margin: 16;
background-color: #bbb;
}

.elevation-0 {
android-elevation: 0;
}

.elevation-2 {
android-elevation: 2;
}

.elevation-4 {
android-elevation: 4;
}

.elevation-4:highlighted {
android-elevation: 2;
}

.elevation-8 {
android-elevation: 8;
}

.elevation-10 {
android-elevation: 10;
}

.pressed-z-10 {
android-dynamic-elevation-offset: 10;
}

.round {
color: #fff;
background-color: #ff1744;
border-radius: 50%; /* TODO kills elevation */
width: 80;
height: 80;
android-elevation: 8;
}
20 changes: 20 additions & 0 deletions apps/app/ui-tests-app/css/elevation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Button } from "tns-core-modules/ui/button/button";
import { EventData } from "tns-core-modules/ui/page/page";

const states = [
{ class: "", text: "default elevation" },
{ class: "elevation-10", text: "elevetion 10" },
{ class: "elevation-10 pressed-z-10", text: "elevetion 10 pressed-z 10" },
{ class: "elevation-0", text: "elevetion 0" },
]
let currentState = 0;

export function buttonTap(args: EventData) {
let btn: Button = args.object as Button;
currentState++;
if (currentState >= states.length) {
currentState = 0;
}
btn.className = states[currentState].class;
btn.text = states[currentState].text;
}
25 changes: 25 additions & 0 deletions apps/app/ui-tests-app/css/elevation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
<ScrollView>
<StackLayout backgroundColor="aliceblue" androidElevation="2" padding="10" margin="20">
<Button text="default elevation" tap="buttonTap"/>

<Button class="elevation-0" text="elevation 0"/>

<Label class="elevation-2" text="Label with elevation 2"/>

<StackLayout androidElevation="4" backgroundColor="#ff1744" horizontalAlignment="center" margin="20">
<Button class="elevation-2" text="elevation 2"/>
<Button class="elevation-4" text="elevation 4"/>
</StackLayout>

<StackLayout orientation="horizontal" horizontalAlignment="center">
<Button class="round" androidElevation="4" text="el. 4"/>
<Button class="elevation-8 round" text="el. 8"/>
</StackLayout>

<Label class="elevation-8" text="elevation 8"/>
<Button class="elevation-8" text="elevation 8, with radius" borderRadius="4"/>
<Button class="elevation-10" text="elevation 10"/>
</StackLayout>
</ScrollView>
</Page>
1 change: 1 addition & 0 deletions apps/app/ui-tests-app/css/main-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function loadExamples() {
examples.set("margins-paddings-with-percentage", "css/margins-paddings-with-percentage");
examples.set("padding-and-border", "css/padding-and-border");
examples.set("combinators", "css/combinators");
examples.set("elevation", "css/elevation");
examples.set("styled-formatted-text", "css/styled-formatted-text");
examples.set("non-uniform-radius", "css/non-uniform-radius");
examples.set("missing-background-image", "css/missing-background-image");
Expand Down
26 changes: 25 additions & 1 deletion tns-core-modules/ui/button/button.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
Length, zIndexProperty, textAlignmentProperty, TextAlignment
} from "./button-common";
import { androidElevationProperty, androidDynamicElevationOffsetProperty } from "../styling/style-properties";
import { profile } from "../../profiling";
import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures";
import { device } from "../../platform";
import lazy from "../../utils/lazy";

export * from "./button-common";

const sdkVersion = lazy(() => parseInt(device.sdkVersion));

interface ClickListener {
new(owner: Button): android.view.View.OnClickListener;
}
Expand Down Expand Up @@ -146,9 +151,28 @@ export class Button extends ButtonBase {
org.nativescript.widgets.ViewHelper.setZIndex(this.nativeViewProtected, value);
}

[androidElevationProperty.getDefault](): number {
if (sdkVersion() < 21) {
return 0;
}

// NOTE: Button widget has StateListAnimator that defines the elevation value and
// at the time of the getDefault() query the animator is not applied yet so we
// return the hardcoded @dimen/button_elevation_material value 2dp here instead
return 2;
}

[androidDynamicElevationOffsetProperty.getDefault](): number {
if (sdkVersion() < 21) {
return 0;
}

return 4; // 4dp @dimen/button_pressed_z_material
}

[textAlignmentProperty.setNative](value: TextAlignment) {
// Button initial value is center.
const newValue = value === "initial" ? "center" : value;
super[textAlignmentProperty.setNative](newValue);
}
}
}
2 changes: 1 addition & 1 deletion tns-core-modules/ui/core/view-base/view-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ export const classNameProperty = new Property<ViewBase, string>({
valueChanged(view: ViewBase, oldValue: string, newValue: string) {
let classes = view.cssClasses;
classes.clear();
if (typeof newValue === "string") {
if (typeof newValue === "string" && newValue !== "") {
newValue.split(" ").forEach(c => classes.add(c));
}
view._onCssStateChange();
Expand Down
14 changes: 14 additions & 0 deletions tns-core-modules/ui/core/view/view-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,20 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
this.style.scaleY = value;
}

get androidElevation(): number {
return this.style.androidElevation;
}
set androidElevation(value: number) {
this.style.androidElevation = value;
}

get androidDynamicElevationOffset(): number {
return this.style.androidDynamicElevationOffset;
}
set androidDynamicElevationOffset(value: number) {
this.style.androidDynamicElevationOffset = value;
}

//END Style property shortcuts

public automationText: string;
Expand Down
84 changes: 81 additions & 3 deletions tns-core-modules/ui/core/view/view.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,27 @@ import {
minWidthProperty, minHeightProperty, widthProperty, heightProperty,
marginLeftProperty, marginTopProperty, marginRightProperty, marginBottomProperty,
rotateProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty,
zIndexProperty, backgroundInternalProperty
zIndexProperty, backgroundInternalProperty, androidElevationProperty, androidDynamicElevationOffsetProperty
} from "../../styling/style-properties";

import { Background, ad as androidBackground } from "../../styling/background";
import { profile } from "../../../profiling";
import { topmost } from "../../frame/frame-stack";
import { AndroidActivityBackPressedEventData, android as androidApp } from "../../../application";
import { device } from "../../../platform";
import lazy from "../../../utils/lazy";

export * from "./view-common";

const DOMID = "_domId";
const androidBackPressedEvent = "androidBackPressed";

const shortAnimTime = 17694720; // android.R.integer.config_shortAnimTime
const statePressed = 16842919; // android.R.attr.state_pressed
const stateEnabled = 16842910; // android.R.attr.state_enabled

const sdkVersion = lazy(() => parseInt(device.sdkVersion));

const modalMap = new Map<number, DialogOptions>();

let TouchListener: TouchListener;
Expand Down Expand Up @@ -255,6 +263,8 @@ export class View extends ViewCommon {
private layoutChangeListener: android.view.View.OnLayoutChangeListener;
private _manager: android.support.v4.app.FragmentManager;
private _rootManager: android.support.v4.app.FragmentManager;
private _originalElevation: number;
private _originalStateListAnimator: any; /* android.animation.StateListAnimator; */

nativeViewProtected: android.view.View;

Expand Down Expand Up @@ -709,6 +719,74 @@ export class View extends ViewCommon {
this.nativeViewProtected.setAlpha(float(value));
}

[androidElevationProperty.getDefault](): number {
if (sdkVersion() < 21) {
return 0;
}

// NOTE: overriden in Button implementation as for widgets with StateListAnimator (Button)
// nativeView.getElevation() returns 0 at the time of the getDefault() query
return layout.toDeviceIndependentPixels((<any>this.nativeViewProtected).getElevation());
}
[androidElevationProperty.setNative](value: number) {
if (sdkVersion() < 21) {
return;
}

this.refreshStateListAnimator();
}

[androidDynamicElevationOffsetProperty.getDefault](): number {
return 0;
}
[androidDynamicElevationOffsetProperty.setNative](value: number) {
if (sdkVersion() < 21) {
return;
}

this.refreshStateListAnimator();
}

private refreshStateListAnimator() {
const nativeView: any = this.nativeViewProtected;

const ObjectAnimator = android.animation.ObjectAnimator;
const AnimatorSet = android.animation.AnimatorSet;

const duration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2;
const elevation = layout.toDevicePixels(this.androidElevation || 0);
const z = layout.toDevicePixels(0);
const pressedZ = layout.toDevicePixels(this.androidDynamicElevationOffset || 0);

const pressedSet = new AnimatorSet();
pressedSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ])
.setDuration(duration),
ObjectAnimator.ofFloat(nativeView, "elevation", [elevation])
.setDuration(0),
]));

const notPressedSet = new AnimatorSet();
notPressedSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [z])
.setDuration(duration),
ObjectAnimator.ofFloat(nativeView, "elevation", [elevation])
.setDuration(0),
]));

const defaultSet = new AnimatorSet();
defaultSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0),
ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0),
]));

const stateListAnimator = new (<any>android.animation).StateListAnimator();
stateListAnimator.addState([statePressed, stateEnabled], pressedSet);
stateListAnimator.addState([stateEnabled], notPressedSet);
stateListAnimator.addState([], defaultSet);
nativeView.setStateListAnimator(stateListAnimator);
}

[horizontalAlignmentProperty.getDefault](): HorizontalAlignment {
return <HorizontalAlignment>org.nativescript.widgets.ViewHelper.getHorizontalAlignment(this.nativeViewProtected);
}
Expand Down Expand Up @@ -946,7 +1024,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO
const { getter, setter, auto = 0 } = options;
let setPixels, getPixels, setPercent;
if (getter) {
View.prototype[getter] = function (this: View): PercentLength {
View.prototype[getter] = function(this: View): PercentLength {
if (options) {
setPixels = options.setPixels;
getPixels = options.getPixels;
Expand All @@ -962,7 +1040,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO
}
}
if (setter) {
View.prototype[setter] = function (this: View, length: PercentLength) {
View.prototype[setter] = function(this: View, length: PercentLength) {
if (options) {
setPixels = options.setPixels;
getPixels = options.getPixels;
Expand Down
10 changes: 10 additions & 0 deletions tns-core-modules/ui/core/view/view.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ export abstract class View extends ViewBase {
*/
color: Color;

/**
* Gets or sets the elevation of the android view.
*/
androidElevation: number;

/**
* Gets or sets the dynamic elevation offset of the android view.
*/
androidDynamicElevationOffset: number;

/**
* Gets or sets the background style property.
*/
Expand Down
6 changes: 6 additions & 0 deletions tns-core-modules/ui/styling/style-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1099,3 +1099,9 @@ export const visibilityProperty = new CssProperty<Style, Visibility>({
}
});
visibilityProperty.register(Style);

export const androidElevationProperty = new CssProperty<Style, number>({ name: "androidElevation", cssName: "android-elevation", valueConverter: parseFloat });
androidElevationProperty.register(Style);

export const androidDynamicElevationOffsetProperty = new CssProperty<Style, number>({ name: "androidDynamicElevationOffset", cssName: "android-dynamic-elevation-offset", valueConverter: parseFloat });
androidDynamicElevationOffsetProperty.register(Style);
8 changes: 5 additions & 3 deletions tns-core-modules/ui/styling/style/style.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export class Style extends Observable {
public fontWeight: FontWeight;
public font: string;

public androidElevation: number;
public androidDynamicElevationOffset: number;
public zIndex: number;
public opacity: number;
public visibility: Visibility;
Expand Down Expand Up @@ -127,13 +129,13 @@ export class Style extends Observable {
public selectedTabTextColor: Color;
public androidSelectedTabHighlightColor: Color;

// ListView-specific props
// ListView-specific props
public separatorColor: Color;

//SegmentedBar-specific props
public selectedBackgroundColor: Color;

// Page-specific props
// Page-specific props
public statusBarStyle: "light" | "dark";
public androidStatusBarBackground: Color;

Expand Down Expand Up @@ -167,4 +169,4 @@ interface PropertyBagClass {
}
interface PropertyBag {
[property: string]: string;
}
}
Loading