Files

976 lines
37 KiB
TypeScript

import styling = require("ui/styling");
import types = require("utils/types");
import trace = require("trace");
import {DependencyObservable, PropertyMetadata, PropertyMetadataSettings, PropertyChangeData, Property, ValueSource, NativeValueResult} from "ui/core/dependency-observable";
import {View} from "ui/core/view";
import {Color} from "color";
import stylers = require("ui/styling/stylers");
import styleProperty = require("ui/styling/style-property");
import converters = require("./converters");
import enums = require("ui/enums");
import imageSource = require("image-source");
import utils = require("utils/utils");
import font = require("ui/styling/font");
import background = require("ui/styling/background");
import platform = require("platform");
// key is the property id and value is Dictionary<string, StylePropertyChangedHandler>;
var _registeredHandlers = Array<Object>();
// key is a className + property id and value is StylePropertyChangedHandler;
var _handlersCache = {};
// classes like Frame that does not need to handle styling properties.
var noStylingClasses = {};
// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
export interface Thickness {
left: number;
top: number;
right: number;
bottom: number;
}
export interface CommonLayoutParams {
width: number;
height: number;
leftMargin: number;
topMargin: number;
rightMargin: number;
bottomMargin: number;
horizontalAlignment: string;
verticalAlignment: string;
}
function parseThickness(value: any): Thickness {
var result: Thickness = { top: 0, right: 0, bottom: 0, left: 0 };
if (types.isString(value)) {
var arr = value.split(/[ ,]+/);
var top = parseInt(arr[0]);
top = isNaN(top) ? 0 : top;
var right = parseInt(arr[1]);
right = isNaN(right) ? top : right;
var bottom = parseInt(arr[2]);
bottom = isNaN(bottom) ? top : bottom;
var left = parseInt(arr[3]);
left = isNaN(left) ? right : left;
result.top = top;
result.right = right;
result.bottom = bottom;
result.left = left;
} else if (types.isNumber(value)) {
result.top = result.right = result.bottom = result.left = value;
}
else {
result = value;
}
return result;
}
function layoutParamsComparer(x: CommonLayoutParams, y: CommonLayoutParams): boolean {
return x.width === y.width
&& x.height === y.height
&& x.leftMargin === y.leftMargin
&& x.topMargin === y.topMargin
&& x.rightMargin === y.rightMargin
&& x.bottomMargin === y.bottomMargin
&& x.horizontalAlignment === y.horizontalAlignment
&& x.verticalAlignment === y.verticalAlignment
}
function onLayoutParamsChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var layoutParams: CommonLayoutParams = {
width: isNaN(style.width) ? -1 : style.width,
height: isNaN(style.height) ? -1 : style.height,
leftMargin: style.marginLeft,
topMargin: style.marginTop,
rightMargin: style.marginRight,
bottomMargin: style.marginBottom,
horizontalAlignment: style.horizontalAlignment,
verticalAlignment: style.verticalAlignment
};
style._setValue(nativeLayoutParamsProperty, layoutParams);
}
function onPaddingValueChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var thickness: Thickness = {
top: style.paddingTop,
right: style.paddingRight,
bottom: style.paddingBottom,
left: style.paddingLeft
};
style._setValue(nativePaddingsProperty, thickness);
}
function thicknessComparer(x: Thickness, y: Thickness): boolean {
if (x && y) {
return x.left === y.left && x.top === y.top && x.right === y.right && x.bottom === y.bottom;
}
return !x === !y;
}
function isWidthHeightValid(value: number): boolean {
return isNaN(value) || (value >= 0.0 && isFinite(value));
}
function isMinWidthHeightValid(value: number): boolean {
return !isNaN(value) && value >= 0.0 && isFinite(value);
}
function onBackgroundImagePropertyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var url: string = data.newValue;
var currentBackground = <background.Background>style._getValue(backgroundInternalProperty);
var isValid = false;
if (types.isString(data.newValue)) {
var pattern: RegExp = /url\(('|")(.*?)\1\)/;
var match = url.match(pattern);
if (match && match[2]) {
url = match[2];
}
if (utils.isDataURI(url)) {
var base64Data = url.split(",")[1];
if (types.isDefined(base64Data)) {
style._setValue(backgroundInternalProperty, currentBackground.withImage(imageSource.fromBase64(base64Data)));
isValid = true;
}
}
else if (utils.isFileOrResourcePath(url)) {
style._setValue(backgroundInternalProperty, currentBackground.withImage(imageSource.fromFileOrResource(url)));
isValid = true;
}
else if (url.indexOf("http") !== -1) {
style["_url"] = url;
style._setValue(backgroundInternalProperty, currentBackground.withImage(undefined));
imageSource.fromUrl(url).then((r) => {
if (style && style["_url"] === url) {
style._setValue(backgroundInternalProperty, currentBackground.withImage(r));
}
});
isValid = true;
}
}
if (!isValid) {
style._setValue(backgroundInternalProperty, currentBackground.withImage(undefined));
}
}
function onBackgroundColorPropertyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentBackground = <background.Background>style._getValue(backgroundInternalProperty);
if (!Color.equals(currentBackground.color, data.newValue)) {
style._setValue(backgroundInternalProperty, currentBackground.withColor(data.newValue));
}
}
function onBackgroundSizePropertyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentBackground = <background.Background>style._getValue(backgroundInternalProperty);
if (data.newValue !== currentBackground.size) {
style._setValue(backgroundInternalProperty, currentBackground.withSize(data.newValue));
}
}
function onBackgroundRepeatPropertyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentBackground = <background.Background>style._getValue(backgroundInternalProperty);
if (data.newValue !== currentBackground.repeat) {
style._setValue(backgroundInternalProperty, currentBackground.withRepeat(data.newValue));
}
}
function onBackgroundPositionPropertyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentBackground = <background.Background>style._getValue(backgroundInternalProperty);
if (data.newValue !== currentBackground.position) {
style._setValue(backgroundInternalProperty, currentBackground.withPosition(data.newValue));
}
}
function getHandlerInternal(propertyId: number, classInfo: types.ClassInfo): styling.stylers.StylePropertyChangedHandler {
var className = classInfo ? classInfo.name : "default";
var handlerKey = className + propertyId;
// try the cache first
var result = _handlersCache[handlerKey];
if (types.isDefined(result)) {
return result;
}
var propertyHandlers = _registeredHandlers[propertyId];
if (noStylingClasses.hasOwnProperty(className) || !propertyHandlers) {
// Reached 'no-styling' class or no property handlers are registered for this propertyID
result = null;
}
else if (propertyHandlers.hasOwnProperty(className)) {
// Found handler for this class
result = propertyHandlers[className];
}
else if (classInfo) {
// Check the base class
result = getHandlerInternal(propertyId, classInfo.baseClassInfo);
}
else {
result = null;
}
_handlersCache[handlerKey] = result;
return result;
}
function isVisibilityValid(value: string): boolean {
return value === enums.Visibility.visible || value === enums.Visibility.collapse || value === enums.Visibility.collapsed;
}
function isTextDecorationValid(value: string): boolean {
var values = (value + "").split(" ");
return values.indexOf(enums.TextDecoration.none) !== -1 || values.indexOf(enums.TextDecoration.underline) !== -1 || values.indexOf(enums.TextDecoration.lineThrough) !== -1;
}
function isTextTransformValid(value: string): boolean {
return value === enums.TextTransform.none || value === enums.TextTransform.uppercase || value === enums.TextTransform.lowercase || value === enums.TextTransform.capitalize;
}
function isWhiteSpaceValid(value: string): boolean {
return value === enums.WhiteSpace.nowrap || value === enums.WhiteSpace.normal;
}
function onVisibilityChanged(data: PropertyChangeData) {
(<any>data.object)._view._isVisibleCache = data.newValue === enums.Visibility.visible;
}
function isPaddingValid(value: number): boolean {
return isFinite(value) && !isNaN(value) && value >= 0;
}
function isMarginValid(value: number): boolean {
return isFinite(value) && !isNaN(value);
}
function isOpacityValid(value: string): boolean {
var parsedValue: number = parseFloat(value);
return !isNaN(parsedValue) && 0 <= parsedValue && parsedValue <= 1;
}
function isFontWeightValid(value: string): boolean {
return value === enums.FontWeight.normal || value === enums.FontWeight.bold;
}
function isFontStyleValid(value: string): boolean {
return value === enums.FontStyle.normal || value === enums.FontStyle.italic;
}
function onFontFamilyChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentFont = <font.Font>style._getValue(fontInternalProperty);
if (currentFont.fontFamily !== data.newValue) {
style._setValue(fontInternalProperty, currentFont.withFontFamily(data.newValue));
}
}
function onFontStyleChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentFont = <font.Font>style._getValue(fontInternalProperty);
if (currentFont.fontStyle !== data.newValue) {
style._setValue(fontInternalProperty, currentFont.withFontStyle(data.newValue));
}
}
function onFontWeightChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentFont = <font.Font>style._getValue(fontInternalProperty);
if (currentFont.fontWeight !== data.newValue) {
style._setValue(fontInternalProperty, currentFont.withFontWeight(data.newValue));
}
}
function onFontSizeChanged(data: PropertyChangeData) {
var style = <Style>data.object;
var currentFont = <font.Font>style._getValue(fontInternalProperty);
if (currentFont.fontSize !== data.newValue) {
style._setValue(fontInternalProperty, currentFont.withFontSize(data.newValue));
}
}
export class Style extends DependencyObservable implements styling.Style {
private _view: View;
private _updateCounter = 0;
private _nativeSetters = new Map<Property, any>();
get color(): Color {
return this._getValue(colorProperty);
}
set color(value: Color) {
this._setValue(colorProperty, value);
}
get backgroundColor(): Color {
return this._getValue(backgroundColorProperty);
}
set backgroundColor(value: Color) {
this._setValue(backgroundColorProperty, value);
}
get backgroundImage(): string {
return this._getValue(backgroundImageProperty);
}
set backgroundImage(value: string) {
this._setValue(backgroundImageProperty, value);
}
get backgroundRepeat(): string {
return this._getValue(backgroundRepeatProperty);
}
set backgroundRepeat(value: string) {
this._setValue(backgroundRepeatProperty, value);
}
get backgroundSize(): string {
return this._getValue(backgroundSizeProperty);
}
set backgroundSize(value: string) {
this._setValue(backgroundSizeProperty, value);
}
get backgroundPosition(): string {
return this._getValue(backgroundPositionProperty);
}
set backgroundPosition(value: string) {
this._setValue(backgroundPositionProperty, value);
}
get borderColor(): Color {
return this._getValue(borderColorProperty);
}
set borderColor(value: Color) {
this._setValue(borderColorProperty, value);
}
get borderWidth(): number {
return this._getValue(borderWidthProperty);
}
set borderWidth(value: number) {
this._setValue(borderWidthProperty, value);
}
get borderRadius(): number {
return this._getValue(borderRadiusProperty);
}
set borderRadius(value: number) {
this._setValue(borderRadiusProperty, value);
}
get fontSize(): number {
return this._getValue(fontSizeProperty);
}
set fontSize(value: number) {
this._setValue(fontSizeProperty, value);
}
get fontFamily(): string {
return this._getValue(fontFamilyProperty);
}
set fontFamily(value: string) {
this._setValue(fontFamilyProperty, value);
}
get fontStyle(): string {
return this._getValue(fontStyleProperty);
}
set fontStyle(value: string) {
this._setValue(fontStyleProperty, value);
}
get fontWeight(): string {
return this._getValue(fontWeightProperty);
}
set fontWeight(value: string) {
this._setValue(fontWeightProperty, value);
}
get font(): string {
return this.fontStyle + " " + this.fontWeight + " " + this.fontSize + " " + this.fontFamily;
}
set font(value: string) {
this._setShorthandProperty("font", value);
}
get textAlignment(): string {
return this._getValue(textAlignmentProperty);
}
set textAlignment(value: string) {
this._setValue(textAlignmentProperty, value);
}
get minWidth(): number {
return this._getValue(minWidthProperty);
}
set minWidth(value: number) {
this._setValue(minWidthProperty, value);
}
get minHeight(): number {
return this._getValue(minHeightProperty);
}
set minHeight(value: number) {
this._setValue(minHeightProperty, value);
}
get width(): number {
return this._getValue(widthProperty);
}
set width(value: number) {
this._setValue(widthProperty, value);
}
get height(): number {
return this._getValue(heightProperty);
}
set height(value: number) {
this._setValue(heightProperty, value);
}
get margin(): string {
return this.marginTop + " " + this.marginRight + " " + this.marginBottom + " " + this.marginLeft;
}
set margin(value: string) {
this._setShorthandProperty("margin", value);
}
get marginLeft(): number {
return this._getValue(marginLeftProperty);
}
set marginLeft(value: number) {
this._setValue(marginLeftProperty, value);
}
get marginTop(): number {
return this._getValue(marginTopProperty);
}
set marginTop(value: number) {
this._setValue(marginTopProperty, value);
}
get marginRight(): number {
return this._getValue(marginRightProperty);
}
set marginRight(value: number) {
this._setValue(marginRightProperty, value);
}
get marginBottom(): number {
return this._getValue(marginBottomProperty);
}
set marginBottom(value: number) {
this._setValue(marginBottomProperty, value);
}
get padding(): string {
return this.paddingTop + " " + this.paddingRight + " " + this.paddingBottom + " " + this.paddingLeft;
}
set padding(value: string) {
this._setShorthandProperty("padding", value);
}
get paddingLeft(): number {
return this._getValue(paddingLeftProperty);
}
set paddingLeft(value: number) {
this._setValue(paddingLeftProperty, value);
}
get paddingTop(): number {
return this._getValue(paddingTopProperty);
}
set paddingTop(value: number) {
this._setValue(paddingTopProperty, value);
}
get paddingRight(): number {
return this._getValue(paddingRightProperty);
}
set paddingRight(value: number) {
this._setValue(paddingRightProperty, value);
}
get paddingBottom(): number {
return this._getValue(paddingBottomProperty);
}
set paddingBottom(value: number) {
this._setValue(paddingBottomProperty, value);
}
get horizontalAlignment(): string {
return this._getValue(horizontalAlignmentProperty);
}
set horizontalAlignment(value: string) {
this._setValue(horizontalAlignmentProperty, value);
}
get verticalAlignment(): string {
return this._getValue(verticalAlignmentProperty);
}
set verticalAlignment(value: string) {
this._setValue(verticalAlignmentProperty, value);
}
get visibility(): string {
return this._getValue(visibilityProperty);
}
set visibility(value: string) {
this._setValue(visibilityProperty, value);
}
get opacity(): number {
return this._getValue(opacityProperty);
}
set opacity(value: number) {
this._setValue(opacityProperty, value);
}
get textDecoration(): string {
return this._getValue(textDecorationProperty);
}
set textDecoration(value: string) {
this._setValue(textDecorationProperty, value);
}
get textTransform(): string {
return this._getValue(textTransformProperty);
}
set textTransform(value: string) {
this._setValue(textTransformProperty, value);
}
get whiteSpace(): string {
return this._getValue(whiteSpaceProperty);
}
set whiteSpace(value: string) {
this._setValue(whiteSpaceProperty, value);
}
public _updateTextDecoration() {
if (this._getValue(textDecorationProperty) !== enums.TextDecoration.none) {
this._applyProperty(textDecorationProperty, this._getValue(textDecorationProperty));
}
}
public _updateTextTransform() {
if (this._getValue(textTransformProperty) !== enums.TextTransform.none) {
this._applyProperty(textTransformProperty, this._getValue(textTransformProperty));
}
}
constructor(parentView: View) {
super();
this._view = parentView;
}
public _beginUpdate() {
this._updateCounter++;
}
public _endUpdate() {
this._updateCounter--;
if (this._updateCounter < 0) {
throw new Error("style._endUpdate() called, but no update is in progress.");
}
if (this._updateCounter === 0) {
this._nativeSetters.forEach((newValue, property, map) => { this._applyStyleProperty(property, newValue); });
this._nativeSetters.clear();
}
}
public _resetCssValues() {
var that = this;
this._eachSetProperty(function (property: Property) {
that._resetValue(property, ValueSource.Css);
return true;
});
}
public _resetLocalValues() {
var that = this;
this._eachSetProperty(function (property: Property) {
that._resetValue(property);
return true;
});
}
public _onPropertyChanged(property: Property, oldValue: any, newValue: any) {
trace.write(
"Style._onPropertyChanged view:" + this._view +
", property: " + property.name +
", oldValue: " + oldValue +
", newValue: " + newValue, trace.categories.Style);
super._onPropertyChanged(property, oldValue, newValue);
this._view._checkMetadataOnPropertyChanged(property.metadata);
this._applyProperty(property, newValue);
}
public _syncNativeProperties() {
var that = this;
// loop all style properties and call the _applyProperty method
// TODO: Potential performance bottle-neck
styleProperty.eachProperty(function (p: styleProperty.Property) {
var value = that._getValue(p);
var valueSource = that._getValueSource(p);
if (valueSource !== ValueSource.Default && types.isDefined(value)) {
that._applyProperty(p, value);
}
});
}
public _sizeChanged() {
if (!(<background.Background>this._getValue(backgroundInternalProperty)).isEmpty()) {
this._applyProperty(backgroundInternalProperty, this._getValue(backgroundInternalProperty));
}
}
private _applyProperty(property: Property, newValue: any) {
this._applyStyleProperty(property, newValue);
// The effective value of an inheritable property has changed
// propagate the change down to the descendants to update their inherited properties.
if (this._view._childrenCount === 0 || !property.metadata.inheritable) {
return;
}
var eachChild = function (child: View): boolean {
child.style._inheritStyleProperty(property);
return true;
}
this._view._eachChildView(eachChild);
}
private _applyStyleProperty(property: Property, newValue: any) {
if (!this._view._shouldApplyStyleHandlers()) {
return;
}
if (this._updateCounter > 0) {
this._nativeSetters.set(property, newValue);
return;
}
try {
var handler: styling.stylers.StylePropertyChangedHandler = getHandler(property, this._view);
if (!handler) {
trace.write("No handler for property: " + property.name + " with id: " + property.id + ", view:" + this._view, trace.categories.Style);
}
else {
trace.write("Found handler for property: " + property.name + ", view:" + this._view, trace.categories.Style);
var shouldReset = false;
if (property.metadata.equalityComparer) {
shouldReset = property.metadata.equalityComparer(newValue, property.metadata.defaultValue);
}
else {
shouldReset = (newValue === property.metadata.defaultValue);
}
if (shouldReset) {
(<any>handler).resetProperty(property, this._view);
} else {
(<any>handler).applyProperty(property, this._view, newValue);
}
}
}
catch (ex) {
trace.write("Error setting property: " + property.name + " on " + this._view + ": " + ex, trace.categories.Style, trace.messageType.error);
}
}
public _inheritStyleProperty(property: Property) {
if (!property.metadata.inheritable) {
throw new Error("An attempt was made to inherit a style property which is not marked as 'inheritable'.");
}
var currentParent = this._view.parent;
var valueSource: number;
while (currentParent) {
valueSource = currentParent.style._getValueSource(property);
if (valueSource > ValueSource.Default) {
this._setValue(property, currentParent.style._getValue(property), ValueSource.Inherited);
break;
}
currentParent = currentParent.parent;
}
}
public _inheritStyleProperties() {
var that = this;
styleProperty.eachInheritableProperty((p) => {
that._inheritStyleProperty(p);
});
}
get _nativeView(): any {
return this._view._nativeView;
}
private _setShorthandProperty(name: string, value: any): void {
var pairs = styleProperty.getShorthandPairs(name, value);
if (pairs) {
this._beginUpdate();
for (let j = 0; j < pairs.length; j++) {
let pair = pairs[j];
this._setValue(pair.property, pair.value, ValueSource.Local);
}
this._endUpdate();
}
}
}
export function registerHandler(property: Property, handler: styling.stylers.StylePropertyChangedHandler, className?: string) {
var realClassName = className ? className : "default";
var handlerRecord = _registeredHandlers[property.id];
if (!handlerRecord) {
handlerRecord = {};
_registeredHandlers[property.id] = handlerRecord;
}
handlerRecord[realClassName] = handler;
}
export function registerNoStylingClass(className) {
noStylingClasses[className] = 1;
}
export function getHandler(property: Property, view: View): styling.stylers.StylePropertyChangedHandler {
return getHandlerInternal(property.id, types.getClassInfo(view));
}
// Property registration
export var colorProperty = new styleProperty.Property("color", "color",
new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, undefined, Color.isValid, Color.equals),
converters.colorConverter);
export var backgroundImageProperty = new styleProperty.Property("backgroundImage", "background-image",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, onBackgroundImagePropertyChanged));
export var backgroundColorProperty = new styleProperty.Property("backgroundColor", "background-color",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, onBackgroundColorPropertyChanged, Color.isValid, Color.equals), converters.colorConverter);
export var backgroundRepeatProperty = new styleProperty.Property("backgroundRepeat", "background-repeat",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, onBackgroundRepeatPropertyChanged));
export var backgroundSizeProperty = new styleProperty.Property("backgroundSize", "background-size",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, onBackgroundSizePropertyChanged));
export var backgroundPositionProperty = new styleProperty.Property("backgroundPosition", "background-position",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, onBackgroundPositionPropertyChanged));
export var borderColorProperty = new styleProperty.Property("borderColor", "border-color",
new PropertyMetadata(undefined, PropertyMetadataSettings.None, undefined, Color.isValid, Color.equals), converters.colorConverter);
export var borderWidthProperty = new styleProperty.Property("borderWidth", "border-width",
new PropertyMetadata(0, AffectsLayout, null, isPaddingValid), converters.numberConverter);
export var borderRadiusProperty = new styleProperty.Property("borderRadius", "border-radius",
new PropertyMetadata(0, AffectsLayout, null, isPaddingValid), converters.numberConverter);
export var backgroundInternalProperty = new styleProperty.Property("_backgroundInternal", "_backgroundInternal",
new PropertyMetadata(background.Background.default, PropertyMetadataSettings.None, undefined, undefined, background.Background.equals));
export var fontSizeProperty = new styleProperty.Property("fontSize", "font-size",
new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, onFontSizeChanged), converters.fontSizeConverter);
export var fontFamilyProperty = new styleProperty.Property("fontFamily", "font-family",
new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, onFontFamilyChanged));
export var fontStyleProperty = new styleProperty.Property("fontStyle", "font-style",
new PropertyMetadata(enums.FontStyle.normal, PropertyMetadataSettings.Inheritable, onFontStyleChanged, isFontStyleValid));
export var fontWeightProperty = new styleProperty.Property("fontWeight", "font-weight",
new PropertyMetadata(enums.FontWeight.normal, PropertyMetadataSettings.Inheritable, onFontWeightChanged, isFontWeightValid));
export var fontInternalProperty = new styleProperty.Property("_fontInternal", "_fontInternal",
new PropertyMetadata(font.Font.default, AffectsLayout, null, null, font.Font.equals), font.Font.parse);
export var textAlignmentProperty = new styleProperty.Property("textAlignment", "text-align",
new PropertyMetadata(undefined, AffectsLayout | PropertyMetadataSettings.Inheritable), converters.textAlignConverter);
export var minWidthProperty = new styleProperty.Property("minWidth", "min-width",
new PropertyMetadata(0, AffectsLayout, null, isMinWidthHeightValid), converters.numberConverter);
export var minHeightProperty = new styleProperty.Property("minHeight", "min-height",
new PropertyMetadata(0, AffectsLayout, null, isMinWidthHeightValid), converters.numberConverter);
export var visibilityProperty = new styleProperty.Property("visibility", "visibility",
new PropertyMetadata(enums.Visibility.visible, AffectsLayout, onVisibilityChanged, isVisibilityValid), converters.visibilityConverter);
export var opacityProperty = new styleProperty.Property("opacity", "opacity",
new PropertyMetadata(1.0, PropertyMetadataSettings.None, undefined, isOpacityValid), converters.opacityConverter);
export var textDecorationProperty = new styleProperty.Property("textDecoration", "text-decoration",
new PropertyMetadata(enums.TextDecoration.none, PropertyMetadataSettings.None, undefined, isTextDecorationValid), converters.textDecorationConverter);
export var textTransformProperty = new styleProperty.Property("textTransform", "text-transform",
new PropertyMetadata(enums.TextTransform.none, PropertyMetadataSettings.None, undefined, isTextTransformValid), converters.textTransformConverter);
export var whiteSpaceProperty = new styleProperty.Property("whiteSpace", "white-space",
new PropertyMetadata(undefined, AffectsLayout, undefined, isWhiteSpaceValid), converters.whiteSpaceConverter);
// Helper property holding most layout related properties available in CSS.
// When layout related properties are set in CSS we chache them and send them to the native view in a single call.
export var nativeLayoutParamsProperty = new styleProperty.Property("nativeLayoutParams", "nativeLayoutParams",
new PropertyMetadata({
width: -1,
height: -1,
leftMargin: 0,
topMargin: 0,
rightMargin: 0,
bottomMargin: 0,
horizontalAlignment: enums.HorizontalAlignment.stretch,
verticalAlignment: enums.VerticalAlignment.stretch
}, null, null, null, layoutParamsComparer));
export var widthProperty = new styleProperty.Property("width", "width",
new PropertyMetadata(Number.NaN, AffectsLayout, onLayoutParamsChanged, isWidthHeightValid), converters.numberConverter);
export var heightProperty = new styleProperty.Property("height", "height",
new PropertyMetadata(Number.NaN, AffectsLayout, onLayoutParamsChanged, isWidthHeightValid), converters.numberConverter);
export var verticalAlignmentProperty = new styleProperty.Property("verticalAlignment", "vertical-align",
new PropertyMetadata(enums.VerticalAlignment.stretch, AffectsLayout, onLayoutParamsChanged));
export var horizontalAlignmentProperty = new styleProperty.Property("horizontalAlignment", "horizontal-align",
new PropertyMetadata(enums.HorizontalAlignment.stretch, AffectsLayout, onLayoutParamsChanged));
export var marginLeftProperty = new styleProperty.Property("marginLeft", "margin-left",
new PropertyMetadata(0, AffectsLayout, onLayoutParamsChanged, isMarginValid), converters.numberConverter);
export var marginRightProperty = new styleProperty.Property("marginRight", "margin-right",
new PropertyMetadata(0, AffectsLayout, onLayoutParamsChanged, isMarginValid), converters.numberConverter);
export var marginTopProperty = new styleProperty.Property("marginTop", "margin-top",
new PropertyMetadata(0, AffectsLayout, onLayoutParamsChanged, isMarginValid), converters.numberConverter);
export var marginBottomProperty = new styleProperty.Property("marginBottom", "margin-bottom",
new PropertyMetadata(0, AffectsLayout, onLayoutParamsChanged, isMarginValid), converters.numberConverter);
function getNativePadding(nativeView: android.view.View, callback: (view: android.view.View) => number): NativeValueResult {
return {
result: nativeView ? callback(nativeView) / utils.layout.getDisplayDensity() : 0,
cacheable: !!nativeView
};
}
function getNativePaddingLeft(instance: DependencyObservable): NativeValueResult {
var nativeView: android.view.View = (<any>instance)._nativeView;
return getNativePadding(nativeView, (view) => { return view.getPaddingLeft(); });
}
function getNativePaddingTop(instance: DependencyObservable): NativeValueResult {
var nativeView: android.view.View = (<any>instance)._nativeView;
return getNativePadding(nativeView, (view) => { return view.getPaddingTop(); });
}
function getNativePaddingRight(instance: DependencyObservable): NativeValueResult {
var nativeView: android.view.View = (<any>instance)._nativeView;
return getNativePadding(nativeView, (view) => { return view.getPaddingRight(); });
}
function getNativePaddingBottom(instance: DependencyObservable): NativeValueResult {
var nativeView: android.view.View = (<any>instance)._nativeView;
return getNativePadding(nativeView, (view) => { return view.getPaddingBottom(); });
}
// Helper property holding all paddings. When paddings are set through CSS we cache them and send them to the native view in a single call.
export var nativePaddingsProperty = new styleProperty.Property("paddingNative", "paddingNative",
new PropertyMetadata(undefined, null, null, null, thicknessComparer));
// TODO: separate into .android/.ios files so that there is no need for such checks
var defaultPadding = platform.device.os === platform.platformNames.android ? undefined : 0;
export var paddingLeftProperty = new styleProperty.Property("paddingLeft", "padding-left",
new PropertyMetadata(defaultPadding, AffectsLayout, onPaddingValueChanged, isPaddingValid), converters.numberConverter);
export var paddingRightProperty = new styleProperty.Property("paddingRight", "padding-right",
new PropertyMetadata(defaultPadding, AffectsLayout, onPaddingValueChanged, isPaddingValid), converters.numberConverter);
export var paddingTopProperty = new styleProperty.Property("paddingTop", "padding-top",
new PropertyMetadata(defaultPadding, AffectsLayout, onPaddingValueChanged, isPaddingValid), converters.numberConverter);
export var paddingBottomProperty = new styleProperty.Property("paddingBottom", "padding-bottom",
new PropertyMetadata(defaultPadding, AffectsLayout, onPaddingValueChanged, isPaddingValid), converters.numberConverter);
// TODO: separate into .android/.ios files so that there is no need for such checks
if (platform.device.os === platform.platformNames.android) {
paddingTopProperty.defaultValueGetter = getNativePaddingTop;
paddingLeftProperty.defaultValueGetter = getNativePaddingLeft;
paddingRightProperty.defaultValueGetter = getNativePaddingRight;
paddingBottomProperty.defaultValueGetter = getNativePaddingBottom;
}
function onPaddingChanged(value: any): Array<styleProperty.KeyValuePair<styleProperty.Property, any>> {
var thickness = parseThickness(value);
var array = new Array<styleProperty.KeyValuePair<styleProperty.Property, any>>();
array.push({ property: paddingTopProperty, value: thickness.top });
array.push({ property: paddingRightProperty, value: thickness.right });
array.push({ property: paddingBottomProperty, value: thickness.bottom });
array.push({ property: paddingLeftProperty, value: thickness.left });
return array;
}
function onMarginChanged(value: any): Array<styleProperty.KeyValuePair<styleProperty.Property, any>> {
var thickness = parseThickness(value);
var array = new Array<styleProperty.KeyValuePair<styleProperty.Property, any>>();
array.push({ property: marginTopProperty, value: thickness.top });
array.push({ property: marginRightProperty, value: thickness.right });
array.push({ property: marginBottomProperty, value: thickness.bottom });
array.push({ property: marginLeftProperty, value: thickness.left });
return array;
}
function onFontChanged(value: any): Array<styleProperty.KeyValuePair<styleProperty.Property, any>> {
var newFont = font.Font.parse(value);
var array = new Array<styleProperty.KeyValuePair<styleProperty.Property, any>>();
array.push({ property: fontFamilyProperty, value: newFont.fontFamily });
array.push({ property: fontStyleProperty, value: newFont.fontStyle });
array.push({ property: fontWeightProperty, value: newFont.fontWeight });
array.push({ property: fontSizeProperty, value: newFont.fontSize });
return array;
}
// register default shorthand callbacks.
styleProperty.registerShorthandCallback("font", onFontChanged);
styleProperty.registerShorthandCallback("margin", onMarginChanged);
styleProperty.registerShorthandCallback("padding", onPaddingChanged);
// register default stylers once all properties are defined.
stylers._registerDefaultStylers();