mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Call native setters once when a page is instantiated. Fix background not being reset, Label's background is now set to background, not layer Make the nativeView field instead of property for performance reasons
800 lines
26 KiB
TypeScript
800 lines
26 KiB
TypeScript
// Definitions.
|
|
import { View as ViewDefinition, Point, Size, Color, dip } from ".";
|
|
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
|
|
|
|
// Types.
|
|
import { Source } from "../../../utils/debug";
|
|
|
|
import {
|
|
ViewBase, Property, booleanConverter, EventData, layout,
|
|
getEventOrGestureName, traceEnabled, traceWrite, traceCategories
|
|
} from "../view-base";
|
|
|
|
import {
|
|
observe as gestureObserve,
|
|
GesturesObserver,
|
|
GestureTypes,
|
|
GestureEventData,
|
|
fromString as gestureFromString
|
|
} from "../../gestures";
|
|
|
|
export * from "../../styling/style-properties";
|
|
export * from "../view-base";
|
|
|
|
import * as am from "../../animation";
|
|
let animationModule: typeof am;
|
|
function ensureAnimationModule() {
|
|
if (!animationModule) {
|
|
animationModule = require("ui/animation");
|
|
}
|
|
}
|
|
|
|
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator {
|
|
let stateEventNames = pseudoClasses.map(s => ":" + s);
|
|
let listeners = Symbol("listeners");
|
|
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => {
|
|
function update(change: number) {
|
|
let prev = this[listeners] || 0;
|
|
let next = prev + change;
|
|
if (prev <= 0 && next > 0) {
|
|
this[propertyKey](true);
|
|
} else if (prev > 0 && next <= 0) {
|
|
this[propertyKey](false);
|
|
}
|
|
}
|
|
stateEventNames.forEach(s => target[s] = update);
|
|
};
|
|
}
|
|
|
|
export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|
private _measuredWidth: number;
|
|
private _measuredHeight: number;
|
|
|
|
_currentWidthMeasureSpec: number;
|
|
_currentHeightMeasureSpec: number;
|
|
|
|
_setMinWidthNative: (value: Length) => void;
|
|
_setMinHeightNative: (value: Length) => void;
|
|
|
|
private _isLayoutValid: boolean;
|
|
private _cssType: string;
|
|
|
|
public _gestureObservers = {};
|
|
|
|
observe(type: GestureTypes, callback: (args: GestureEventData) => void, thisArg?: any): void {
|
|
if (!this._gestureObservers[type]) {
|
|
this._gestureObservers[type] = [];
|
|
}
|
|
|
|
this._gestureObservers[type].push(gestureObserve(this, type, callback, thisArg));
|
|
}
|
|
|
|
public getGestureObservers(type: GestureTypes): Array<GesturesObserver> {
|
|
return this._gestureObservers[type];
|
|
}
|
|
|
|
public addEventListener(arg: string | GestureTypes, callback: (data: EventData) => void, thisArg?: any) {
|
|
if (typeof arg === "string") {
|
|
arg = getEventOrGestureName(arg);
|
|
|
|
let gesture = gestureFromString(arg);
|
|
if (gesture && !this._isEvent(arg)) {
|
|
this.observe(gesture, callback, thisArg);
|
|
} else {
|
|
let events = (arg).split(",");
|
|
if (events.length > 0) {
|
|
for (let i = 0; i < events.length; i++) {
|
|
let evt = events[i].trim();
|
|
let gst = gestureFromString(evt);
|
|
if (gst && !this._isEvent(arg)) {
|
|
this.observe(gst, callback, thisArg);
|
|
} else {
|
|
super.addEventListener(evt, callback, thisArg);
|
|
}
|
|
}
|
|
} else {
|
|
super.addEventListener(arg, callback, thisArg);
|
|
}
|
|
}
|
|
} else if (typeof arg === "number") {
|
|
this.observe(<GestureTypes>arg, callback, thisArg);
|
|
}
|
|
}
|
|
|
|
public removeEventListener(arg: string | GestureTypes, callback?: any, thisArg?: any) {
|
|
if (typeof arg === "string") {
|
|
let gesture = gestureFromString(arg);
|
|
if (gesture && !this._isEvent(arg)) {
|
|
this._disconnectGestureObservers(gesture);
|
|
} else {
|
|
let events = arg.split(",");
|
|
if (events.length > 0) {
|
|
for (let i = 0; i < events.length; i++) {
|
|
let evt = events[i].trim();
|
|
let gst = gestureFromString(evt);
|
|
if (gst && !this._isEvent(arg)) {
|
|
this._disconnectGestureObservers(gst);
|
|
} else {
|
|
super.removeEventListener(evt, callback, thisArg);
|
|
}
|
|
}
|
|
} else {
|
|
super.removeEventListener(arg, callback, thisArg);
|
|
}
|
|
|
|
}
|
|
} else if (typeof arg === "number") {
|
|
this._disconnectGestureObservers(<GestureTypes>arg);
|
|
}
|
|
}
|
|
|
|
private _isEvent(name: string): boolean {
|
|
return this.constructor && `${name}Event` in this.constructor;
|
|
}
|
|
|
|
private _disconnectGestureObservers(type: GestureTypes): void {
|
|
let observers = this.getGestureObservers(type);
|
|
if (observers) {
|
|
for (let i = 0; i < observers.length; i++) {
|
|
observers[i].disconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
// START Style property shortcuts
|
|
get borderColor(): string | Color {
|
|
return this.style.borderColor;
|
|
}
|
|
set borderColor(value: string | Color) {
|
|
this.style.borderColor = value;
|
|
}
|
|
|
|
get borderTopColor(): Color {
|
|
return this.style.borderTopColor;
|
|
}
|
|
set borderTopColor(value: Color) {
|
|
this.style.borderTopColor = value;
|
|
}
|
|
|
|
get borderRightColor(): Color {
|
|
return this.style.borderRightColor;
|
|
}
|
|
set borderRightColor(value: Color) {
|
|
this.style.borderRightColor = value;
|
|
}
|
|
|
|
get borderBottomColor(): Color {
|
|
return this.style.borderBottomColor;
|
|
}
|
|
set borderBottomColor(value: Color) {
|
|
this.style.borderBottomColor = value;
|
|
}
|
|
|
|
get borderLeftColor(): Color {
|
|
return this.style.borderLeftColor;
|
|
}
|
|
set borderLeftColor(value: Color) {
|
|
this.style.borderLeftColor = value;
|
|
}
|
|
|
|
get borderWidth(): string | Length {
|
|
return this.style.borderWidth;
|
|
}
|
|
set borderWidth(value: string | Length) {
|
|
this.style.borderWidth = value;
|
|
}
|
|
|
|
get borderTopWidth(): Length {
|
|
return this.style.borderTopWidth;
|
|
}
|
|
set borderTopWidth(value: Length) {
|
|
this.style.borderTopWidth = value;
|
|
}
|
|
|
|
get borderRightWidth(): Length {
|
|
return this.style.borderRightWidth;
|
|
}
|
|
set borderRightWidth(value: Length) {
|
|
this.style.borderRightWidth = value;
|
|
}
|
|
|
|
get borderBottomWidth(): Length {
|
|
return this.style.borderBottomWidth;
|
|
}
|
|
set borderBottomWidth(value: Length) {
|
|
this.style.borderBottomWidth = value;
|
|
}
|
|
|
|
get borderLeftWidth(): Length {
|
|
return this.style.borderLeftWidth;
|
|
}
|
|
set borderLeftWidth(value: Length) {
|
|
this.style.borderLeftWidth = value;
|
|
}
|
|
|
|
get borderRadius(): string | Length {
|
|
return this.style.borderRadius;
|
|
}
|
|
set borderRadius(value: string | Length) {
|
|
this.style.borderRadius = value;
|
|
}
|
|
|
|
get borderTopLeftRadius(): Length {
|
|
return this.style.borderTopLeftRadius;
|
|
}
|
|
set borderTopLeftRadius(value: Length) {
|
|
this.style.borderTopLeftRadius = value;
|
|
}
|
|
|
|
get borderTopRightRadius(): Length {
|
|
return this.style.borderTopRightRadius;
|
|
}
|
|
set borderTopRightRadius(value: Length) {
|
|
this.style.borderTopRightRadius = value;
|
|
}
|
|
|
|
get borderBottomRightRadius(): Length {
|
|
return this.style.borderBottomRightRadius;
|
|
}
|
|
set borderBottomRightRadius(value: Length) {
|
|
this.style.borderBottomRightRadius = value;
|
|
}
|
|
|
|
get borderBottomLeftRadius(): Length {
|
|
return this.style.borderBottomLeftRadius;
|
|
}
|
|
set borderBottomLeftRadius(value: Length) {
|
|
this.style.borderBottomLeftRadius = value;
|
|
}
|
|
|
|
get color(): Color {
|
|
return this.style.color;
|
|
}
|
|
set color(value: Color) {
|
|
this.style.color = value;
|
|
}
|
|
|
|
get backgroundColor(): Color {
|
|
return this.style.backgroundColor;
|
|
}
|
|
set backgroundColor(value: Color) {
|
|
this.style.backgroundColor = value;
|
|
}
|
|
|
|
get backgroundImage(): string {
|
|
return this.style.backgroundImage;
|
|
}
|
|
set backgroundImage(value: string) {
|
|
this.style.backgroundImage = value;
|
|
}
|
|
|
|
get minWidth(): Length {
|
|
return this.style.minWidth;
|
|
}
|
|
set minWidth(value: Length) {
|
|
this.style.minWidth = value;
|
|
}
|
|
|
|
get minHeight(): Length {
|
|
return this.style.minHeight;
|
|
}
|
|
set minHeight(value: Length) {
|
|
this.style.minHeight = value;
|
|
}
|
|
|
|
get width(): PercentLength {
|
|
return this.style.width;
|
|
}
|
|
set width(value: PercentLength) {
|
|
this.style.width = value;
|
|
}
|
|
|
|
get height(): PercentLength {
|
|
return this.style.height;
|
|
}
|
|
set height(value: PercentLength) {
|
|
this.style.height = value;
|
|
}
|
|
|
|
get margin(): string | PercentLength {
|
|
return this.style.margin;
|
|
}
|
|
set margin(value: string | PercentLength) {
|
|
this.style.margin = value;
|
|
}
|
|
|
|
get marginLeft(): PercentLength {
|
|
return this.style.marginLeft;
|
|
}
|
|
set marginLeft(value: PercentLength) {
|
|
this.style.marginLeft = value;
|
|
}
|
|
|
|
get marginTop(): PercentLength {
|
|
return this.style.marginTop;
|
|
}
|
|
set marginTop(value: PercentLength) {
|
|
this.style.marginTop = value;
|
|
}
|
|
|
|
get marginRight(): PercentLength {
|
|
return this.style.marginRight;
|
|
}
|
|
set marginRight(value: PercentLength) {
|
|
this.style.marginRight = value;
|
|
}
|
|
|
|
get marginBottom(): PercentLength {
|
|
return this.style.marginBottom;
|
|
}
|
|
set marginBottom(value: PercentLength) {
|
|
this.style.marginBottom = value;
|
|
}
|
|
|
|
get horizontalAlignment(): HorizontalAlignment {
|
|
return this.style.horizontalAlignment;
|
|
}
|
|
set horizontalAlignment(value: HorizontalAlignment) {
|
|
this.style.horizontalAlignment = value;
|
|
}
|
|
|
|
get verticalAlignment(): VerticalAlignment {
|
|
return this.style.verticalAlignment;
|
|
}
|
|
set verticalAlignment(value: VerticalAlignment) {
|
|
this.style.verticalAlignment = value;
|
|
}
|
|
|
|
get visibility(): Visibility {
|
|
return this.style.visibility;
|
|
}
|
|
set visibility(value: Visibility) {
|
|
this.style.visibility = value;
|
|
}
|
|
|
|
get opacity(): number {
|
|
return this.style.opacity;
|
|
}
|
|
set opacity(value: number) {
|
|
this.style.opacity = value;
|
|
}
|
|
|
|
get rotate(): number {
|
|
return this.style.rotate;
|
|
}
|
|
set rotate(value: number) {
|
|
this.style.rotate = value;
|
|
}
|
|
|
|
get translateX(): dip {
|
|
return this.style.translateX;
|
|
}
|
|
set translateX(value: dip) {
|
|
this.style.translateX = value;
|
|
}
|
|
|
|
get translateY(): dip {
|
|
return this.style.translateY;
|
|
}
|
|
set translateY(value: dip) {
|
|
this.style.translateY = value;
|
|
}
|
|
|
|
get scaleX(): number {
|
|
return this.style.scaleX;
|
|
}
|
|
set scaleX(value: number) {
|
|
this.style.scaleX = value;
|
|
}
|
|
|
|
get scaleY(): number {
|
|
return this.style.scaleY;
|
|
}
|
|
set scaleY(value: number) {
|
|
this.style.scaleY = value;
|
|
}
|
|
|
|
//END Style property shortcuts
|
|
|
|
public automationText: string;
|
|
public originX: number;
|
|
public originY: number;
|
|
public isEnabled: boolean;
|
|
public isUserInteractionEnabled: boolean;
|
|
|
|
get isLayoutValid(): boolean {
|
|
return this._isLayoutValid;
|
|
}
|
|
|
|
get cssType(): string {
|
|
if (!this._cssType) {
|
|
this._cssType = this.typeName.toLowerCase();
|
|
}
|
|
return this._cssType;
|
|
}
|
|
|
|
get isLayoutRequired(): boolean {
|
|
return true;
|
|
}
|
|
|
|
public measure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
|
this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
|
|
public layout(left: number, top: number, right: number, bottom: number): void {
|
|
this._setCurrentLayoutBounds(left, top, right, bottom);
|
|
}
|
|
|
|
public getMeasuredWidth(): number {
|
|
return this._measuredWidth & layout.MEASURED_SIZE_MASK || 0;
|
|
}
|
|
|
|
public getMeasuredHeight(): number {
|
|
return this._measuredHeight & layout.MEASURED_SIZE_MASK || 0;
|
|
}
|
|
|
|
public getMeasuredState(): number {
|
|
return (this._measuredWidth & layout.MEASURED_STATE_MASK)
|
|
| ((this._measuredHeight >> layout.MEASURED_HEIGHT_STATE_SHIFT)
|
|
& (layout.MEASURED_STATE_MASK >> layout.MEASURED_HEIGHT_STATE_SHIFT));
|
|
}
|
|
|
|
public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void {
|
|
this._measuredWidth = measuredWidth;
|
|
this._measuredHeight = measuredHeight;
|
|
if (traceEnabled()) {
|
|
traceWrite(this + " :setMeasuredDimension: " + measuredWidth + ", " + measuredHeight, traceCategories.Layout);
|
|
}
|
|
}
|
|
|
|
public requestLayout(): void {
|
|
this._isLayoutValid = false;
|
|
}
|
|
|
|
public abstract onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void;
|
|
public abstract onLayout(left: number, top: number, right: number, bottom: number): void;
|
|
public abstract layoutNativeView(left: number, top: number, right: number, bottom: number): void;
|
|
|
|
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number {
|
|
let result = size;
|
|
switch (specMode) {
|
|
case layout.UNSPECIFIED:
|
|
result = Math.ceil(size);
|
|
break;
|
|
|
|
case layout.AT_MOST:
|
|
if (specSize < size) {
|
|
result = Math.ceil(specSize) | layout.MEASURED_STATE_TOO_SMALL;
|
|
}
|
|
break;
|
|
|
|
case layout.EXACTLY:
|
|
result = Math.ceil(specSize);
|
|
break;
|
|
}
|
|
|
|
return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
|
|
}
|
|
|
|
public static combineMeasuredStates(curState: number, newState): number {
|
|
return curState | newState;
|
|
}
|
|
|
|
public static layoutChild(parent: ViewDefinition, child: ViewDefinition, left: number, top: number, right: number, bottom: number): void {
|
|
if (!child || child.isCollapsed) {
|
|
return;
|
|
}
|
|
|
|
let childStyle = child.style;
|
|
|
|
let childTop: number;
|
|
let childLeft: number;
|
|
|
|
let childWidth = child.getMeasuredWidth();
|
|
let childHeight = child.getMeasuredHeight();
|
|
|
|
let effectiveMarginTop = child.effectiveMarginTop;
|
|
let effectiveMarginBottom = child.effectiveMarginBottom;
|
|
|
|
let vAlignment: VerticalAlignment;
|
|
if (child.effectiveHeight >= 0 && childStyle.verticalAlignment === "stretch") {
|
|
vAlignment = "middle";
|
|
}
|
|
else {
|
|
vAlignment = childStyle.verticalAlignment;
|
|
}
|
|
|
|
switch (vAlignment) {
|
|
case "top":
|
|
childTop = top + effectiveMarginTop;
|
|
break;
|
|
|
|
case "middle":
|
|
childTop = top + (bottom - top - childHeight + (effectiveMarginTop - effectiveMarginBottom)) / 2;
|
|
break;
|
|
|
|
case "bottom":
|
|
childTop = bottom - childHeight - effectiveMarginBottom;
|
|
break;
|
|
|
|
case "stretch":
|
|
default:
|
|
childTop = top + effectiveMarginTop;
|
|
childHeight = bottom - top - (effectiveMarginTop + effectiveMarginBottom);
|
|
break;
|
|
}
|
|
|
|
let effectiveMarginLeft = child.effectiveMarginLeft;
|
|
let effectiveMarginRight = child.effectiveMarginRight;
|
|
|
|
let hAlignment: HorizontalAlignment;
|
|
if (child.effectiveWidth >= 0 && childStyle.horizontalAlignment === "stretch") {
|
|
hAlignment = "center";
|
|
}
|
|
else {
|
|
hAlignment = childStyle.horizontalAlignment;
|
|
}
|
|
|
|
switch (hAlignment) {
|
|
case "left":
|
|
childLeft = left + effectiveMarginLeft;
|
|
break;
|
|
|
|
case "center":
|
|
childLeft = left + (right - left - childWidth + (effectiveMarginLeft - effectiveMarginRight)) / 2;
|
|
break;
|
|
|
|
case "right":
|
|
childLeft = right - childWidth - effectiveMarginRight;
|
|
break;
|
|
|
|
case "stretch":
|
|
default:
|
|
childLeft = left + effectiveMarginLeft;
|
|
childWidth = right - left - (effectiveMarginLeft + effectiveMarginRight);
|
|
break;
|
|
}
|
|
|
|
let childRight = Math.round(childLeft + childWidth);
|
|
let childBottom = Math.round(childTop + childHeight);
|
|
childLeft = Math.round(childLeft);
|
|
childTop = Math.round(childTop);
|
|
|
|
if (traceEnabled()) {
|
|
traceWrite(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, traceCategories.Layout);
|
|
}
|
|
|
|
child.layout(childLeft, childTop, childRight, childBottom);
|
|
}
|
|
|
|
public static measureChild(parent: ViewCommon, child: ViewCommon, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number } {
|
|
let measureWidth = 0;
|
|
let measureHeight = 0;
|
|
|
|
if (child && !child.isCollapsed) {
|
|
child._updateEffectiveLayoutValues(parent);
|
|
|
|
let style = child.style;
|
|
let horizontalMargins = child.effectiveMarginLeft + child.effectiveMarginRight;
|
|
let verticalMargins = child.effectiveMarginTop + child.effectiveMarginBottom;
|
|
|
|
let childWidthMeasureSpec = ViewCommon.getMeasureSpec(widthMeasureSpec, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch");
|
|
let childHeightMeasureSpec = ViewCommon.getMeasureSpec(heightMeasureSpec, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch");
|
|
|
|
if (traceEnabled()) {
|
|
traceWrite(child.parent + " :measureChild: " + child + " " + layout.measureSpecToString(childWidthMeasureSpec) + ", " + layout.measureSpecToString(childHeightMeasureSpec), traceCategories.Layout);
|
|
}
|
|
|
|
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
|
measureWidth = Math.round(child.getMeasuredWidth() + horizontalMargins);
|
|
measureHeight = Math.round(child.getMeasuredHeight() + verticalMargins);
|
|
}
|
|
|
|
return { measuredWidth: measureWidth, measuredHeight: measureHeight };
|
|
}
|
|
|
|
private static getMeasureSpec(parentSpec: number, margins: number, childLength: number, stretched: boolean): number {
|
|
const parentLength = layout.getMeasureSpecSize(parentSpec);
|
|
const parentSpecMode = layout.getMeasureSpecMode(parentSpec);
|
|
|
|
let resultSize: number;
|
|
let resultMode: number;
|
|
|
|
// We want a specific size... let be it.
|
|
if (childLength >= 0) {
|
|
// If mode !== UNSPECIFIED we take the smaller of parentLength and childLength
|
|
// Otherwise we will need to clip the view but this is not possible in all Android API levels.
|
|
// TODO: remove Math.min(parentLength, childLength)
|
|
resultSize = parentSpecMode === layout.UNSPECIFIED ? childLength : Math.min(parentLength, childLength);
|
|
resultMode = layout.EXACTLY;
|
|
}
|
|
else {
|
|
switch (parentSpecMode) {
|
|
// Parent has imposed an exact size on us
|
|
case layout.EXACTLY:
|
|
resultSize = Math.max(0, parentLength - margins);
|
|
// if stretched - nativeView wants to be our size. So be it.
|
|
// else - nativeView wants to determine its own size. It can't be bigger than us.
|
|
resultMode = stretched ? layout.EXACTLY : layout.AT_MOST;
|
|
break;
|
|
|
|
// Parent has imposed a maximum size on us
|
|
case layout.AT_MOST:
|
|
resultSize = Math.max(0, parentLength - margins);
|
|
resultMode = layout.AT_MOST;
|
|
break;
|
|
|
|
// Equivalent to measure with Infinity.
|
|
case layout.UNSPECIFIED:
|
|
resultSize = 0;
|
|
resultMode = layout.UNSPECIFIED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return layout.makeMeasureSpec(resultSize, resultMode);
|
|
}
|
|
|
|
_setCurrentMeasureSpecs(widthMeasureSpec: number, heightMeasureSpec: number): boolean {
|
|
let changed: boolean = this._currentWidthMeasureSpec !== widthMeasureSpec || this._currentHeightMeasureSpec !== heightMeasureSpec;
|
|
this._currentWidthMeasureSpec = widthMeasureSpec;
|
|
this._currentHeightMeasureSpec = heightMeasureSpec;
|
|
return changed;
|
|
}
|
|
|
|
_getCurrentLayoutBounds(): { left: number; top: number; right: number; bottom: number } {
|
|
return { left: this._oldLeft, top: this._oldTop, right: this._oldRight, bottom: this._oldBottom };
|
|
}
|
|
|
|
/**
|
|
* Returns two booleans - the first if "boundsChanged" the second is "sizeChanged".
|
|
*/
|
|
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean } {
|
|
this._isLayoutValid = true;
|
|
let boundsChanged: boolean = this._oldLeft !== left || this._oldTop !== top || this._oldRight !== right || this._oldBottom !== bottom;
|
|
let sizeChanged: boolean = (this._oldRight - this._oldLeft !== right - left) || (this._oldBottom - this._oldTop !== bottom - top);
|
|
this._oldLeft = left;
|
|
this._oldTop = top;
|
|
this._oldRight = right;
|
|
this._oldBottom = bottom;
|
|
return { boundsChanged, sizeChanged };
|
|
}
|
|
|
|
public eachChild(callback: (child: ViewBase) => boolean): void {
|
|
this.eachChildView(<any>callback);
|
|
}
|
|
|
|
public eachChildView(callback: (view: ViewDefinition) => boolean) {
|
|
//
|
|
}
|
|
|
|
_getNativeViewsCount(): number {
|
|
return this._isAddedToNativeVisualTree ? 1 : 0;
|
|
}
|
|
|
|
_eachLayoutView(callback: (View) => void): void {
|
|
return callback(this);
|
|
}
|
|
|
|
public _updateLayout() {
|
|
// needed for iOS.
|
|
}
|
|
|
|
public focus(): boolean {
|
|
return undefined;
|
|
}
|
|
|
|
public getLocationInWindow(): Point {
|
|
return undefined;
|
|
}
|
|
|
|
public getLocationOnScreen(): Point {
|
|
return undefined;
|
|
}
|
|
|
|
public getLocationRelativeTo(otherView: ViewDefinition): Point {
|
|
return undefined;
|
|
}
|
|
|
|
public getActualSize(): Size {
|
|
let currentBounds = this._getCurrentLayoutBounds();
|
|
if (!currentBounds) {
|
|
return undefined;
|
|
}
|
|
|
|
return {
|
|
width: layout.toDeviceIndependentPixels(currentBounds.right - currentBounds.left),
|
|
height: layout.toDeviceIndependentPixels(currentBounds.bottom - currentBounds.top),
|
|
};
|
|
}
|
|
|
|
public animate(animation: any): am.AnimationPromise {
|
|
return this.createAnimation(animation).play();
|
|
}
|
|
|
|
public createAnimation(animation: any): am.Animation {
|
|
ensureAnimationModule();
|
|
animation.target = this;
|
|
return new animationModule.Animation([animation]);
|
|
}
|
|
|
|
public toString(): string {
|
|
let str = this.typeName;
|
|
if (this.id) {
|
|
str += `<${this.id}>`;
|
|
} else {
|
|
str += `(${this._domId})`;
|
|
}
|
|
let source = Source.get(this);
|
|
if (source) {
|
|
str += `@${source};`;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
public _setNativeViewFrame(nativeView: any, frame: any) {
|
|
//
|
|
}
|
|
|
|
public _getValue(): never {
|
|
throw new Error("The View._setValue is obsolete. There is a new property system.");
|
|
}
|
|
|
|
public _setValue(): never {
|
|
throw new Error("The View._setValue is obsolete. There is a new property system.");
|
|
}
|
|
|
|
_updateEffectiveLayoutValues(parent: ViewDefinition): void {
|
|
const style = this.style;
|
|
|
|
let parentWidthMeasureSpec = parent._currentWidthMeasureSpec;
|
|
let parentWidthMeasureSize = layout.getMeasureSpecSize(parentWidthMeasureSpec);
|
|
let parentWidthMeasureMode = layout.getMeasureSpecMode(parentWidthMeasureSpec);
|
|
let parentAvailableWidth = parentWidthMeasureMode === layout.UNSPECIFIED ? -1 : parentWidthMeasureSize;
|
|
|
|
this.effectiveWidth = PercentLength.toDevicePixels(style.width, -2, parentAvailableWidth);
|
|
this.effectiveMarginLeft = PercentLength.toDevicePixels(style.marginLeft, 0, parentAvailableWidth);
|
|
this.effectiveMarginRight = PercentLength.toDevicePixels(style.marginRight, 0, parentAvailableWidth);
|
|
|
|
let parentHeightMeasureSpec = parent._currentHeightMeasureSpec;
|
|
let parentHeightMeasureSize = layout.getMeasureSpecSize(parentHeightMeasureSpec);
|
|
let parentHeightMeasureMode = layout.getMeasureSpecMode(parentHeightMeasureSpec);
|
|
let parentAvailableHeight = parentHeightMeasureMode === layout.UNSPECIFIED ? -1 : parentHeightMeasureSize;
|
|
|
|
this.effectiveHeight = PercentLength.toDevicePixels(style.height, -2, parentAvailableHeight);
|
|
this.effectiveMarginTop = PercentLength.toDevicePixels(style.marginTop, 0, parentAvailableHeight);
|
|
this.effectiveMarginBottom = PercentLength.toDevicePixels(style.marginBottom, 0, parentAvailableHeight);
|
|
}
|
|
|
|
public _setNativeClipToBounds() {
|
|
//
|
|
}
|
|
|
|
public _redrawNativeBackground(value: any): void {
|
|
//
|
|
}
|
|
}
|
|
|
|
export const automationTextProperty = new Property<ViewCommon, string>({ name: "automationText" });
|
|
automationTextProperty.register(ViewCommon);
|
|
|
|
export const originXProperty = new Property<ViewCommon, number>({ name: "originX", defaultValue: 0.5, valueConverter: (v) => parseFloat(v) });
|
|
originXProperty.register(ViewCommon);
|
|
|
|
export const originYProperty = new Property<ViewCommon, number>({ name: "originY", defaultValue: 0.5, valueConverter: (v) => parseFloat(v) });
|
|
originYProperty.register(ViewCommon);
|
|
|
|
export const isEnabledProperty = new Property<ViewCommon, boolean>({
|
|
name: "isEnabled",
|
|
defaultValue: true,
|
|
valueConverter: booleanConverter,
|
|
valueChanged(this: void, target, oldValue, newValue): void {
|
|
target._goToVisualState(newValue ? "normal" : "disabled");
|
|
}
|
|
});
|
|
isEnabledProperty.register(ViewCommon);
|
|
|
|
export const isUserInteractionEnabledProperty = new Property<ViewCommon, boolean>({ name: "isUserInteractionEnabled", defaultValue: true, valueConverter: booleanConverter });
|
|
isUserInteractionEnabledProperty.register(ViewCommon);
|