import styling = require("ui/styling"); import observable = require("ui/core/dependency-observable"); import color = require("color"); import types = require("utils/types"); import trace = require("trace"); import dependencyObservable = require("ui/core/dependency-observable"); import view = require("ui/core/view"); import stylers = require("ui/styling/stylers"); import styleProperty = require("ui/styling/style-property"); import converters = require("ui/styling/converters"); import enums = require("ui/enums"); import imageSource = require("image-source"); import utils = require("utils/utils"); // key is the property id and value is Dictionary; var _registeredHandlers = Array(); // 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 = {}; export class Style extends observable.DependencyObservable implements styling.Style { private _view: view.View; get color(): color.Color { return this._getValue(colorProperty); } set color(value: color.Color) { this._setValue(colorProperty, value, observable.ValueSource.Local); } get backgroundColor(): color.Color { return this._getValue(backgroundColorProperty); } set backgroundColor(value: color.Color) { this._setValue(backgroundColorProperty, value, observable.ValueSource.Local); } get backgroundImage(): string { return this._getValue(backgroundImageProperty); } set backgroundImage(value: string) { this._setValue(backgroundImageProperty, value, observable.ValueSource.Local); } get fontSize(): number { return this._getValue(fontSizeProperty); } set fontSize(value: number) { this._setValue(fontSizeProperty, value, observable.ValueSource.Local); } get textAlignment(): string { return this._getValue(textAlignmentProperty); } set textAlignment(value: string) { this._setValue(textAlignmentProperty, value, observable.ValueSource.Local); } get minWidth(): number { return this._getValue(minWidthProperty); } set minWidth(value: number) { this._setValue(minWidthProperty, value, observable.ValueSource.Local); } get minHeight(): number { return this._getValue(minHeightProperty); } set minHeight(value: number) { this._setValue(minHeightProperty, value, observable.ValueSource.Local); } get width(): number { return this._getValue(widthProperty); } set width(value: number) { this._setValue(widthProperty, value, observable.ValueSource.Local); } get height(): number { return this._getValue(heightProperty); } set height(value: number) { this._setValue(heightProperty, value, observable.ValueSource.Local); } get margin(): string { return this._getValue(marginProperty); } set margin(value: string) { this._setValue(marginProperty, value, observable.ValueSource.Local); } get marginLeft(): number { return this._getValue(marginLeftProperty); } set marginLeft(value: number) { this._setValue(marginLeftProperty, value, observable.ValueSource.Local); } get marginTop(): number { return this._getValue(marginTopProperty); } set marginTop(value: number) { this._setValue(marginTopProperty, value, observable.ValueSource.Local); } get marginRight(): number { return this._getValue(marginRightProperty); } set marginRight(value: number) { this._setValue(marginRightProperty, value, observable.ValueSource.Local); } get marginBottom(): number { return this._getValue(marginBottomProperty); } set marginBottom(value: number) { this._setValue(marginBottomProperty, value, observable.ValueSource.Local); } get padding(): string { return this._getValue(paddingProperty); } set padding(value: string) { this._setValue(paddingProperty, value, observable.ValueSource.Local); } get paddingLeft(): number { return this._getValue(paddingLeftProperty); } set paddingLeft(value: number) { this._setValue(paddingLeftProperty, value, observable.ValueSource.Local); } get paddingTop(): number { return this._getValue(paddingTopProperty); } set paddingTop(value: number) { this._setValue(paddingTopProperty, value, observable.ValueSource.Local); } get paddingRight(): number { return this._getValue(paddingRightProperty); } set paddingRight(value: number) { this._setValue(paddingRightProperty, value, observable.ValueSource.Local); } get paddingBottom(): number { return this._getValue(paddingBottomProperty); } set paddingBottom(value: number) { this._setValue(paddingBottomProperty, value, observable.ValueSource.Local); } get horizontalAlignment(): string { return this._getValue(horizontalAlignmentProperty); } set horizontalAlignment(value: string) { this._setValue(horizontalAlignmentProperty, value, observable.ValueSource.Local); } get verticalAlignment(): string { return this._getValue(verticalAlignmentProperty); } set verticalAlignment(value: string) { this._setValue(verticalAlignmentProperty, value, observable.ValueSource.Local); } get visibility(): string { return this._getValue(visibilityProperty); } set visibility(value: string) { this._setValue(visibilityProperty, value, observable.ValueSource.Local); } get opacity(): number { return this._getValue(opacityProperty); } set opacity(value: number) { this._setValue(opacityProperty, value, observable.ValueSource.Local); } constructor(parentView: view.View) { super(); this._view = parentView; } public _resetCssValues() { var that = this; this._eachSetProperty(function (property: observable.Property) { that._resetValue(property, observable.ValueSource.Css); return true; }); } public _onPropertyChanged(property: dependencyObservable.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); if (types.isDefined(value)) { that._applyProperty(p, value); } }); } private _applyProperty(property: dependencyObservable.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.View): boolean { child.style._inheritStyleProperty(property); return true; } this._view._eachChildView(eachChild); } private _applyStyleProperty(property: dependencyObservable.Property, newValue: any) { 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); if (types.isUndefined(newValue)) { (handler).resetProperty(property, this._view); } else { (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: dependencyObservable.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 > dependencyObservable.ValueSource.Default) { this._setValue(property, currentParent.style._getValue(property), dependencyObservable.ValueSource.Inherited); break; } currentParent = currentParent.parent; } } public _inheritStyleProperties() { var that = this; styleProperty.eachInheritableProperty((p) => { that._inheritStyleProperty(p); }); } } export function registerHandler(property: dependencyObservable.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: dependencyObservable.Property, view: view.View): styling.stylers.StylePropertyChangedHandler { return getHandlerInternal(property.id, types.getClassInfo(view)); } 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 proeprtyID 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; } // Property registration export var colorProperty = new styleProperty.Property("color", "color", new observable.PropertyMetadata(undefined, observable.PropertyMetadataSettings.Inheritable, undefined, undefined, color.Color.equals), converters.colorConverter); export var backgroundImageProperty = new styleProperty.Property("backgroundImage", "background-image", new observable.PropertyMetadata(undefined, observable.PropertyMetadataSettings.None, onBackgroundImagePropertyChanged)); function onBackgroundImagePropertyChanged(data: observable.PropertyChangeData) { var style =