up to segmented-bar.ios

This commit is contained in:
Hristo Hristov
2016-12-08 18:20:25 +02:00
parent 007b78325c
commit befb494a50
43 changed files with 847 additions and 1044 deletions

View File

@@ -2,7 +2,6 @@
* Contains the Border class, which represents a UI border component.
*/
declare module "ui/border" {
import { Color } from "color";
import { ContentView } from "ui/content-view";
/**
@@ -13,15 +12,5 @@ declare module "ui/border" {
* Gets or sets the corner radius of the border component.
*/
cornerRadius: number;
/**
* Gets or sets the border width of the border component.
*/
borderWidth: string | number;
/**
* Gets or sets the border color of the border component.
*/
borderColor: string | Color;
}
}

View File

@@ -1,5 +1,7 @@
import { ButtonBase, textProperty, formattedTextProperty } from "./button-common";
import { FormattedString } from "text/formatted-string";
import {
ButtonBase, textProperty, formattedTextProperty, TouchGestureEventData, FormattedString, GestureTypes, TouchAction,
PseudoClassHandler
} from "./button-common";
export * from "./button-common";
@@ -17,32 +19,11 @@ class ClickListener implements android.view.View.OnClickListener {
}
}
@Interfaces([android.view.View.OnTouchListener])
class TouchListener implements android.view.View.OnTouchListener {
constructor(public owner: WeakRef<Button>) {
return global.__native(this);
}
public onTouch(v: android.view.View, event: android.view.MotionEvent): boolean {
let btn = this.owner.get();
if (!btn) {
return false;
}
if (event.getAction() === 0) { // down
btn._goToVisualState("highlighted");
}
else if (event.getAction() === 1) { // up
btn._goToVisualState("normal");
}
return false;
}
}
export class Button extends ButtonBase {
nativeView: android.widget.Button;
private _isPressed: boolean = false;
private _isPressed: boolean;
private _transformationMethod;
private _highlightedHandler: (args: TouchGestureEventData) => void;
get android(): android.widget.Button {
return this.nativeView;
@@ -52,7 +33,6 @@ export class Button extends ButtonBase {
let weakRef = new WeakRef(this);
this.nativeView = new android.widget.Button(this._context);
this.nativeView.setOnClickListener(new ClickListener(weakRef));
this.nativeView.setOnTouchListener(new TouchListener(weakRef));
}
public _setFormattedTextPropertyToNative(value: FormattedString) {
@@ -70,7 +50,6 @@ export class Button extends ButtonBase {
this.nativeView.setText(newText);
}
}
@PseudoClassHandler("normal", "highlighted", "pressed", "active")
_updateHandler(subscribe: boolean) {
@@ -91,4 +70,3 @@ export class Button extends ButtonBase {
}
}
}

View File

@@ -2,8 +2,7 @@
* Contains the Button class, which represents a standard button widget.
*/
declare module "ui/button" {
import { TextBase, Property, EventData } from "ui/text-base";
import { FormattedString, FormattedStringView } from "text/formatted-string";
import { TextBase, Property, EventData, FormattedString, FormattedStringView } from "ui/text-base";
/**
* Represents a standard Button widget.

View File

@@ -27,7 +27,7 @@ export interface PropertyOptions<T, U> {
}
export interface CoerciblePropertyOptions<T, U> extends PropertyOptions<T, U> {
coerceValue(T, U): U
readonly coerceValue: (T, U) => U;
}
export interface ShorthandPropertyOptions {
@@ -95,6 +95,10 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor {
this[key] = unboxedValue;
}
if (this.nativeView) {
this[native] = unboxedValue;
}
if (valueChanged) {
valueChanged(this, currentValue, unboxedValue);
}
@@ -108,10 +112,6 @@ export class Property<T extends ViewBase, U> implements PropertyDescriptor {
});
}
if (this.nativeView) {
this[native] = unboxedValue;
}
if (affectsLayout) {
this.requestLayout();
}
@@ -201,6 +201,10 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
this[key] = unboxedValue;
}
if (this.nativeView) {
this[native] = unboxedValue;
}
if (valueChanged) {
valueChanged(this, currentValue, unboxedValue);
}
@@ -214,10 +218,6 @@ export class CoercibleProperty<T extends ViewBase, U> implements PropertyDescrip
});
}
if (this.nativeView) {
this[native] = unboxedValue;
}
if (affectsLayout) {
this.requestLayout();
}
@@ -378,6 +378,15 @@ export class CssProperty<T extends Style, U> {
this[key] = value;
}
let view = this.view;
if (view.nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (valueChanged) {
valueChanged(this, currentValue, value);
}
@@ -391,15 +400,6 @@ export class CssProperty<T extends Style, U> {
});
}
let view = this.view;
if (view.nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (affectsLayout) {
view.requestLayout();
}
@@ -435,6 +435,15 @@ export class CssProperty<T extends Style, U> {
this[key] = value;
}
let view = this.view;
if (view.nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (valueChanged) {
valueChanged(this, currentValue, value);
}
@@ -448,15 +457,6 @@ export class CssProperty<T extends Style, U> {
});
}
let view = this.view;
if (view.nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (affectsLayout) {
view.requestLayout();
}
@@ -568,6 +568,15 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
this[key] = newValue;
}
let nativeView = view.nativeView;
if (nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (valueChanged) {
valueChanged(this, currentValue, newValue);
}
@@ -581,15 +590,6 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
});
}
let nativeView = view.nativeView;
if (nativeView) {
view[native] = value;
if (dependentPropertyNativeKey) {
// Call the native setter for dependent property.
view[dependentPropertyNativeKey] = this[dependentPropertyKey];
}
}
if (affectsLayout) {
view.requestLayout();
}
@@ -752,7 +752,7 @@ function inheritableCssPropertiesOn(style: Object): Array<InheritedCssProperty<a
}
export function applyNativeSetters(view: ViewBase): void {
let symbols = Object.getOwnPropertySymbols(view);
let symbols = (<any>Object).getOwnPropertySymbols(view);
for (let symbol of symbols) {
let property: Property<any, any> = symbolPropertyMap[symbol];
if (!property) {
@@ -767,7 +767,7 @@ export function applyNativeSetters(view: ViewBase): void {
}
let style = view.style;
symbols = Object.getOwnPropertySymbols(style);
symbols = (<any>Object).getOwnPropertySymbols(style);
for (let symbol of symbols) {
let property: CssProperty<any, any> = cssSymbolPropertyMap[symbol];
if (!property) {

View File

@@ -1,11 +1,15 @@
import { ViewBase as ViewBaseDefinition } from "ui/core/view-base";
import { Observable, EventData } from "data/observable";
import { Property, InheritedProperty, CssProperty, Style } from "./properties";
import { Property, InheritedProperty, CssProperty, Style, clearInheritedProperties, propagateInheritedProperties } from "./properties";
import { Binding, BindingOptions, Bindable } from "ui/core/bindable";
import { isIOS } from "platform";
import { fromString as gestureFromString } from "ui/gestures";
import { CssState, StyleScope, applyInlineSyle } from "ui/styling/style-scope";
import { KeyframeAnimation } from "ui/animation/keyframe-animation";
export { Observable, EventData, Binding, BindingOptions, Bindable, isIOS, gestureFromString };
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent } from "trace";
export { KeyframeAnimation, Observable, EventData, Binding, BindingOptions, Bindable, isIOS, gestureFromString, traceEnabled, traceWrite, traceCategories, traceNotifyEvent };
export * from "./properties";
let defaultBindingSource = {};
@@ -46,12 +50,19 @@ export function isEventOrGesture(name: string, view: ViewBaseDefinition): boolea
export class ViewBase extends Observable implements ViewBaseDefinition {
private _updatingJSPropertiesDict = {};
private _style: Style;
private _isLoaded: boolean;
private _registeredAnimations: Array<KeyframeAnimation>;
private _visualState: string;
public bindingContext: any;
public nativeView: any;
public parent: ViewBase;
public isCollapsed;
public id: string;
public className: string;
public _cssState: CssState;
constructor() {
super();
this._style = new Style(this);
@@ -69,6 +80,200 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
return undefined;
}
get isLoaded(): boolean {
return this._isLoaded;
}
get page(): ViewBaseDefinition {
if (this.parent) {
return this.parent.page;
}
return null;
}
protected onLoaded() {
this._isLoaded = true;
this._loadEachChildView();
this._applyStyleFromScope();
this._emit("loaded");
}
get _childrenCount(): number {
return 0;
}
public _loadEachChildView() {
if (this._childrenCount > 0) {
this.eachChild((child) => {
child.onLoaded();
return true;
});
}
}
protected onUnloaded() {
this._setCssState(null);
this._unloadEachChildView();
this._isLoaded = false;
this._emit("unloaded");
}
private _unloadEachChildView() {
if (this._childrenCount > 0) {
this.eachChild((child) => {
if (child.isLoaded) {
child.onUnloaded();
}
return true;
});
}
}
private _applyStyleFromScope() {
let rootPage = this.page;
if (!rootPage || !rootPage.isLoaded) {
return;
}
let scope: StyleScope = (<any>rootPage)._getStyleScope();
scope.applySelectors(this);
}
// TODO: Make sure the state is set to null and this is called on unloaded to clean up change listeners...
_setCssState(next: CssState): void {
const previous = this._cssState;
this._cssState = next;
if (!this._invalidateCssHandler) {
this._invalidateCssHandler = () => {
if (this._invalidateCssHandlerSuspended) {
return;
}
this.applyCssState();
};
}
try {
this._invalidateCssHandlerSuspended = true;
if (next) {
next.changeMap.forEach((changes, view) => {
if (changes.attributes) {
changes.attributes.forEach(attribute => {
view.addEventListener(attribute + "Change", this._invalidateCssHandler)
});
}
if (changes.pseudoClasses) {
changes.pseudoClasses.forEach(pseudoClass => {
let eventName = ":" + pseudoClass;
view.addEventListener(":" + pseudoClass, this._invalidateCssHandler);
if (view[eventName]) {
view[eventName](+1);
}
});
}
});
}
if (previous) {
previous.changeMap.forEach((changes, view) => {
if (changes.attributes) {
changes.attributes.forEach(attribute => {
view.removeEventListener("onPropertyChanged:" + attribute, this._invalidateCssHandler)
});
}
if (changes.pseudoClasses) {
changes.pseudoClasses.forEach(pseudoClass => {
let eventName = ":" + pseudoClass;
view.removeEventListener(eventName, this._invalidateCssHandler)
if (view[eventName]) {
view[eventName](-1);
}
});
}
});
}
} finally {
this._invalidateCssHandlerSuspended = false;
}
this.applyCssState();
}
private notifyPseudoClassChanged(pseudoClass: string): void {
this.notify({ eventName: ":" + pseudoClass, object: this });
}
/**
* Notify that some attributes or pseudo classes that may affect the current CssState had changed.
*/
private _invalidateCssHandler;
private _invalidateCssHandlerSuspended: boolean;
private applyCssState(): void {
if (!this._cssState) {
return;
}
// this.style._beginUpdate();
this._cssState.apply();
// this.style._endUpdate();
}
private pseudoClassAliases = {
'highlighted': [
'active',
'pressed'
]
};
public cssClasses: Set<string> = new Set();
public cssPseudoClasses: Set<string> = new Set();
private getAllAliasedStates(name: string): Array<string> {
let allStates = [];
allStates.push(name);
if (name in this.pseudoClassAliases) {
for (let i = 0; i < this.pseudoClassAliases[name].length; i++) {
allStates.push(this.pseudoClassAliases[name][i]);
}
}
return allStates;
}
public addPseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (!this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.add(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
public deletePseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.delete(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
private _applyInlineStyle(inlineStyle) {
if (typeof inlineStyle === "string") {
try {
// this.style._beginUpdate();
applyInlineSyle(this, inlineStyle);
} finally {
// this.style._endUpdate();
}
}
}
private bindings = new Map<string, Binding>();
public bind(options: BindingOptions, source: Object = defaultBindingSource): void {
let binding: Binding = this.bindings.get(options.targetProperty);
@@ -126,6 +331,125 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public eachChild(callback: (child: ViewBase) => boolean) {
//
}
public _addView(view: ViewBase, atIndex?: number) {
if (traceEnabled) {
traceWrite(`${this}._addView(${view}, ${atIndex})`, traceCategories.ViewHierarchy);
}
if (!view) {
throw new Error("Expecting a valid View instance.");
}
if (!(view instanceof ViewBase)) {
throw new Error(view + " is not a valid View instance.");
}
if (view.parent) {
throw new Error("View already has a parent. View: " + view + " Parent: " + view.parent);
}
view.parent = this;
this._addViewCore(view, atIndex);
view._parentChanged(null);
}
protected _addViewCore(view: ViewBase, atIndex?: number) {
// TODO: Discuss this.
if (this._isLoaded) {
view.onLoaded();
}
propagateInheritedProperties(this);
}
/**
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not outside the UI Stack implementation.
*/
public _removeView(view: ViewBase) {
if (traceEnabled) {
traceWrite(`${this}._removeView(${view})`, traceCategories.ViewHierarchy);
}
if (view.parent !== this) {
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view.parent + " ExpectedParent: " + this);
}
this._removeViewCore(view);
view.parent = undefined;
view._parentChanged(this);
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewBase) {
// TODO: Discuss this.
if (view.isLoaded) {
view.onUnloaded();
}
// view.unsetInheritedProperties();
}
public _goToVisualState(state: string) {
if (traceEnabled) {
traceWrite(this + " going to state: " + state, traceCategories.Style);
}
if (state === this._visualState) {
return;
}
this.deletePseudoClass(this._visualState);
this._visualState = state;
this.addPseudoClass(state);
}
public _applyXmlAttribute(attribute, value): boolean {
if (attribute === "style") {
this._applyInlineStyle(value);
return true;
}
return false;
}
public setInlineStyle(style: string): void {
if (typeof style !== "string") {
throw new Error("Parameter should be valid CSS string!");
}
this._applyInlineStyle(style);
}
public _parentChanged(oldParent: ViewBase): void {
//Overridden
if (oldParent) {
// Move these method in property class.
clearInheritedProperties(this);
}
}
public _registerAnimation(animation: KeyframeAnimation) {
if (this._registeredAnimations === undefined) {
this._registeredAnimations = new Array<KeyframeAnimation>();
}
this._registeredAnimations.push(animation);
}
public _unregisterAnimation(animation: KeyframeAnimation) {
if (this._registeredAnimations) {
let index = this._registeredAnimations.indexOf(animation);
if (index >= 0) {
this._registeredAnimations.splice(index, 1);
}
}
}
public _cancelAllAnimations() {
if (this._registeredAnimations) {
for (let animation of this._registeredAnimations) {
animation.cancel();
}
}
}
}
export const visibilityProperty = new CssProperty<Style, "visible" | "hidden" | "collapse" | "collapsed">({
@@ -139,5 +463,30 @@ export const visibilityProperty = new CssProperty<Style, "visible" | "hidden" |
});
visibilityProperty.register(Style);
export let bindingContextProperty = new InheritedProperty<ViewBase, any>({ name: "bindingContext" });
export const bindingContextProperty = new InheritedProperty<ViewBase, any>({ name: "bindingContext" });
bindingContextProperty.register(ViewBase);
function onCssClassPropertyChanged(view: ViewBase, oldValue: string, newValue: string) {
let classes = view.cssClasses;
classes.clear();
if (typeof newValue === "string") {
newValue.split(" ").forEach(c => classes.add(c));
}
}
export const classNameProperty = new Property<ViewBase, string>({ name: "className", valueChanged: onCssClassPropertyChanged });
classNameProperty.register(ViewBase);
function resetStyles(view: ViewBase): void {
// view.style._resetCssValues();
// view._applyStyleFromScope();
view.eachChild((child) => {
// TODO.. Check old implementation....
resetStyles(child);
return true;
});
}
export const idProperty = new Property<ViewBase, string>({ name: "id", valueChanged: (view, oldValue, newValue) => resetStyles(view) });
idProperty.register(ViewBase);

View File

@@ -1,14 +1,12 @@
import { View as ViewDefinition, Point, Size } from "ui/core/view";
import { CssState, StyleScope, applyInlineSyle } from "ui/styling/style-scope";
import { Color } from "color";
import { Animation, AnimationPromise } from "ui/animation";
import { KeyframeAnimation } from "ui/animation/keyframe-animation";
import { Source } from "utils/debug";
import { Background } from "ui/styling/background";
import {
ViewBase, getEventOrGestureName, Observable, EventData, Style, propagateInheritedProperties, clearInheritedProperties,
ViewBase, getEventOrGestureName, Observable, EventData, Style,
Property, InheritedProperty, CssProperty, ShorthandProperty, InheritedCssProperty,
gestureFromString, isIOS
gestureFromString, isIOS, traceEnabled, traceWrite, traceCategories, traceNotifyEvent
} from "./view-base";
import { observe as gestureObserve, GesturesObserver, GestureTypes, GestureEventData } from "ui/gestures";
import { Font, parseFont } from "ui/styling/font";
@@ -16,15 +14,14 @@ import { fontSizeConverter } from "../styling/converters";
// TODO: Remove this and start using string as source (for android).
import { fromFileOrResource, fromBase64, fromUrl } from "image-source";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent } from "trace";
import { isDataURI, isFileOrResourcePath } from "utils/utils";
export * from "./view-base";
export {
Color, GestureTypes, GesturesObserver, GestureEventData, Animation, AnimationPromise, KeyframeAnimation,
Background, Font, traceEnabled, traceWrite, traceCategories, traceNotifyEvent
GestureTypes, GesturesObserver, GestureEventData,
Animation, AnimationPromise,
Background, Font, Color
}
// registerSpecialProperty("class", (instance: ViewDefinition, propertyValue: string) => {
@@ -192,22 +189,17 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
private _parent: ViewCommon;
private _visualState: string;
private _isLoaded: boolean;
private _isLayoutValid: boolean;
private _cssType: string;
private _updatingInheritedProperties: boolean;
private _registeredAnimations: Array<KeyframeAnimation>;
public _domId: number;
public _isAddedToNativeVisualTree: boolean;
public _gestureObservers = {};
public cssClasses: Set<string> = new Set();
public cssPseudoClasses: Set<string> = new Set();
public _cssState: CssState;
public parent: ViewCommon;
constructor() {
@@ -560,22 +552,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
//END Style property shortcuts
get page(): ViewDefinition {
if (this.parent) {
return this.parent.page;
}
return null;
}
public id: string;
public automationText: string;
public originX: number;
public originY: number;
public isEnabled: boolean;
public isUserInteractionEnabled: boolean;
public className: string;
get isLayoutValid(): boolean {
return this._isLayoutValid;
@@ -592,49 +574,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return true;
}
get isLoaded(): boolean {
return this._isLoaded;
}
public onLoaded() {
this._isLoaded = true;
this._loadEachChildView();
this._applyStyleFromScope();
this._emit("loaded");
}
public _loadEachChildView() {
if (this._childrenCount > 0) {
// iterate all children and call onLoaded on them first
let eachChild = function (child: ViewCommon): boolean {
child.onLoaded();
return true;
}
this._eachChildView(eachChild);
}
}
public onUnloaded() {
this._setCssState(null);
this._unloadEachChildView();
this._isLoaded = false;
this._emit("unloaded");
}
public _unloadEachChildView() {
if (this._childrenCount > 0) {
this._eachChildView((child: ViewCommon) => {
if (child.isLoaded) {
child.onUnloaded();
}
return true;
});
}
}
// public _onPropertyChanged(property: Property, oldValue: any, newValue: any) {
// super._onPropertyChanged(property, oldValue, newValue);
@@ -721,44 +660,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public abstract onLayout(left: number, top: number, right: number, bottom: number): void;
public abstract layoutNativeView(left: number, top: number, right: number, bottom: number): void;
private pseudoClassAliases = {
'highlighted': [
'active',
'pressed'
]
};
private getAllAliasedStates(name: string): Array<string> {
let allStates = [];
allStates.push(name);
if (name in this.pseudoClassAliases) {
for (let i = 0; i < this.pseudoClassAliases[name].length; i++) {
allStates.push(this.pseudoClassAliases[name][i]);
}
}
return allStates;
}
public addPseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (!this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.add(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
public deletePseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.delete(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number {
let result = size;
switch (specMode) {
@@ -975,26 +876,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return { boundsChanged, sizeChanged };
}
private _applyStyleFromScope() {
let rootPage = this.page;
if (!rootPage || !rootPage.isLoaded) {
return;
}
let scope: StyleScope = (<any>rootPage)._getStyleScope();
scope.applySelectors(this);
}
private _applyInlineStyle(inlineStyle) {
if (typeof inlineStyle === "string") {
try {
// this.style._beginUpdate();
applyInlineSyle(this, inlineStyle);
} finally {
// this.style._endUpdate();
}
}
}
// TODO: We need to implement some kind of build step that includes these members only when building for Android
//@android
public _context: android.content.Context;
@@ -1019,9 +900,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// TODO: We need to implement some kind of build step that includes these members only when building for iOS
//@endios
get _childrenCount(): number {
return 0;
}
public _eachChildView(callback: (view: ViewCommon) => boolean) {
//
@@ -1047,113 +926,39 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// IOS specific
}
/**
* Core logic for adding a child view to this instance. Used by the framework to handle lifecycle events more centralized. Do not outside the UI Stack implementation.
* // TODO: Think whether we need the base Layout routine.
*/
public _addView(view: ViewDefinition, atIndex?: number) {
if (traceEnabled) {
traceWrite(`${this}._addView(${view}, ${atIndex})`, traceCategories.ViewHierarchy);
}
if (!view) {
throw new Error("Expecting a valid View instance.");
}
if (!(view instanceof ViewBase)) {
throw new Error(view + " is not a valid View instance.");
}
if (view.parent) {
throw new Error("View already has a parent. View: " + view + " Parent: " + view.parent);
}
view.parent = this;
this._addViewCore(view, atIndex);
view._parentChanged(null);
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _addViewCore(view: ViewDefinition, atIndex?: number) {
this._propagateInheritableProperties(view);
public _addViewCore(view: ViewCommon, atIndex?: number) {
if (!view._isAddedToNativeVisualTree) {
let nativeIndex = this._childIndexToNativeChildIndex(atIndex);
view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex);
}
// TODO: Discuss this.
if (this._isLoaded) {
view.onLoaded();
}
}
public _propagateInheritableProperties(view: ViewDefinition) {
propagateInheritedProperties(this);
// view._inheritProperties(this);
// view.style._inheritStyleProperties(this);
}
// public _inheritProperties(parentView: ViewDefinition) {
// parentView._eachSetProperty((property) => {
// if (!(property instanceof styling.Property) && property.inheritable) {
// let baseValue = parentView._getValue(property);
// this._setValue(property, baseValue, ValueSource.Inherited);
// }
// return true;
// });
// }
/**
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not outside the UI Stack implementation.
*/
public _removeView(view: ViewDefinition) {
if (traceEnabled) {
traceWrite(`${this}._removeView(${view})`, traceCategories.ViewHierarchy);
}
if (view.parent !== this) {
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view.parent + " ExpectedParent: " + this);
}
this._removeViewCore(view);
view.parent = undefined;
view._parentChanged(this);
super._addViewCore(view, atIndex);
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewDefinition) {
public _removeViewCore(view: ViewCommon) {
// TODO: Change type from ViewCommon to ViewBase. Probably this
// method will need to go to ViewBase class.
// Remove the view from the native visual scene first
this._removeViewFromNativeVisualTree(view);
// TODO: Discuss this.
if (view.isLoaded) {
view.onUnloaded();
super._removeViewCore(view);
}
// view.unsetInheritedProperties();
}
public unsetInheritedProperties(): void {
// this._setValue(ProxyObject.bindingContextProperty, undefined, ValueSource.Inherited);
// this._eachSetProperty((property) => {
// if (!(property instanceof styling.Property) && property.inheritable) {
// this._resetValue(property, ValueSource.Inherited);
// public unsetInheritedProperties(): void {
// // this._setValue(ProxyObject.bindingContextProperty, undefined, ValueSource.Inherited);
// // this._eachSetProperty((property) => {
// // if (!(property instanceof styling.Property) && property.inheritable) {
// // this._resetValue(property, ValueSource.Inherited);
// // }
// // return true;
// // });
// }
// return true;
// });
}
public _parentChanged(oldParent: ViewDefinition): void {
//Overridden
if (oldParent) {
// Move these method in property class.
clearInheritedProperties(this);
}
}
/**
* Method is intended to be overridden by inheritors and used as "protected".
@@ -1173,36 +978,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
view._isAddedToNativeVisualTree = false;
}
public _goToVisualState(state: string) {
if (traceEnabled) {
traceWrite(this + " going to state: " + state, traceCategories.Style);
}
if (state === this._visualState) {
return;
}
this.deletePseudoClass(this._visualState);
this._visualState = state;
this.addPseudoClass(state);
}
public _applyXmlAttribute(attribute, value): boolean {
if (attribute === "style") {
this._applyInlineStyle(value);
return true;
}
return false;
}
public setInlineStyle(style: string): void {
if (typeof style !== "string") {
throw new Error("Parameter should be valid CSS string!");
}
this._applyInlineStyle(style);
}
public _updateLayout() {
// needed for iOS.
}
@@ -1253,30 +1028,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return new Animation([animation]);
}
public _registerAnimation(animation: KeyframeAnimation) {
if (this._registeredAnimations === undefined) {
this._registeredAnimations = new Array<KeyframeAnimation>();
}
this._registeredAnimations.push(animation);
}
public _unregisterAnimation(animation: KeyframeAnimation) {
if (this._registeredAnimations) {
let index = this._registeredAnimations.indexOf(animation);
if (index >= 0) {
this._registeredAnimations.splice(index, 1);
}
}
}
public _unregisterAllAnimations() {
if (this._registeredAnimations) {
for (let animation of this._registeredAnimations) {
animation.cancel();
}
}
}
public toString(): string {
let str = this.typeName;
if (this.id) {
@@ -1304,88 +1055,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// // Check for a valid _nativeView instance
// return !!this._nativeView;
// }
private notifyPseudoClassChanged(pseudoClass: string): void {
this.notify({ eventName: ":" + pseudoClass, object: this });
}
// TODO: Make sure the state is set to null and this is called on unloaded to clean up change listeners...
_setCssState(next: CssState): void {
const previous = this._cssState;
this._cssState = next;
if (!this._invalidateCssHandler) {
this._invalidateCssHandler = () => {
if (this._invalidateCssHandlerSuspended) {
return;
}
this.applyCssState();
};
}
try {
this._invalidateCssHandlerSuspended = true;
if (next) {
next.changeMap.forEach((changes, view) => {
if (changes.attributes) {
changes.attributes.forEach(attribute => {
view.addEventListener(attribute + "Change", this._invalidateCssHandler)
});
}
if (changes.pseudoClasses) {
changes.pseudoClasses.forEach(pseudoClass => {
let eventName = ":" + pseudoClass;
view.addEventListener(":" + pseudoClass, this._invalidateCssHandler);
if (view[eventName]) {
view[eventName](+1);
}
});
}
});
}
if (previous) {
previous.changeMap.forEach((changes, view) => {
if (changes.attributes) {
changes.attributes.forEach(attribute => {
view.removeEventListener("onPropertyChanged:" + attribute, this._invalidateCssHandler)
});
}
if (changes.pseudoClasses) {
changes.pseudoClasses.forEach(pseudoClass => {
let eventName = ":" + pseudoClass;
view.removeEventListener(eventName, this._invalidateCssHandler)
if (view[eventName]) {
view[eventName](-1);
}
});
}
});
}
} finally {
this._invalidateCssHandlerSuspended = false;
}
this.applyCssState();
}
/**
* Notify that some attributes or pseudo classes that may affect the current CssState had changed.
*/
private _invalidateCssHandler;
private _invalidateCssHandlerSuspended: boolean;
private applyCssState(): void {
if (!this._cssState) {
return;
}
// this.style._beginUpdate();
this._cssState.apply();
// this.style._endUpdate();
}
}
function getLengthEffectiveValue(density: number, param: Length): number {
@@ -1512,39 +1181,6 @@ export namespace Length {
}
}
function onCssClassPropertyChanged(view: ViewCommon, oldValue: string, newValue: string) {
let classes = view.cssClasses;
classes.clear();
if (typeof newValue === "string") {
newValue.split(" ").forEach(c => classes.add(c));
}
}
export const classNameProperty = new Property<ViewCommon, string>({ name: "className", valueChanged: onCssClassPropertyChanged });
classNameProperty.register(ViewCommon);
function resetStyles(view: ViewCommon): void {
// view.style._resetCssValues();
// view._applyStyleFromScope();
view._eachChildView((child) => {
// TODO.. Check old implementation....
resetStyles(child);
return true;
});
}
export const idProperty = new Property<ViewCommon, string>({ name: "id", valueChanged: (view, oldValue, newValue) => resetStyles(view) });
idProperty.register(ViewCommon);
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 function booleanConverter(v: string): boolean {
let lowercase = (v + '').toLowerCase();
if (lowercase === "true") {
@@ -1556,6 +1192,15 @@ export function booleanConverter(v: string): boolean {
throw new Error(`Invalid boolean: ${v}`);
}
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 });
isEnabledProperty.register(ViewCommon);

View File

@@ -76,15 +76,18 @@ export class View extends ViewCommon {
}
}
public onLoaded() {
protected onLoaded() {
super.onLoaded();
this.setOnTouchListener();
}
public onUnloaded() {
if (this.touchListenerIsSet) {
this._nativeView.setOnTouchListener(null);
this.touchListenerIsSet = false;
this._unregisterAllAnimations();
}
this._cancelAllAnimations();
super.onUnloaded();
}
@@ -99,12 +102,12 @@ export class View extends ViewCommon {
this._nativeView.setClickable(true);
}
let touchListener = this.touchListener || new TouchListener(new WeakRef(this));
this._nativeView.setOnTouchListener(touchListener);
this.touchListener = this.touchListener || new TouchListener(new WeakRef(this));
this._nativeView.setOnTouchListener(this.touchListener);
}
}
public _addViewCore(view: View, atIndex?: number) {
public _addViewCore(view: ViewCommon, atIndex?: number) {
if (this._context) {
view._onAttached(this._context);
}
@@ -127,6 +130,7 @@ export class View extends ViewCommon {
if (traceEnabled) {
traceWrite(`${this}._onAttached(context)`, traceCategories.VisualTreeEvents);
}
if (this._context === context) {
return;
}
@@ -158,6 +162,9 @@ export class View extends ViewCommon {
return true;
}
this._eachChildView(eachChild);
} else if (this._nativeView) {
// copy all the locally cached values to the native android widget
applyNativeSetters(this);
}
}

View File

@@ -1,18 +1,17 @@
declare module "ui/core/view" {
import { GestureTypes, GesturesObserver, GestureEventData } from "ui/gestures";
import { GestureTypes, GesturesObserver, GestureEventData, TouchGestureEventData, TouchAction } from "ui/gestures";
import { Animation, AnimationDefinition, AnimationPromise } from "ui/animation";
import { KeyframeAnimation } from "ui/animation/keyframe-animation";
import {
ViewBase, Property, CssProperty, InheritedCssProperty, ShorthandProperty, Style,
BindingOptions, Observable, EventData,
ViewBase, Property, CssProperty, InheritedCssProperty, Style,
BindingOptions, Observable, EventData
} from "ui/core/view-base";
import { Background } from "ui/styling/background";
import { Font } from "ui/styling/font";
import { Color } from "color";
export {
GestureTypes, GesturesObserver, GestureEventData,
Animation, AnimationDefinition, AnimationPromise, KeyframeAnimation,
GestureTypes, GesturesObserver, GestureEventData, TouchGestureEventData, TouchAction,
Animation, AnimationDefinition, AnimationPromise,
Background, Font, Color
}
@@ -356,16 +355,6 @@ declare module "ui/core/view" {
cssClasses: Set<string>;
cssPseudoClasses: Set<string>;
/**
* Gets the parent view. This property is read-only.
*/
public parent: View;
/**
* Gets owner page. This is a read-only property.
*/
page: View;
/**
* This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters.
* The actual measurement work of a view is performed in onMeasure(int, int), called by this method. Therefore, only onMeasure(int, int) can and must be overridden by subclasses.
@@ -550,29 +539,9 @@ declare module "ui/core/view" {
*/
public getActualSize(): Size;
/**
* @protected
* @unstable
* A widget can call this method to add a matching css pseudo class.
*/
public addPseudoClass(name: string): void;
/**
* @protected
* @unstable
* A widget can call this method to discard mathing css pseudo class.
*/
public deletePseudoClass(name: string): void;
// Lifecycle events
onLoaded(): void;
onUnloaded(): void;
isLoaded: boolean;
_addView(view: View, atIndex?: number);
_propagateInheritableProperties(view: View);
// _inheritProperties(parentView: View);
_removeView(view: View);
_context: any /* android.content.Context */;
_childIndexToNativeChildIndex(index?: number): number;
@@ -595,18 +564,11 @@ declare module "ui/core/view" {
isCollapsed: boolean;
isLayoutRequired: boolean;
_parentChanged(oldParent: View): void;
_gestureObservers: any;
// _isInheritedChange(): boolean;
_domId: number;
_cssState: any /* "ui/styling/style-scope" */;
_setCssState(next: any /* "ui/styling/style-scope" */);
_registerAnimation(animation: KeyframeAnimation);
_unregisterAnimation(animation: KeyframeAnimation);
_unregisterAllAnimations();
_isAddedToNativeVisualTree: boolean;
/**
@@ -740,8 +702,6 @@ declare module "ui/core/view" {
export function measureSpecToString(measureSpec: number): string;
}
export const classNameProperty: Property<View, string>;
export const idProperty: Property<View, string>;
export const automationTextProperty: Property<View, string>;
export const originXProperty: Property<View, number>;
export const originYProperty: Property<View, number>;

View File

@@ -49,12 +49,9 @@ export class DatePicker extends DatePickerBase {
}
public _createUI() {
if (!this._listener) {
this._listener = new DateChangedListener(new WeakRef(this));
}
this._android = new android.widget.DatePicker(this._context);
this._android.setCalendarViewShown(false);
this._listener = this._listener = new DateChangedListener(new WeakRef(this));
this._android.init(0, 0, 0, this._listener);
}

View File

@@ -9,9 +9,10 @@ declare module "ui/core/view-base" {
import { Style } from "ui/styling/style";
import { isIOS } from "platform";
import { fromString as gestureFromString } from "ui/gestures";
import { KeyframeAnimation } from "ui/animation/keyframe-animation";
export {
Observable, EventData,
Observable, EventData, KeyframeAnimation,
Binding, BindingOptions, Bindable, Style, isIOS, gestureFromString
};
@@ -41,10 +42,17 @@ declare module "ui/core/view-base" {
public android: any;
public nativeView: any;
public bindingContext: any;
/**
* Gets the parent view. This property is read-only.
*/
public parent: ViewBase;
public readonly parent: ViewBase;
/**
* Gets owner page. This is a read-only property.
*/
public readonly page: ViewBase;
/**
* Gets the style object associated to this view.
*/
@@ -52,15 +60,50 @@ declare module "ui/core/view-base" {
/**
* Returns true if visibility is set to 'collapse'.
* Readonly property
*/
public isCollapsed: boolean;
public readonly isLoaded: boolean;
// public onLoaded(): void;
// public onUnloaded(): void;
public bind(options: BindingOptions, source: Object): void;
public unbind(property: string): void;
public requestLayout(): void;
public eachChild(callback: (child: ViewBase) => boolean): void;
public _addView(view: ViewBase, atIndex?: number): void;
public _removeView(view: ViewBase): void;
public _parentChanged(oldParent: ViewBase): void;
_childrenCount: number;
_cssState: any /* "ui/styling/style-scope" */;
_setCssState(next: any /* "ui/styling/style-scope" */);
_registerAnimation(animation: KeyframeAnimation);
_unregisterAnimation(animation: KeyframeAnimation);
_cancelAllAnimations();
/**
* @protected
* @unstable
* A widget can call this method to add a matching css pseudo class.
*/
public addPseudoClass(name: string): void;
/**
* @protected
* @unstable
* A widget can call this method to discard mathing css pseudo class.
*/
public deletePseudoClass(name: string): void;
}
export const idProperty: Property<ViewBase, string>;
export const classNameProperty: Property<ViewBase, string>;
export const bindingContextProperty: InheritedProperty<ViewBase, any>;
}
declare module "ui/core/properties" {
@@ -80,7 +123,7 @@ declare module "ui/core/properties" {
}
export interface CoerciblePropertyOptions<T, U> extends PropertyOptions<T, U> {
coerceValue(T, U): U
readonly coerceValue: (t: T, u: U) => U;
}
export interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<T, U> {
@@ -134,4 +177,6 @@ declare module "ui/core/properties" {
public readonly cssName: string;
public register(cls: { prototype: T }): void;
}
export function applyNativeSetters(view: ViewBase): void;
}

View File

@@ -4,15 +4,14 @@ import { Button } from "ui/button";
import { TextField } from "ui/text-field";
import { Label } from "ui/label";
import { View, Color } from "ui/core/view";
import * as types from "utils/types";
export let STRING = "string",
PROMPT = "Prompt",
CONFIRM = "Confirm",
ALERT = "Alert",
LOGIN = "Login",
OK = "OK",
CANCEL = "Cancel";
export const STRING = "string";
export const PROMPT = "Prompt";
export const CONFIRM = "Confirm";
export const ALERT = "Alert";
export const LOGIN = "Login";
export const OK = "OK";
export const CANCEL = "Cancel";
/**
* Defines the input type for prompt dialog.
@@ -65,7 +64,7 @@ let buttonBackgroundColor: Color;
// NOTE: This will fail if app.css is changed.
export function getButtonBackgroundColor(): Color {
if (!buttonBackgroundColor) {
let btn = new button.Button();
let btn = new Button();
applySelectors(btn);
buttonBackgroundColor = btn.backgroundColor;
btn.onUnloaded();
@@ -100,5 +99,5 @@ export function getLabelColor(): Color {
}
export function isDialogOptions(arg): boolean {
return !types.isNullOrUndefined(arg) && (arg.message || arg.title);
return arg && (arg.message || arg.title);
}

View File

@@ -7,15 +7,6 @@
import { ad } from "utils/utils";
export class EditableTextBase extends common.EditableTextBase {
private _android: android.widget.EditText;
private _textWatcher: android.text.TextWatcher;
private _keyListenerCache: android.text.method.IKeyListener;
/* tslint:disable */
private _dirtyTextAccumulator: string;
/* tslint:enable */
@Interfaces([android.text.TextWatcher])
class TextWatcher implements android.text.TextWatcher {
constructor(private owner: WeakRef<EditableTextBase>) {
@@ -61,6 +52,9 @@ class TextWatcher implements android.text.TextWatcher {
}
}
//https://github.com/NativeScript/NativeScript/issues/2942
let dismissKeyboardTimeoutId: number;
@Interfaces([android.view.View.OnFocusChangeListener])
class FocusChangeListener implements android.view.View.OnFocusChangeListener {
constructor(private owner: WeakRef<EditableTextBase>) {
@@ -73,7 +67,7 @@ class FocusChangeListener implements android.view.View.OnFocusChangeListener {
return;
}
if (!hasFocus) {
if (hasFocus) {
if (dismissKeyboardTimeoutId) {
// https://github.com/NativeScript/NativeScript/issues/2942
// Don't hide the keyboard since another (or the same) EditText has gained focus.
@@ -118,6 +112,7 @@ class EditorActionListener implements android.widget.TextView.OnEditorActionList
}
owner._onReturnPress();
}
// If action is ACTION_NEXT then do not close keyboard
if (actionId === android.view.inputmethod.EditorInfo.IME_ACTION_NEXT) {
owner._onReturnPress();
@@ -126,6 +121,7 @@ class EditorActionListener implements android.widget.TextView.OnEditorActionList
return false;
}
}
}
export abstract class EditableTextBase extends EditableTextBaseCommon {
_textWatcher: android.text.TextWatcher;

View File

@@ -3,14 +3,11 @@
NavigationTransition, AndroidFragmentCallbacks, AndroidActivityCallbacks
} from "ui/frame";
import { FrameBase, NavigationContext, stack, goBack } from "./frame-common";
import { Page } from "ui/page";
import { View } from "ui/core/view";
import { Observable } from "data/observable";
import { Page, View, Observable } from "ui/page";
import { DIALOG_FRAGMENT_TAG } from "../page/constants";
import * as transitionModule from "ui/transition";
import * as trace from "trace";
import * as application from "application";
import * as types from "utils/types";
export * from "./frame-common";

View File

@@ -2,8 +2,7 @@
* Contains the Frame class, which represents the logical View unit that is responsible for navigation within an application.
*/
declare module "ui/frame" {
import { View, Observable, EventData } from "ui/core/view";
import { Page } from "ui/page";
import { Page, View, Observable, EventData } from "ui/page";
import { Transition } from "ui/transition";
/**

View File

@@ -1,7 +1,6 @@
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from "ui/frame";
import { FrameBase } from "./frame-common";
import { Page } from "ui/page";
import { View } from "ui/core/view";
import { Page, View } from "ui/page";
import * as transitionModule from "ui/transition";
import * as trace from "trace";

View File

@@ -1,5 +1,7 @@
import { GestureTypes as GestureTypesDefinition, GestureEventData, GesturesObserver as GesturesObserverDefinition } from "ui/gestures";
import { View, EventData } from "ui/core/view";
import { View } from "ui/core/view";
export { View, EventData, layout } from "ui/core/view";
export enum GestureTypes {
tap = 1 << 0,

View File

@@ -1,7 +1,5 @@
import { GestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData } from "ui/gestures";
import { GesturesObserverBase, toString, TouchAction, GestureStateTypes, GestureTypes, SwipeDirection } from "./gestures-common";
import { View, layout } from "ui/core/view";
import { EventData } from "data/observable";
import { GesturesObserverBase, toString, TouchAction, GestureStateTypes, GestureTypes, SwipeDirection, View, layout, EventData } from "./gestures-common";
import * as trace from "trace";
export * from "./gestures-common";

View File

@@ -1,11 +1,8 @@
import { GestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, PinchGestureEventData } from "ui/gestures";
import { GesturesObserverBase, toString, TouchAction, GestureStateTypes, GestureTypes, SwipeDirection } from "./gestures-common";
import { View } from "ui/core/view";
import { EventData } from "data/observable";
import { GesturesObserverBase, toString, TouchAction, GestureStateTypes, GestureTypes, SwipeDirection, View, EventData } from "./gestures-common";
import * as trace from "trace";
import * as utils from "utils/utils";
import types = require("utils/types");
import getter = utils.ios.getter;
import { ios } from "utils/utils";
import getter = ios.getter;
export * from "./gestures-common";
@@ -451,7 +448,7 @@ class TouchGestureEventData implements TouchGestureEventData {
}
private getMainPointer(): UITouch {
if (types.isUndefined(this._mainPointer)) {
if (this._mainPointer === undefined) {
this._mainPointer = this.ios.touches.anyObject();
}
return this._mainPointer;

View File

@@ -89,7 +89,7 @@ export class Image extends ImageBase {
let owner = new WeakRef<Image>(this);
this._imageLoadedListener = this._imageLoadedListener || new ImageLoadedListener(new WeakRef(this));
this.imageSource = unsetValue;
this.imageSource = <any>unsetValue;
if (typeof value === "string") {
value = value.trim();
this.isLoading = true;
@@ -98,8 +98,8 @@ export class Image extends ImageBase {
// TODO: Check with runtime what should we do in case of base64 string.
super._createImageSourceFromSrc();
}
else if (imageSource.isFileOrResourcePath(value)) {
if (value.indexOf(utils.RESOURCE_PREFIX) === 0) {
else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
imageView.setUri(value, this.decodeWidth, this.decodeHeight, this.useCache, async, this._imageLoadedListener);
}
else {

View File

@@ -65,10 +65,10 @@ export class ListPicker extends ListPickerBase {
return this._android;
}
public _createUI() {
this._android = new android.widget.NumberPicker(this._context);
this._editText = getEditText(this._android);
let editText = getEditText(this._android);
this._editText = editText;
this._selectorWheelPaint = getSelectorWheelPaint(this._android);
this._android.setDescendantFocusability(android.widget.NumberPicker.FOCUS_BLOCK_DESCENDANTS);
@@ -77,36 +77,24 @@ export class ListPicker extends ListPickerBase {
this._android.setMaxValue(0);
this._android.setValue(0);
let formatter = this._formatter || new Formatter(new WeakRef(this));
this._formatter = this._formatter || new Formatter(new WeakRef(this));
this._android.setFormatter(this._formatter);
this._valueChangedListener = this._valueChangedListener || new ValueChangeListener(new WeakRef(this));
this._android.setOnValueChangedListener(this._valueChangedListener);
if (editText) {
//Fix the disappearing selected item.
//HACK: http://stackoverflow.com/questions/17708325/android-numberpicker-with-formatter-does-not-format-on-first-rendering/26797732
this._editText.setFilters([]);
editText.setFilters([]);
//Since the Android NumberPicker has to always have at least one item, i.e. minValue=maxValue=value=0, we don't want this zero showing up when this.items is empty.
this._editText.setText(" ", android.widget.TextView.BufferType.NORMAL);
editText.setText(" ", android.widget.TextView.BufferType.NORMAL);
}
this._android.setWrapSelectorWheel(false);
}
private updateSelectedValue(): void {
let selectedIndex = this.selectedIndex;
this.android.setValue(selectedIndex);
}
private onItemsPropertyChanged(items: any[] | ItemsSource) {
let maxValue = items && items.length > 0 ? items.length - 1 : 0;
this.android.setMaxValue(maxValue);
this.updateSelectedValue();
this._fixNumberPickerRendering();
}
private _fixNumberPickerRendering() {
//HACK: Force the stubborn NumberPicker to render correctly when we have 0 or 1 items.
this._android.setFormatter(null);
@@ -122,8 +110,8 @@ export class ListPicker extends ListPickerBase {
return -1;
}
set [selectedIndexProperty.native](value: number) {
if (this.itemsSet) {
this.updateSelectedValue();
if (value >= 0) {
this.android.setValue(value);
}
}
@@ -131,23 +119,15 @@ export class ListPicker extends ListPickerBase {
return null;
}
set [itemsProperty.native](value: any[] | ItemsSource) {
this.onItemsPropertyChanged(value);
// items are cleared - set selectedIndex to -1
if (!value) {
this.itemsSet = false;
this.selectedIndex = -1;
} else if (this.selectedIndex < 0) {
// items are set and selectedIndex is set - update maxValue & value.
this.selectedIndex = 0;
// set this flag later so no native call happens
this.itemsSet = true;
}
let maxValue = value && value.length > 0 ? value.length - 1 : 0;
this.android.setMaxValue(maxValue);
this._fixNumberPickerRendering();
}
get [colorProperty.native](): { wheelColor: number, textColor: number } {
return {
wheelColor: this._selectorWheelPaint.getColor(),
textColor: this._editText.getTextColors().getDefaultColor()
textColor: this._editText ? this._editText.getTextColors().getDefaultColor() : -1
}
}
@@ -162,6 +142,8 @@ export class ListPicker extends ListPickerBase {
}
this._selectorWheelPaint.setColor(wheelColor);
if (this._editText) {
this._editText.setTextColor(color);
}
}
}

View File

@@ -1,4 +1,4 @@
import { ListPickerBase, selectedIndexProperty, itemsProperty } from "./list-picker-common";
import { ListPickerBase, Color, selectedIndexProperty, itemsProperty, backgroundColorProperty, colorProperty } from "./list-picker-common";
import { ItemsSource } from "ui/list-picker";
export * from "./list-picker-common";
@@ -30,24 +30,12 @@ export class ListPicker extends ListPickerBase {
return this._ios;
}
private updateSelectedValue(): void {
let selectedIndex = this.selectedIndex;
if (selectedIndex >= 0) {
this.ios.selectRowInComponentAnimated(selectedIndex, 0, false);
}
}
private onItemsPropertyChanged(items: any[] | ItemsSource) {
this.ios.reloadAllComponents();
this.updateSelectedValue();
}
get [selectedIndexProperty.native](): number {
return -1;
}
set [selectedIndexProperty.native](value: number) {
if (this.itemsSet) {
this.updateSelectedValue();
if (value >= 0) {
this.ios.selectRowInComponentAnimated(value, 0, false);
}
}
@@ -55,17 +43,21 @@ export class ListPicker extends ListPickerBase {
return null;
}
set [itemsProperty.native](value: any[] | ItemsSource) {
this.onItemsPropertyChanged(value);
// items are cleared - set selectedIndex to -1
if (!value) {
this.itemsSet = false;
this.selectedIndex = -1;
} else if (this.selectedIndex < 0) {
// items are set and selectedIndex is set - update maxValue & value.
this.selectedIndex = 0;
// set this flag later so no native call happens
this.itemsSet = true;
this.ios.reloadAllComponents();
}
get [backgroundColorProperty.native](): UIColor {
return this._ios.backgroundColor;
}
set [backgroundColorProperty.native](value: UIColor | Color) {
this._ios.backgroundColor = value instanceof Color ? value.ios : value;
}
get [colorProperty.native](): UIColor {
return this._ios.tintColor;
}
set [colorProperty.native](value: UIColor | Color) {
this._ios.tintColor = value instanceof Color ? value.ios : value;
}
}
@@ -117,51 +109,3 @@ class ListPickerDelegateImpl extends NSObject implements UIPickerViewDelegate {
}
}
}
export class ListPickerStyler implements Styler {
// background-color
private static setBackgroundColorProperty(view: View, newValue: any) {
var picker = <UIPickerView>view._nativeView;
picker.backgroundColor = newValue;
}
private static resetBackgroundColorProperty(view: View, nativeValue: any) {
var picker = <UIPickerView>view._nativeView;
picker.backgroundColor = nativeValue;
}
private static getBackgroundColorProperty(view: View): any {
var picker = <UIPickerView>view._nativeView;
return picker.backgroundColor;
}
// color
private static setColorProperty(view: View, newValue: any) {
var picker = <UIPickerView>view._nativeView;
picker.tintColor = newValue;
}
private static resetColorProperty(view: View, nativeValue: any) {
var picker = <UIPickerView>view._nativeView;
picker.tintColor = nativeValue;
}
private static getColorProperty(view: View): any {
var picker = <UIPickerView>view._nativeView;
return picker.tintColor;
}
public static registerHandlers() {
registerHandler(backgroundColorProperty, new StylePropertyChangedHandler(
ListPickerStyler.setBackgroundColorProperty,
ListPickerStyler.resetBackgroundColorProperty,
ListPickerStyler.getBackgroundColorProperty), "ListPicker");
registerHandler(colorProperty, new StylePropertyChangedHandler(
ListPickerStyler.setColorProperty,
ListPickerStyler.resetColorProperty,
ListPickerStyler.getColorProperty), "ListPicker");
}
}
ListPickerStyler.registerHandlers();

View File

@@ -131,17 +131,21 @@ export class ListView extends ListViewBase {
this._realizedTemplates.clear();
}
get [separatorColor.native](): number {
return null;
}
set [separatorColor.native](value: Color) {
get [separatorColor.native](): { dividerHeight: number, divider: android.graphics.drawable.Drawable } {
let nativeView = this._android;
if (value) {
return {
dividerHeight: nativeView.getDividerHeight(),
divider: nativeView.getDivider()
};
}
set [separatorColor.native](value: Color | { dividerHeight: number, divider: android.graphics.drawable.Drawable }) {
let nativeView = this._android;
if (value instanceof Color) {
nativeView.setDivider(new android.graphics.drawable.ColorDrawable(value.android));
nativeView.setDividerHeight(1);
} else {
nativeView.setDivider(null);
nativeView.setDividerHeight(0);
nativeView.setDivider(value.divider);
nativeView.setDividerHeight(value.dividerHeight);
}
}
@@ -283,26 +287,3 @@ function ensureListViewAdapterClass() {
ListViewAdapterClass = ListViewAdapter;
}
export class ListViewStyler implements Styler {
// separator-color
private static setSeparatorColorProperty(view: viewModule.View, newValue: any) {
let listView = <android.widget.ListView>view._nativeView;
listView.setDivider(new android.graphics.drawable.ColorDrawable(newValue));
listView.setDividerHeight(1);
}
private static resetSeparatorColorProperty(view: viewModule.View, nativeValue: any) {
let listView = <android.widget.ListView>view._nativeView;
listView.setDivider(new android.graphics.drawable.ColorDrawable(nativeValue));
listView.setDividerHeight(1);
}
public static registerHandlers() {
registerHandler(separatorColorProperty, new StylePropertyChangedHandler(
ListViewStyler.setSeparatorColorProperty,
ListViewStyler.resetSeparatorColorProperty), "ListView");
}
}
ListViewStyler.registerHandlers();

View File

@@ -46,10 +46,10 @@ export class PageBase extends ContentView implements PageDefinition {
this.style[backgroundColorProperty.cssName] = new Color("white");
}
public onLoaded() {
this._applyCss();
super.onLoaded();
}
// public onLoaded() {
// this._applyCss();
// super.onLoaded();
// }
get navigationContext(): any {

View File

@@ -3,6 +3,8 @@ import { ActionBar } from "ui/action-bar";
import { GridLayout } from "ui/layouts/grid-layout";
import { DIALOG_FRAGMENT_TAG } from "./constants";
import { device } from "platform";
import { applyNativeSetters } from "ui/core/properties";
import * as trace from "trace";
export * from "./page-common";
@@ -14,7 +16,7 @@ const STATUS_BAR_DARK_BCKG = 1711276032;
interface DialogFragmentClass {
new (owner: Page, fullscreen: boolean, shownCallback: () => void, dismissCallback: () => void): android.app.DialogFragment;
}
var DialogFragmentClass: DialogFragmentClass;
let DialogFragmentClass: DialogFragmentClass;
function ensureDialogFragmentClass() {
if (DialogFragmentClass) {
@@ -32,7 +34,7 @@ function ensureDialogFragmentClass() {
}
public onCreateDialog(savedInstanceState: android.os.Bundle): android.app.Dialog {
var dialog = new android.app.Dialog(this._owner._context);
const dialog = new android.app.Dialog(this._owner._context);
dialog.requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
// Hide actionBar and adjust alignment based on _fullscreen value.
@@ -42,7 +44,7 @@ function ensureDialogFragmentClass() {
dialog.setContentView(this._owner._nativeView, this._owner._nativeView.getLayoutParams());
var window = dialog.getWindow();
const window = dialog.getWindow();
window.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
if (this._fullscreen) {
@@ -124,7 +126,7 @@ export class Page extends PageBase {
}
public _onDetached(force?: boolean) {
var skipDetached = !force && this.frame.android.cachePagesOnNavigate && !this._isBackNavigation;
const skipDetached = !force && this.frame.android.cachePagesOnNavigate && !this._isBackNavigation;
if (skipDetached) {
// Do not detach the context and android reference.
@@ -153,7 +155,7 @@ export class Page extends PageBase {
this._onAttached(parent._context);
this._isAddedToNativeVisualTree = true;
this._syncNativeProperties();
applyNativeSetters(this);
ensureDialogFragmentClass();

View File

@@ -11,6 +11,8 @@ declare module "ui/page" {
import styleScope = require("ui/styling/style-scope");
//@endprivate
export * from "ui/content-view";
/**
* Defines the data for the page navigation events.
*/

View File

@@ -1,17 +1,9 @@
import { Progress as ProgressDefinition } from "ui/progress";
import { View, Property } from "ui/core/view";
import { View, Property, CoercibleProperty } from "ui/core/view";
export * from "ui/core/view";
export class ProgressBase extends View implements ProgressDefinition {
constructor() {
super();
// This calls make both platforms have default values from 0 to 100.
this.maxValue = 100;
this.value = 0;
}
public value: number;
public maxValue: number;
// get maxValue(): number {
@@ -38,11 +30,11 @@ export class ProgressBase extends View implements ProgressDefinition {
/**
* Represents the observable property backing the value property of each Progress instance.
*/
export const valueProperty = new Property<ProgressBase, number>({ name: "value", defaultValue: 0 });
export const valueProperty = new CoercibleProperty<ProgressBase, number>({ name: "value", defaultValue: 0, coerceValue: (t, v) => v < 0 ? 0 : Math.min(v, t.maxValue) });
valueProperty.register(ProgressBase);
/**
* Represents the observable property backing the maxValue property of each Progress instance.
*/
export const maxValueProperty = new Property<ProgressBase, number>({ name: "maxValue", defaultValue: 100 });
export const maxValueProperty = new Property<ProgressBase, number>({ name: "maxValue", defaultValue: 100, valueChanged: (target, oldValue, newValue) => valueProperty.coerce(target) });
maxValueProperty.register(ProgressBase);

View File

@@ -1,5 +1,7 @@
import { ProgressBase, valueProperty, maxValueProperty } from "./progress-common";
import { View, Color, colorProperty, backgroundColorProperty, backgroundInternalProperty } from "ui/core/view";
import {
View, Color, ProgressBase, valueProperty, maxValueProperty,
colorProperty, backgroundColorProperty, backgroundInternalProperty
} from "./progress-common";
export * from "./progress-common";

View File

@@ -1,16 +1,12 @@
import { ProgressBase, valueProperty, maxValueProperty } from "./progress-common";
import { View, Color, colorProperty, backgroundColorProperty, backgroundInternalProperty } from "ui/core/view";
import {
ProgressBase, View, Color, valueProperty, maxValueProperty,
colorProperty, backgroundColorProperty, backgroundInternalProperty
} from "./progress-common";
export * from "./progress-common";
export class Progress extends ProgressBase {
private _ios: UIProgressView;
constructor() {
super();
this._ios = UIProgressView.new();
}
private _ios = UIProgressView.new();
get ios(): UIProgressView {
return this._ios;

View File

@@ -1,7 +1,7 @@
import types = require("utils/types");
import { ProxyViewContainer as ProxyViewContainerDefinition } from "ui/proxy-view-container";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories } from "trace";
import { LayoutBase, View } from "ui/layouts/layout-base";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories } from "trace";
/**
* Proxy view container that adds all its native children directly to the parent.
* To be used as a logical grouping container of views.
@@ -50,7 +50,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
public _eachLayoutView(callback: (View) => void): void {
this._eachChildView((cv) => {
if (cv._isVisible) {
if (!cv.isCollapsed) {
cv._eachLayoutView(callback);
}
return true;

View File

@@ -1,7 +1,7 @@
import { SegmentedBar as SegmentedBarDefinition, SegmentedBarItem as SegmentedBarItemDefinition } from "ui/segmented-bar";
import { SegmentedBar as SegmentedBarDefinition, SegmentedBarItem as SegmentedBarItemDefinition, SelectedIndexChangedEventData } from "ui/segmented-bar";
import {
View, AddChildFromBuilder, AddArrayFromBuilder,
Property, EventData, Color, Bindable
ViewBase, View, AddChildFromBuilder, AddArrayFromBuilder,
Property, CoercibleProperty, EventData, Color
} from "ui/core/view";
export * from "ui/core/view";
@@ -10,9 +10,7 @@ export module knownCollections {
export var items = "items";
}
var CHILD_SEGMENTED_BAR_ITEM = "SegmentedBarItem";
export abstract class SegmentedBarItemBase extends Bindable implements SegmentedBarItemDefinition {
export abstract class SegmentedBarItemBase extends ViewBase implements SegmentedBarItemDefinition {
private _title: string = "";
public _parent: SegmentedBarBase;
@@ -32,11 +30,9 @@ export abstract class SegmentedBarItemBase extends Bindable implements Segmented
export abstract class SegmentedBarBase extends View implements SegmentedBarDefinition, AddChildFromBuilder, AddArrayFromBuilder {
public static selectedIndexChangedEvent = "selectedIndexChanged";
protected previousSelectedIndex: number;
public selectedIndex: number;
public items: Array<SegmentedBarItemDefinition>;
public selectedBackgroundColor: Color;
public items: Array<SegmentedBarItemDefinition>;
public _addArrayFromBuilder(name: string, value: Array<any>): void {
if (name === "items") {
@@ -45,7 +41,7 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin
}
public _addChildFromBuilder(name: string, value: any): void {
if (name === CHILD_SEGMENTED_BAR_ITEM) {
if (name === "SegmentedBarItem") {
if (!this.items) {
this.items = new Array<SegmentedBarItemBase>();
}
@@ -53,58 +49,52 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin
}
}
public _adjustSelectedIndex(items: Array<SegmentedBarItemDefinition>) {
if (this.items) {
if (this.items.length > 0) {
if (this.selectedIndex === undefined || (this.selectedIndex > this.items.length - 1)) {
this.selectedIndex = 0;
}
} else {
this.selectedIndex = undefined;
}
} else {
this.selectedIndex = undefined;
// public _onBindingContextChanged(oldValue: any, newValue: any) {
// super._onBindingContextChanged(oldValue, newValue);
// if (this.items && this.items.length > 0) {
// var i = 0;
// var length = this.items.length;
// for (; i < length; i++) {
// this.items[i].bindingContext = newValue;
// }
// }
// }
public onItemsChanged(oldItems: SegmentedBarItemDefinition[], newItems: SegmentedBarItemDefinition[]): void {
if (oldItems) {
for (let i = 0, count = oldItems.length; i < count; i++) {
this._removeView(oldItems[i]);
}
}
public _onBindingContextChanged(oldValue: any, newValue: any) {
super._onBindingContextChanged(oldValue, newValue);
if (this.items && this.items.length > 0) {
var i = 0;
var length = this.items.length;
for (; i < length; i++) {
this.items[i].bindingContext = newValue;
if (newItems) {
for (let i = 0, count = newItems.length; i < count; i++) {
this._addView(newItems[i]);
}
}
}
public abstract insertTab(tabItem: SegmentedBarItemBase, index?: number): void;
public getValidIndex(index?: number): number {
let idx: number;
let itemsLength = this.items ? this.items.length : 0;
if (index === null || index === undefined) {
idx = itemsLength - 1;
} else {
if (index < 0 || index > itemsLength) {
idx = itemsLength - 1;
} else {
idx = index;
}
}
return idx;
}
public onSelectedIndexChanged(oldValue: number, newValue: number): void {
this.previousSelectedIndex = oldValue;
}
}
/**
* Gets or sets the selected index dependency property of the SegmentedBar.
*/
export const selectedIndexProperty = new Property<SegmentedBarBase, number>({
name: "selectedIndex", defaultValue: -1, valueConverter: (v) => parseInt(v), valueChanged: (target, oldValue, newValue) => target.onSelectedIndexChanged(oldValue, newValue)
export const selectedIndexProperty = new CoercibleProperty<SegmentedBarBase, number>({
name: "selectedIndex", defaultValue: -1, valueConverter: (v) => parseInt(v), valueChanged: (target, oldValue, newValue) => {
target.notify(<SelectedIndexChangedEventData>{ eventName: SegmentedBarBase.selectedIndexChangedEvent, object: target, oldIndex: oldValue, newIndex: newValue });
},
coerceValue: (target, value) => {
let items = target.items;
if (items) {
let max = items.length - 1;
if (value > max) {
value = max;
}
} else {
value = -1;
}
return value;
}
});
selectedIndexProperty.register(SegmentedBarBase);
@@ -117,5 +107,10 @@ selectedBackgroundColorProperty.register(SegmentedBarBase);
/**
* Gets or sets the items dependency property of the SegmentedBar.
*/
export const itemsProperty = new Property<SegmentedBarBase, SegmentedBarItemDefinition[]>({ name: "items" });
export const itemsProperty = new Property<SegmentedBarBase, SegmentedBarItemDefinition[]>({
name: "items", valueChanged: (target, oldValue, newValue) => {
target.onItemsChanged(oldValue, newValue);
selectedIndexProperty.coerce(target);
}
});
itemsProperty.register(SegmentedBarBase);

View File

@@ -1,6 +1,6 @@
import {
SegmentedBarItemBase, SegmentedBarBase, selectedIndexProperty, itemsProperty, selectedBackgroundColorProperty,
colorProperty, fontInternalProperty, Color, Font
colorProperty, fontInternalProperty, Color, Font, applyNativeSetters
} from "./segmented-bar-common";
export * from "./segmented-bar-common";
@@ -15,6 +15,7 @@ const R_ATTR_STATE_SELECTED = 0x010100a1;
// TODO: Use addView instead of _parent property. This way
// bindingContext and style propagation will work out fo the box.
let apiLevel: number;
// TODO: Move this into widgets.
let SegmentedBarColorDrawableClass;
function ensureSegmentedBarColorDrawableClass() {
@@ -22,6 +23,8 @@ function ensureSegmentedBarColorDrawableClass() {
return;
}
apiLevel = android.os.Build.VERSION.SDK_INT;
class SegmentedBarColorDrawable extends android.graphics.drawable.ColorDrawable {
constructor(arg: any) {
super(arg);
@@ -40,15 +43,95 @@ function ensureSegmentedBarColorDrawableClass() {
SegmentedBarColorDrawableClass = SegmentedBarColorDrawable;
}
function setBackground(view: android.view.View, background: android.graphics.drawable.Drawable): void {
if (apiLevel >= 16) {
view.setBackground(background);
} else {
view.setBackgroundDrawable(background);
}
}
export class SegmentedBarItem extends SegmentedBarItemBase {
public _update() {
if (this._parent && this._parent.android) {
// TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set.
// http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android
const tabIndex = this._parent.items.indexOf(this);
const titleTextViewId = 16908310; // http://developer.android.com/reference/android/R.id.html#title
const titleTextView = <android.widget.TextView>this._parent.android.getTabWidget().getChildAt(tabIndex).findViewById(titleTextViewId);
titleTextView.setText(this.title || "");
private _nativeView: android.widget.TextView;
public setNativeView(textView: android.widget.TextView): void {
this._nativeView = textView;
applyNativeSetters(this);
if (this.titleDirty) {
this._update();
}
}
private titleDirty: boolean;
public _update(): void {
// if (this._parent && this._parent.android) {
// // TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set.
// // http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android
// const tabIndex = this._parent.items.indexOf(this);
// const titleTextViewId = 16908310; // http://developer.android.com/reference/android/R.id.html#title
// const titleTextView = <android.widget.TextView>this._parent.android.getTabWidget().getChildAt(tabIndex).findViewById(titleTextViewId);
// titleTextView.setText(this.title || "");
// }
let tv = this._nativeView;
if (tv) {
tv.setText(this.title || "");
this.titleDirty = false;
} else {
this.titleDirty = true;
}
}
get [colorProperty.native](): number {
return this._nativeView.getCurrentTextColor();
}
set [colorProperty.native](value: Color | number) {
let color = typeof value === "Color" ? value.android : value;
this._nativeView.setTextColor(color);
}
get [fontInternalProperty.native](): { typeface: android.graphics.Typeface, fontSize: number } {
let textView = this._nativeView;
return {
typeface: textView.getTypeface(),
fontSize: textView.getTextSize()
};
}
set [fontInternalProperty.native](value: Font | { typeface: android.graphics.Typeface, fontSize: number }) {
let tv = this._nativeView;
if (value instanceof Font) {
tv.setTypeface(value.getAndroidTypeface());
tv.setTextSize(value.fontSize);
} else {
tv.setTypeface(value.typeface);
tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.fontSize);
}
}
get [selectedBackgroundColorProperty.native](): android.graphics.drawable.Drawable {
let viewGroup = <android.view.ViewGroup>this._nativeView.getParent();
return viewGroup.getBackground();
}
set [selectedBackgroundColorProperty.native](value: Color | android.graphics.drawable.Drawable) {
let viewGroup = <android.view.ViewGroup>this._nativeView.getParent();
if (value instanceof Color) {
let color = value.android;
let backgroundDrawable = viewGroup.getBackground();
if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") {
backgroundDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN);
} else {
let stateDrawable = new android.graphics.drawable.StateListDrawable();
let arr = Array.create("int", 1);
arr[0] = R_ATTR_STATE_SELECTED;
let colorDrawable: android.graphics.drawable.ColorDrawable = new SegmentedBarColorDrawableClass(color);
stateDrawable.addState(arr, colorDrawable);
stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom());
setBackground(viewGroup, stateDrawable);
}
} else {
setBackground(viewGroup, value);
}
}
}
@@ -77,8 +160,15 @@ class TabContentFactory implements android.widget.TabHost.TabContentFactory {
let owner = this.owner.get();
if (owner) {
let tv = new android.widget.TextView(owner._context);
// TODO: Why do we set it to collapse???
let index = parseInt(tag);
// This is collapsed by default and made visibile
// by android when TabItem becomes visible/selected.
// TODO: Try commenting visigility change.
tv.setVisibility(android.view.View.GONE);
tv.setMaxLines(1);
tv.setEllipsize(android.text.TextUtils.TruncateAt.END);
(<SegmentedBarItem>owner.items[index]).setNativeView(tv);
return tv;
} else {
throw new Error(`Invalid owner: ${this.owner}`);
@@ -95,9 +185,6 @@ export class SegmentedBar extends SegmentedBarBase {
public _createUI() {
ensureTabHostClass();
ensureSegmentedBarColorDrawableClass();
if (this.apiLevel === undefined) {
this.apiLevel = android.os.Build.VERSION.SDK_INT;
}
let weakRef = new WeakRef(this);
this._android = new TabHostClass(this._context, null);
@@ -128,14 +215,14 @@ export class SegmentedBar extends SegmentedBarBase {
return this._android;
}
public insertTab(tabItem: SegmentedBarItem, index?: number): void {
tabItem._parent = this;
const tab = this.android.newTabSpec(this.getValidIndex(index) + "");
public insertTab(tabItem: SegmentedBarItem, index: number): void {
const tab = this.android.newTabSpec(index + "");
tab.setIndicator(tabItem.title);
tab.setContent(this.tabContentFactory);
this.android.addTab(tab);
let tabHost = this.android;
tabHost.addTab(tab);
// TODO: Why do we need to call this for every added tab?
this.resetNativeListener();
}
@@ -149,155 +236,24 @@ export class SegmentedBar extends SegmentedBarBase {
return -1;
}
set [selectedIndexProperty.native](value: number) {
let items = this.items;
if (!items) {
return;
}
if (value >= 0 && value <= (items.length - 1)) {
this._android.setCurrentTab(value);
this.notify({ eventName: SegmentedBar.selectedIndexChangedEvent, object: this, oldIndex: this.previousSelectedIndex, newIndex: value });
}
}
private previousItems: SegmentedBarItem[];
get [itemsProperty.native](): SegmentedBarItem[] {
return null;
}
set [itemsProperty.native](value: SegmentedBarItem[]) {
const oldItems = this.previousItems;
if (oldItems) {
for (let i = 0, length = oldItems.length; i < length; i++) {
oldItems[i]._parent = null;
}
}
this._android.clearAllTabs();
const newItems = value;
this._adjustSelectedIndex(newItems);
let tabHost = this._android;
if (newItems && newItems.length) {
for (let i = 0; i < newItems.length; i++) {
this.insertTab(newItems[i], i);
}
if (this.selectedIndex !== undefined && tabHost.getCurrentTab() !== this.selectedIndex) {
tabHost.setCurrentTab(this.selectedIndex);
}
let color = this.color ? this.color.android : -1;
for (let i = 0, count = tabHost.getTabWidget().getTabCount(); i < count; i++) {
let vg = <android.view.ViewGroup>tabHost.getTabWidget().getChildTabViewAt(i);
let t = <android.widget.TextView>vg.getChildAt(1);
if (color > -1) {
t.setTextColor(color);
}
t.setMaxLines(1);
t.setEllipsize(android.text.TextUtils.TruncateAt.END);
}
}
}
get [selectedBackgroundColorProperty.native](): android.graphics.drawable.Drawable[] {
let tabHost = this._android;
let result = [];
for (let i = 0, count = tabHost.getTabWidget().getTabCount(); i < count; i++) {
let background = tabHost.getTabWidget().getChildTabViewAt(i).getBackground();
result.push(background);
}
return result;
}
set [selectedBackgroundColorProperty.native](value: Color | android.graphics.drawable.Drawable[]) {
let setValue: boolean;
let color: number;
if (value instanceof Color) {
setValue = true;
color = value.android;
}
let tabHost = this._android;
let apiLevel = this.apiLevel;
for (let i = 0, count = tabHost.getTabWidget().getTabCount(); i < count; i++) {
let vg = <android.view.ViewGroup>tabHost.getTabWidget().getChildTabViewAt(i);
if (setValue) {
let backgroundDrawable = vg.getBackground();
if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") {
backgroundDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN);
} else {
let stateDrawable = new android.graphics.drawable.StateListDrawable();
let arr = Array.create("int", 1);
arr[0] = R_ATTR_STATE_SELECTED;
let colorDrawable: android.graphics.drawable.ColorDrawable = new SegmentedBarColorDrawableClass(color);
stateDrawable.addState(arr, colorDrawable);
stateDrawable.setBounds(0, 15, vg.getRight(), vg.getBottom());
if (android.os.Build.VERSION.SDK_INT >= 16) {
vg.setBackground(stateDrawable);
} else {
vg.setBackgroundDrawable(stateDrawable);
}
}
} else {
if (apiLevel >= 16) {
vg.setBackground(value[i]);
} else {
vg.setBackgroundDrawable(value[i]);
}
}
}
}
get [colorProperty.native](): number {
let textView = new android.widget.TextView(this._context);
return textView.getCurrentTextColor();
}
set [colorProperty.native](value: Color | number) {
let tabHost = this._android;
let color = typeof value === "number" ? value : value.android;
for (let i = 0, count = tabHost.getTabWidget().getTabCount(); i < count; i++) {
let tab = <android.view.ViewGroup>tabHost.getTabWidget().getChildTabViewAt(i);
let t = <android.widget.TextView>tab.getChildAt(1);
t.setTextColor(color);
}
}
get [fontInternalProperty.native](): { typeface: android.graphics.Typeface, fontSize: number } {
let textView = new android.widget.TextView(this._context);
return {
typeface: textView.getTypeface(),
fontSize: textView.getTextSize()
};
}
set [fontInternalProperty.native](value: Font | { typeface: android.graphics.Typeface, fontSize: number }) {
let typeface: android.graphics.Typeface;
let fontSize: number;
let isFont: boolean;
if (value instanceof Font) {
isFont = true;
typeface = value.getAndroidTypeface();
fontSize = value.fontSize;
} else {
typeface = value.typeface;
fontSize = value.fontSize;
}
let tabHost = this._android;
for (let i = 0, count = tabHost.getTabWidget().getTabCount(); i < count; i++) {
let tab = <android.view.ViewGroup>tabHost.getTabWidget().getChildTabViewAt(i);
let t = <android.widget.TextView>tab.getChildAt(1);
t.setTypeface(typeface);
if (isFont) {
t.setTextSize(fontSize);
}
else {
t.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, fontSize);
if (this.selectedIndex < 0) {
this.selectedIndex = tabHost.getCurrentTab();
}
}
}

View File

@@ -3,14 +3,14 @@
*/
declare module "ui/segmented-bar" {
import {
View, AddChildFromBuilder, AddArrayFromBuilder,
Property, EventData, Color, Bindable
ViewBase, View, AddChildFromBuilder, AddArrayFromBuilder,
Property, CoercibleProperty, EventData, Color
} from "ui/core/view";
/**
* Represents a SegmentedBar item.
*/
class SegmentedBarItem extends Bindable {
class SegmentedBarItem extends ViewBase {
/**
* Gets or sets the title of the SegmentedBarItem.
*/
@@ -76,15 +76,12 @@ declare module "ui/segmented-bar" {
*/
public _addChildFromBuilder(name: string, value: any): void;
public _addArrayFromBuilder(name: string, value: Array<any>): void;
public insertTab(tabItem: SegmentedBarItem, index?: number): void;
public getValidIndex(index?: number): number;
}
/**
* Gets or sets the selected index dependency property of the SegmentedBar.
*/
export const selectedIndexProperty: Property<SegmentedBar, number>;
export const selectedIndexProperty: CoercibleProperty<SegmentedBar, number>;
/**
* Gets or sets the selected background color property of the SegmentedBar.

View File

@@ -32,9 +32,9 @@ export class SegmentedBar extends SegmentedBarBase {
return this._ios;
}
public insertTab(tabItem: SegmentedBarItem, index?: number): void {
public insertTab(tabItem: SegmentedBarItem, index: number): void {
tabItem._parent = this;
this.ios.insertSegmentWithTitleAtIndexAnimated(tabItem.title, this.getValidIndex(index), false);
this.ios.insertSegmentWithTitleAtIndexAnimated(tabItem.title, index, false);
}
get [selectedIndexProperty.native](): number {

View File

@@ -1,11 +1,11 @@
import { View } from "./background-common";
import { isNullOrUndefined, isFunction, getClass } from "utils/types";
import { CacheLayerType } from "utils/utils";
import { Button } from "ui/button";
import cssValue = require("css-value");
export * from "./background-common"
// TODO: Change this implementation to use
// We are using "ad" here to avoid namespace collision with the global android object
export module ad {
let SDK: number;
@@ -40,25 +40,25 @@ export module ad {
let cache = <CacheLayerType>v._nativeView;
if (isSetColorFilterOnlyWidget(nativeView)
&& !types.isNullOrUndefined(backgroundDrawable)
&& types.isFunction(backgroundDrawable.setColorFilter)
&& !isNullOrUndefined(backgroundDrawable)
&& isFunction(backgroundDrawable.setColorFilter)
&& !background.hasBorderWidth()
&& !background.hasBorderRadius()
&& !background.clipPath
&& types.isNullOrUndefined(background.image)
&& !types.isNullOrUndefined(background.color)) {
&& isNullOrUndefined(background.image)
&& !isNullOrUndefined(background.color)) {
let backgroundColor = (<any>backgroundDrawable).backgroundColor = background.color.android;
backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
(<any>backgroundDrawable).backgroundColor = backgroundColor;
}
else if (!background.isEmpty()) {
if (!(backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable)) {
let viewClass = types.getClass(v);
let viewClass = getClass(v);
if (!isSetColorFilterOnlyWidget(nativeView) && !_defaultBackgrounds.has(viewClass)) {
_defaultBackgrounds.set(viewClass, nativeView.getBackground());
}
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(1, v.toString());
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(v.toString());
refreshBorderDrawable(v, <org.nativescript.widgets.BorderDrawable>backgroundDrawable);
if (getSDK() >= 16) {
@@ -80,7 +80,7 @@ export module ad {
}
else {
// reset the value with the default native value
if (v instanceof Button) {
if (nativeView instanceof android.widget.Button) {
let nativeButton = new android.widget.Button(nativeView.getContext());
if (getSDK() >= 16) {

View File

@@ -40,11 +40,6 @@ export module ios {
(<CAShapeLayer>nativeView["leftBorderLayer"]).removeFromSuperlayer();
}
let background = <common.Background>view.style._getValue(style.backgroundInternalProperty);
if (!background || background.isEmpty()) {
return undefined;
}
// Clip-path
if (background.clipPath) {
drawClipPath(nativeView, background);
@@ -297,8 +292,8 @@ function drawClipPath(nativeView: UIView, background: Background) {
}
let top = cssValueToDevicePixels(topString, bounds.bottom);
let right = cssValueToDevicePixels("100%", bounds.right) - common.cssValueToDevicePixels(rightString, bounds.right);
let bottom = cssValueToDevicePixels("100%", bounds.bottom) - common.cssValueToDevicePixels(bottomString, bounds.bottom);
let right = cssValueToDevicePixels("100%", bounds.right) - cssValueToDevicePixels(rightString, bounds.right);
let bottom = cssValueToDevicePixels("100%", bounds.bottom) - cssValueToDevicePixels(bottomString, bounds.bottom);
let left = cssValueToDevicePixels(leftString, bounds.right);
path = UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath;

View File

@@ -9,12 +9,14 @@ const FONTS_BASE_PATH = "/fonts/";
const typefaceCache = new Map<string, android.graphics.Typeface>();
let appAssets: android.content.res.AssetManager;
type fontWeight = "100" | "200" | "300" | "normal" | "400" | "500" | "600" | "bold" | "700" | "800" | "900";
export class Font extends FontBase {
public static default = new Font(undefined, undefined, "normal", "normal");
private _typeface: android.graphics.Typeface;
constructor(family: string, size: number, style: "normal" | "italic", weight: "100" | "200" | "300" | "normal" | "400" | "500" | "600" | "bold" | "700" | "800" | "900") {
constructor(family: string, size: number, style: "normal" | "italic", weight: fontWeight) {
super(family, size, style, weight);
}
@@ -26,7 +28,7 @@ export class Font extends FontBase {
return new Font(this.fontFamily, this.fontSize, style, this.fontWeight);
}
public withFontWeight(weight: "100" | "200" | "300" | "normal" | "400" | "500" | "600" | "bold" | "700" | "800" | "900"): Font {
public withFontWeight(weight: fontWeight): Font {
return new Font(this.fontFamily, this.fontSize, this.fontStyle, weight);
}

View File

@@ -1,6 +1,6 @@
//@private
declare module "ui/styling/style-scope" {
import { View } from "ui/core/view";
import { ViewBase } from "ui/core/view-base";
import { SyntaxTree } from "css";
import { RuleSet, Node, SelectorCore, ChangeMap } from "ui/styling/css-selector";
import { KeyframeAnimationInfo } from "ui/animation/keyframe-animation";
@@ -14,7 +14,7 @@ declare module "ui/styling/style-scope" {
/**
* Gets the static selectors that match the view and the dynamic selectors that may potentially match the view.
*/
public changeMap: ChangeMap<View>;
public changeMap: ChangeMap<ViewBase>;
}
export class StyleScope {
@@ -25,7 +25,7 @@ declare module "ui/styling/style-scope" {
public static createSelectorsFromImports(tree: SyntaxTree, keyframes: Object): RuleSet[];
public ensureSelectors(): boolean;
public applySelectors(view: View): void
public applySelectors(view: ViewBase): void
public query(options: Node): SelectorCore[];
public getKeyframeAnimationWithName(animationName: string): KeyframeAnimationInfo;
@@ -33,5 +33,5 @@ declare module "ui/styling/style-scope" {
}
export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (string) => boolean): string;
export function applyInlineSyle(view: View, style: string): void;
export function applyInlineSyle(view: ViewBase, style: string): void;
}

View File

@@ -1,4 +1,4 @@
import { View } from "ui/core/view";
import { ViewBase } from "ui/core/view-base";
import { SyntaxTree, Keyframes, parse as parseCss, Rule, Declaration, Node } from "css";
import { RuleSet, SelectorsMap, SelectorCore, SelectorsMatch, ChangeMap, fromAstNodes } from "ui/styling/css-selector";
import { KeyframeAnimationInfo, KeyframeAnimation } from "ui/animation/keyframe-animation";
@@ -15,10 +15,10 @@ const animationsSymbol: symbol = Symbol("animations");
let pattern: RegExp = /('|")(.*?)\1/;
export class CssState {
constructor(private view: View, private match: SelectorsMatch<View>) {
constructor(private view: ViewBase, private match: SelectorsMatch<ViewBase>) {
}
public get changeMap(): ChangeMap<View> {
public get changeMap(): ChangeMap<ViewBase> {
return this.match.changeMap;
}
@@ -160,7 +160,7 @@ export class StyleScope {
return true;
}
public applySelectors(view: View): void {
public applySelectors(view: ViewBase): void {
this.ensureSelectors();
let state = this._selectors.query(view);
@@ -229,7 +229,7 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx
return null;
}
export function applyInlineSyle(view: View, style: string) {
export function applyInlineSyle(view: ViewBase, style: string) {
try {
let syntaxTree = parseCss("local { " + style + " }", undefined);
let filteredDeclarations = <Declaration[]>(<Rule[]>syntaxTree.stylesheet.rules.filter(isRule))[0].declarations.filter(isDeclaration);
@@ -249,7 +249,7 @@ function isKeyframe(node: Node): node is Keyframes {
return node.type === "keyframes";
}
function applyDescriptors(view: View, ruleset: RuleSet): void {
function applyDescriptors(view: ViewBase, ruleset: RuleSet): void {
let style = view.style;
ruleset.declarations.forEach(d => {
let name = `css-${d.property}`;
@@ -301,7 +301,7 @@ function applyDescriptors(view: View, ruleset: RuleSet): void {
}
}
function applyInlineStyle(view: View, declarations: Declaration[]): void {
function applyInlineStyle(view: ViewBase, declarations: Declaration[]): void {
let style = view.style;
declarations.forEach(d => {
let name = d.property;

View File

@@ -133,10 +133,10 @@ export class Style extends Observable implements StyleDefinition {
// }
// }
// public _resetCssValues() {
// this.view._unregisterAllAnimations();
// this._resetValues(ValueSource.Css);
// }
public _resetCssValues() {
this.view._cancelAllAnimations();
this._resetValues(ValueSource.Css);
}
// public _resetLocalValues() {
// this._resetValues(ValueSource.Local);
@@ -230,11 +230,11 @@ export class Style extends Observable implements StyleDefinition {
// shouldReset = (newValue === property.defaultValue);
// }
public _updateTextDecoration() {
if (this._getValue(textDecorationProperty) !== enums.TextDecoration.none) {
this._applyProperty(textDecorationProperty, this._getValue(textDecorationProperty));
}
}
// public _updateTextDecoration() {
// if (this._getValue(textDecorationProperty) !== enums.TextDecoration.none) {
// this._applyProperty(textDecorationProperty, this._getValue(textDecorationProperty));
// }
// }
// this._view._onStylePropertyChanged(property);
// }

View File

@@ -2,6 +2,9 @@
import { View, AddChildFromBuilder, Property, CssProperty, InheritedCssProperty, Style } from "ui/core/view";
import { FormattedString, FormattedStringView } from "text/formatted-string";
export * from "ui/core/view";
export { FormattedString, FormattedStringView } from "text/formatted-string";
/**
* Represents the base class for all text views.
*/
@@ -73,6 +76,4 @@
export const textTransformProperty: CssProperty<Style, "none" | "capitalize" | "uppercase" | "lowercase">;
export const whiteSpaceProperty: CssProperty<Style, "normal" | "nowrap">;
export const letterSpacingProperty: CssProperty<Style, number>;
export * from "ui/core/view";
}

View File

@@ -24,7 +24,7 @@ export class TimePicker extends TimePickerBase {
public _createUI() {
this.nativeView = new android.widget.TimePicker(this._context);
this._listener = new TimeChangedListener(new WeakRef(this));
this._listener = this._listener || new TimeChangedListener(new WeakRef(this));
this.nativeView.setOnTimeChangedListener(this._listener);
let c = java.util.Calendar.getInstance();