Hhristov/modules30 fixes (#3316)

* Fixed properties, text-base, action-bar, bindings, view

* Rebase onto master
This commit is contained in:
Hristo Hristov
2016-12-19 10:21:43 +02:00
committed by GitHub
parent 0ac5ecd74f
commit 745388c3da
10 changed files with 193 additions and 134 deletions

View File

@@ -73,11 +73,29 @@ export class NavigationButton extends ActionItemBase {
}
@Interfaces([android.support.v7.widget.Toolbar.OnMenuItemClickListener])
class MenuItemClickListener extends java.lang.Object implements android.support.v7.widget.Toolbar.OnMenuItemClickListener {
constructor(public owner: WeakRef<ActionBar>) {
super();
return global.__native(this);
}
onMenuItemClick(item: android.view.IMenuItem): boolean {
let owner = this.owner.get();
if (!owner) {
return false;
}
let itemId = item.getItemId();
return owner._onAndroidItemSelected(itemId);
}
}
export class ActionBar extends ActionBarBase {
private _appResources: android.content.res.Resources;
private _android: AndroidActionBarSettings;
nativeView: android.support.v7.widget.Toolbar;
private _toolbar: android.support.v7.widget.Toolbar;
private _menuItemClickListener: android.support.v7.widget.Toolbar.OnMenuItemClickListener;
constructor() {
super();
@@ -93,19 +111,14 @@ export class ActionBar extends ActionBarBase {
throw new Error("ActionBar.android is read-only");
}
get _nativeView(): android.support.v7.widget.Toolbar {
return this._toolbar;
}
public _createUI() {
this.nativeView = new android.support.v7.widget.Toolbar(this._context);
let ownerRef = new WeakRef(this);
this.nativeView.setOnMenuItemClickListener(new android.support.v7.widget.Toolbar.OnMenuItemClickListener({
onMenuItemClick: function (item: android.view.IMenuItem): boolean {
let ownerValue = ownerRef.get();
if (!ownerValue) {
return false;
}
let itemId = item.getItemId();
return ownerValue._onAndroidItemSelected(itemId);
}
}));
this._toolbar = new android.support.v7.widget.Toolbar(this._context);
this._menuItemClickListener = this._menuItemClickListener || new MenuItemClickListener(new WeakRef(this));
this._toolbar.setOnMenuItemClickListener(this._menuItemClickListener);
}
public onLoaded() {

View File

@@ -4,7 +4,7 @@ import { unsetValue, DependencyObservable, Property, PropertyMetadata, PropertyM
import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-event-listener";
import types = require("utils/types");
import bindingBuilder = require("../builder/binding-builder");
import { ViewBase, isEventOrGesture } from "ui/core/view-base";
import { ViewBase, isEventOrGesture, bindingContextProperty } from "ui/core/view-base";
import * as application from "application";
import * as polymerExpressions from "js-libs/polymer-expressions";
import * as specialProperties from "ui/builder/special-properties";
@@ -12,16 +12,16 @@ import * as utils from "utils/utils";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent, messageType as traceMessageType } from "trace";
let bindingContextProperty = new Property(
"bindingContext",
"Bindable",
new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, onBindingContextChanged)
);
// let bindingContextProperty = new Property(
// "bindingContext",
// "Bindable",
// new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, onBindingContextChanged)
// );
function onBindingContextChanged(data: DependencyPropertyChangeData) {
let bindable = <Bindable>data.object;
bindable._onBindingContextChanged(data.oldValue, data.newValue);
}
// function onBindingContextChanged(data: DependencyPropertyChangeData) {
// let bindable = <Bindable>data.object;
// bindable._onBindingContextChanged(data.oldValue, data.newValue);
// }
let contextKey = "context";
// this regex is used to get parameters inside [] for example:
@@ -39,31 +39,33 @@ export class Bindable extends DependencyObservable implements definition.Bindabl
private bindings = new Map<string, Binding>();
get bindingContext(): Object {
return this._getValue(Bindable.bindingContextProperty);
throw new Error("Not implemented");
}
set bindingContext(value: Object) {
this._setValue(Bindable.bindingContextProperty, value);
throw new Error("Not implemented");
}
public bind(options: definition.BindingOptions, source: Object = defaultBindingSource) {
let binding: Binding = this.bindings.get(options.targetProperty);
if (binding) {
binding.unbind();
}
binding = new Binding(this, options);
this.bindings.set(options.targetProperty, binding);
let bindingSource = source;
if (bindingSource === defaultBindingSource) {
bindingSource = this.bindingContext;
binding.sourceIsBindingContext = true;
}
// if (!types.isNullOrUndefined(bindingSource)) {
binding.bind(bindingSource);
throw new Error("Not implemented");
// let binding: Binding = this.bindings.get(options.targetProperty);
// if (binding) {
// binding.unbind();
// }
// binding = new Binding(this, options);
// this.bindings.set(options.targetProperty, binding);
// let bindingSource = source;
// if (bindingSource === defaultBindingSource) {
// bindingSource = this.bindingContext;
// binding.sourceIsBindingContext = true;
// }
// // if (!types.isNullOrUndefined(bindingSource)) {
// binding.bind(bindingSource);
// // }
}
public unbind(property: string) {
@@ -164,25 +166,29 @@ function getProperties(property: string): Array<string> {
export class Binding {
private source: WeakRef<Object>;
public target: WeakRef<Bindable>;
// TODO: target should be hard reference!
public target: WeakRef<ViewBase>;
private sourceOptions: { instance: WeakRef<any>; property: string };
private targetOptions: { instance: WeakRef<any>; property: string };
private targetOptions: { instance: WeakRef<Object>; property: string };
private sourcesAndProperties: Array<{ instance: Object; property: string }>;
private propertyChangeListeners: Map<string, Observable> = new Map<string, Observable>();
private sourceProperties: Array<string>;
private propertyChangeListeners: Map<string, Observable> = new Map<string, Observable>();
public updating: boolean;
public options: definition.BindingOptions;
public sourceIsBindingContext: boolean;
public options: definition.BindingOptions;
constructor(target: Bindable, options: definition.BindingOptions) {
constructor(target: ViewBase, options: definition.BindingOptions) {
this.target = new WeakRef(target);
this.options = options;
this.sourceProperties = getProperties(options.sourceProperty);
this.targetOptions = this.resolveOptions(target, getProperties(options.targetProperty));
if (!this.targetOptions) {
throw new Error(`Invalid property: ${options.targetProperty} for target: ${target}`);
}
}
public loadedHandlerVisualTreeBinding(args) {
@@ -231,23 +237,41 @@ export class Binding {
return source;
}
private bindingContextChanged(data: PropertyChangeData): void {
let target = this.target.get();
if (!target) {
this.unbind();
return;
}
let source = target.bindingContext;
// We don't have source so this is first bindingContextChange.
// Bind to the new source.
if (!this.source) {
this.bind(source);
} else if (source == null || source === undefined) {
this.clearBinding();
}
}
public bind(source: Object): void {
this.clearSource();
source = this.sourceAsObject(source);
let sourceValue;
if (!types.isNullOrUndefined(source)) {
this.source = new WeakRef(source);
this.sourceOptions = this.resolveOptions(source, this.sourceProperties);
sourceValue = this.getSourcePropertyValue();
let sourceValue = this.getSourcePropertyValue();
this.updateTarget(sourceValue);
this.addPropertyChangeListeners(this.source, this.sourceProperties);
} else if (!this.sourceIsBindingContext) {
sourceValue = this.getSourcePropertyValue();
let sourceValue = this.getSourcePropertyValue();
this.updateTarget(sourceValue ? sourceValue : source);
} else if (this.sourceIsBindingContext && (source === undefined || source === null)) {
this.target.get().off("bindingContextChange", this.bindingContextChanged, this);
this.target.get().on("bindingContextChange", this.bindingContextChanged, this);
}
}
@@ -317,6 +341,11 @@ export class Binding {
this.clearSource();
let target = this.target.get();
if (target) {
target.off(`${bindingContextProperty}Change`, this.bindingContextChanged, this);
}
if (this.targetOptions) {
this.targetOptions = undefined;
}

View File

@@ -110,18 +110,22 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor {
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue;
if (wrapped || changed) {
const setNativeValue = this.nativeView && native in this;
if (reset) {
delete this[key];
} else {
this[key] = unboxedValue;
}
if (this.nativeView) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = this[native];
if (setNativeValue) {
this[native] = this[defaultValueKey];
delete this[defaultValueKey];
}
} else {
this[key] = value;
if (setNativeValue) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = this[native];
}
this[native] = unboxedValue;
this[native] = value;
}
}
if (valueChanged) {
@@ -227,18 +231,22 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue;
if (wrapped || changed) {
const setNativeValue = this.nativeView && native in this;
if (reset) {
delete this[key];
} else {
this[key] = unboxedValue;
}
if (this.nativeView) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = this[native];
if (setNativeValue) {
this[native] = this[defaultValueKey];
delete this[defaultValueKey];
}
} else {
this[key] = value;
if (setNativeValue) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = this[native];
}
this[native] = unboxedValue;
this[native] = value;
}
}
if (valueChanged) {
@@ -325,7 +333,7 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> {
if (currentValue !== newValue) {
const reset = newValueSource === ValueSource.Default;
that.eachChild((child) => {
const childValueSource = child[sourceKey];
const childValueSource = child[sourceKey] || ValueSource.Default;
if (reset) {
if (childValueSource === ValueSource.Inherited) {
setFunc.call(child, unsetValue);
@@ -411,19 +419,23 @@ export class CssProperty<T extends Style, U> {
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value;
if (changed) {
const view = this.view;
const setNativeValue = view.nativeView && native in view;
if (reset) {
delete this[key];
if (setNativeValue) {
view[native] = this[defaultValueKey];
delete this[defaultValueKey];
}
} else {
this[key] = value;
}
if (setNativeValue) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
}
const view = this.view;
if (view.nativeView) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
view[native] = value;
}
view[native] = value;
}
if (valueChanged) {
@@ -468,19 +480,23 @@ export class CssProperty<T extends Style, U> {
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value;
if (changed) {
const view = this.view;
const setNativeValue = view.nativeView && native in view;
if (reset) {
delete this[key];
if (setNativeValue) {
view[native] = this[defaultValueKey];
delete this[defaultValueKey];
}
} else {
this[key] = value;
}
if (setNativeValue) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
}
const view = this.view;
if (view.nativeView) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
view[native] = value;
}
view[native] = value;
}
if (valueChanged) {
@@ -553,10 +569,6 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
const valueChanged = options.valueChanged;
const valueConverter = options.valueConverter;
const dependentProperty = options.dependentProperty;
const dependentPropertyKey = dependentProperty ? dependentProperty.key : undefined;
const dependentPropertyNativeKey = dependentProperty ? dependentProperty.native : undefined;
const setFunc = (valueSource: ValueSource) => function (this: T, value: any): void {
const reset = value === unsetValue;
const currentValueSource: number = this[sourceKey] || ValueSource.Default;
@@ -599,22 +611,22 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, newValue) : currentValue !== newValue;
if (changed) {
const view = this.view;
const setNativeValue = view.nativeView && native in view;
if (reset) {
delete this[key];
if (setNativeValue) {
view[native] = this[defaultValueKey];
delete this[defaultValueKey];
}
} else {
this[key] = newValue;
}
if (setNativeValue) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
}
const nativeView = view.nativeView;
if (nativeView) {
if (!(defaultValueKey in this)) {
this[defaultValueKey] = view[native];
}
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
view[native] = newValue;
}
}
@@ -806,7 +818,7 @@ export function applyNativeSetters(view: ViewBase): void {
}
const native = property.native;
if (view[native]) {
if (native in view) {
const defaultValueKey = property.defaultValueKey;
if (!(defaultValueKey in view)) {
view[defaultValueKey] = view[native];
@@ -826,7 +838,7 @@ export function applyNativeSetters(view: ViewBase): void {
}
const native = property.native;
if (view[native]) {
if (native in view) {
const defaultValueKey = property.defaultValueKey;
if (!(defaultValueKey in style)) {
style[defaultValueKey] = view[native];
@@ -865,7 +877,7 @@ export function resetStyleProperties(style: Style): void {
}
const native = property.native;
if (view[native]) {
if (native in view) {
view[native] = style[property.defaultValueKey];
delete style[property.defaultValueKey];
}
@@ -878,6 +890,10 @@ export function resetStyleProperties(style: Style): void {
export function propagateInheritedProperties(view: ViewBase): void {
const inheritablePropertyValues = inheritablePropertyValuesOn(view);
const inheritableCssPropertyValues = inheritableCssPropertyValuesOn(view.style);
if (inheritablePropertyValues.length === 0 && inheritableCssPropertyValues.length === 0) {
return;
}
view.eachChild((child) => {
for (let pair of inheritablePropertyValues) {
const prop = pair.property;
@@ -900,4 +916,15 @@ export function propagateInheritedProperties(view: ViewBase): void {
return true;
}
});
}
export function makeValidator<T>(...values: T[]): (value: any) => value is T {
const set = new Set(values);
return (value: any): value is T => set.has(value);
}
export function makeParser<T>(isValid: (value: any) => boolean, def: T): (value: any) => T {
return value => {
const lower = value && value.toLowerCase();
return isValid(lower) ? lower : def;
}
}

View File

@@ -537,15 +537,4 @@ function resetStyles(view: ViewBase): void {
}
export const idProperty = new Property<ViewBase, string>({ name: "id", valueChanged: (view, oldValue, newValue) => resetStyles(view) });
idProperty.register(ViewBase);
export function makeValidator<T>(...values: T[]): (value: any) => value is T {
const set = new Set(values);
return (value: any): value is T => set.has(value);
}
export function makeParser<T>(isValid: (value: any) => boolean, def: T): (value: any) => T {
return value => {
const lower = value && value.toLowerCase();
return isValid(lower) ? lower : def;
}
}
idProperty.register(ViewBase);

View File

@@ -1793,6 +1793,7 @@ export const colorProperty = new InheritedCssProperty<Style, Color>({ name: "col
colorProperty.register(Style);
export const fontInternalProperty = new CssProperty<Style, Font>({ name: "fontInternal", cssName: "_fontInternal", defaultValue: Font.default });
fontInternalProperty.register(Style);
export const fontFamilyProperty = new InheritedCssProperty<Style, string>({
name: "fontFamily", cssName: "font-family", valueChanged: (target, newValue) => {

View File

@@ -11,8 +11,6 @@ import {
traceEnabled, traceWrite, traceCategories, traceNotifyEvent
} from "./view-common";
import { } from "utils/utils";
export * from "./view-common";
let flexbox;
@@ -166,7 +164,7 @@ export class View extends ViewCommon {
return true;
}
this._eachChildView(eachChild);
} else if (this._nativeView) {
} else if (this._nativeView && !this.parent) {
// copy all the locally cached values to the native android widget
applyNativeSetters(this);
}

View File

@@ -129,9 +129,6 @@ declare module "ui/core/view-base" {
export const idProperty: Property<ViewBase, string>;
export const classNameProperty: Property<ViewBase, string>;
export const bindingContextProperty: InheritedProperty<ViewBase, any>;
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
export function makeParser<T>(isValid: (value: any) => boolean, def: T): (value: any) => T;
}
declare module "ui/core/properties" {
@@ -211,4 +208,7 @@ declare module "ui/core/properties" {
export function applyNativeSetters(view: ViewBase): void;
export function resetStyleProperties(style: Style): void;
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
export function makeParser<T>(isValid: (value: any) => boolean, def: T): (value: any) => T;
}

View File

@@ -1,5 +1,5 @@
import { GridLayout as GridLayoutDefinition, ItemSpec as ItemSpecDefinition } from "ui/layouts/grid-layout";
import { LayoutBase, View, Bindable, Property } from "ui/layouts/layout-base";
import { LayoutBase, View, Observable, Property } from "ui/layouts/layout-base";
export * from "ui/layouts/layout-base";
@@ -68,7 +68,7 @@ function parseAndAddItemSpecs(value: string, func: (itemSpec: ItemSpec) => void)
}
}
export class ItemSpec extends Bindable implements ItemSpecDefinition {
export class ItemSpec extends Observable implements ItemSpecDefinition {
private _value: number;
private _unitType: "pixel" | "star" | "auto";

View File

@@ -1,5 +1,5 @@
import { Font as FontDefinition, ParsedFont } from "ui/styling/font";
import { makeValidator, makeParser} from "ui/core/view";
import { makeValidator, makeParser} from "ui/core/properties";
export abstract class FontBase implements FontDefinition {
public static default = undefined;

View File

@@ -1,5 +1,5 @@
import { TextBase as TextBaseDefinition } from "ui/text-base";
import { View, AddChildFromBuilder, Property, CssProperty, InheritedCssProperty, Style, isIOS, Observable, makeValidator, makeParser} from "ui/core/view";
import { TextBase as TextBaseDefinition } from "ui/text-base";
import { View, AddChildFromBuilder, Property, CssProperty, InheritedCssProperty, Style, isIOS, Observable, makeValidator, makeParser } from "ui/core/view";
import { PropertyChangeData } from "data/observable";
import { FormattedString, FormattedStringView } from "text/formatted-string";
import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-event-listener";
@@ -7,13 +7,9 @@ import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-even
export { FormattedString };
export * from "ui/core/view";
export abstract class TextBaseCommon extends View implements TextBaseDefinition, FormattedStringView {
const CHILD_SPAN = "Span";
constructor() {
super();
// NOTE: this was added so that FormattedString.addFormattedStringToView does not instantiate it.
this.formattedText = new FormattedString();
}
export abstract class TextBaseCommon extends View implements TextBaseDefinition, FormattedStringView {
public abstract _setFormattedTextPropertyToNative(value: FormattedString): void;
@@ -69,6 +65,10 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition,
}
public _addChildFromBuilder(name: string, value: any): void {
if (!this.formattedText) {
this.formattedText = new FormattedString();
}
FormattedString.addFormattedStringToView(this, name, value);
}
@@ -107,7 +107,7 @@ export namespace TextAlignment {
export const parse = makeParser(isValid, undefined);
}
export const textAlignmentProperty = new InheritedCssProperty<Style, TextAlignment>({name: "textAlignment", cssName: "text-align", valueConverter: TextAlignment.parse});
export const textAlignmentProperty = new InheritedCssProperty<Style, TextAlignment>({ name: "textAlignment", cssName: "text-align", valueConverter: TextAlignment.parse });
textAlignmentProperty.register(Style);
//TextDecoration
@@ -121,7 +121,9 @@ export namespace TextDecoration {
export const isValid = makeValidator<TextDecoration>(NONE, UNDERLINE, LINE_THROUGH, UNDERLINE_LINE_THROUGH);
export const parse = makeParser(isValid, NONE);
}
export const textDecorationProperty = new CssProperty<Style, TextDecoration>({name: "textDecoration", cssName: "text-decoration", defaultValue: TextDecoration.NONE, valueConverter: TextDecoration.parse});
export const textDecorationProperty = new CssProperty<Style, TextDecoration>({
name: "textDecoration", cssName: "text-decoration", defaultValue: TextDecoration.NONE, valueConverter: TextDecoration.parse
});
textDecorationProperty.register(Style);
//TextTransform
@@ -130,11 +132,11 @@ export namespace TextTransform {
export const NONE: "none" = "none";
export const CAPITALIZE: "capitalize" = "capitalize";
export const UPPERCASE: "uppercase" = "uppercase";
export const LOWERCASE: "lowercase" ="lowercase";
export const LOWERCASE: "lowercase" = "lowercase";
export const isValid = makeValidator<TextTransform>(NONE, CAPITALIZE, UPPERCASE, LOWERCASE);
export const parse = makeParser(isValid, NONE);
}
export const textTransformProperty = new CssProperty<Style, TextTransform>({name: "textTransform", cssName: "text-transform", defaultValue: TextTransform.NONE, valueConverter: TextTransform.parse});
export const textTransformProperty = new CssProperty<Style, TextTransform>({ name: "textTransform", cssName: "text-transform", defaultValue: TextTransform.NONE, valueConverter: TextTransform.parse });
textTransformProperty.register(Style);
//Whitespace
@@ -146,7 +148,7 @@ export namespace WhiteSpace {
export const parse = makeParser(isValid, NORMAL);
}
export const whiteSpaceProperty = new CssProperty<Style, WhiteSpace>({name: "whiteSpace", cssName: "white-space", defaultValue: WhiteSpace.NORMAL, valueConverter: WhiteSpace.parse});
export const whiteSpaceProperty = new CssProperty<Style, WhiteSpace>({ name: "whiteSpace", cssName: "white-space", defaultValue: WhiteSpace.NORMAL, valueConverter: WhiteSpace.parse });
whiteSpaceProperty.register(Style);
export const letterSpacingProperty = new CssProperty<Style, number>({ name: "letterSpacing", cssName: "letter-spacing", defaultValue: 0, affectsLayout: isIOS, valueConverter: (v: string) => parseFloat(v) });