fix tab-view, segmented-bar, action-bar, all ViewBases (#3340)

This commit is contained in:
Hristo Hristov
2016-12-21 12:02:02 +02:00
committed by GitHub
parent 2580dcf575
commit f5905f072d
22 changed files with 175 additions and 249 deletions

3
apps/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"typescript.tsdk": "./node_modules/typescript/lib"
}

View File

@ -1,4 +1,4 @@
{ {
"name": "tns-samples-apps", "name": "tns-samples-apps",
"main": "ui-tests-app/app.js" "main": "cuteness.io/app.js"
} }

View File

@ -353,12 +353,12 @@ export var test_RepeaterItemsGestureBindings = function () {
hasObservers = gestureObservers ? gestureObservers.length > 0 : false; hasObservers = gestureObservers ? gestureObservers.length > 0 : false;
} }
else if (childItem instanceof layoutBaseModule.LayoutBase) { else if (childItem instanceof layoutBaseModule.LayoutBase) {
childItem._eachChildView(eachChildCallback); childItem.eachChildView(eachChildCallback);
} }
return true; return true;
} }
repeater._eachChildView(eachChildCallback); repeater.eachChildView(eachChildCallback);
TKUnit.assertEqual(hasObservers, true, "Every item should have tap observer!"); TKUnit.assertEqual(hasObservers, true, "Every item should have tap observer!");
} }
@ -380,12 +380,12 @@ export var test_RepeaterItemsParentBindingsShouldWork = function () {
} }
} }
else if (childItem instanceof layoutBaseModule.LayoutBase) { else if (childItem instanceof layoutBaseModule.LayoutBase) {
childItem._eachChildView(eachChildCallback); childItem.eachChildView(eachChildCallback);
} }
return true; return true;
} }
repeater._eachChildView(eachChildCallback); repeater.eachChildView(eachChildCallback);
TKUnit.assertEqual(testPass, true, "Every item should have text bound to Page binding context!"); TKUnit.assertEqual(testPass, true, "Every item should have text bound to Page binding context!");
} }

View File

@ -141,9 +141,10 @@ export class ActionBarBase extends View implements ActionBarDefinition {
// this._actionItems.getItems().forEach((item, i, arr) => { item.bindingContext = newValue; }); // this._actionItems.getItems().forEach((item, i, arr) => { item.bindingContext = newValue; });
// } // }
public _eachChildView(callback: (child: View) => boolean) { public eachChildView(callback: (child: View) => boolean) {
if (this.titleView) { const titleView = this.titleView;
callback(this.titleView); if (titleView) {
callback(titleView);
} }
this.actionItems.getItems().forEach((actionItem) => { this.actionItems.getItems().forEach((actionItem) => {
@ -153,6 +154,22 @@ export class ActionBarBase extends View implements ActionBarDefinition {
}); });
} }
public eachChild(callback: (child: ViewBase) => boolean) {
const titleView = this.titleView;
if (titleView) {
callback(titleView);
}
const navigationButton = this._navigationButton;
if (navigationButton) {
callback(navigationButton);
}
this.actionItems.getItems().forEach((actionItem) => {
callback(actionItem);
});
}
public _isEmpty(): boolean { public _isEmpty(): boolean {
if (this.title || if (this.title ||
this.titleView || this.titleView ||

View File

@ -60,9 +60,10 @@ export class ContentView extends CustomLayoutView implements ContentViewDefiniti
} }
} }
public _eachChildView(callback: (child: View) => boolean) { public eachChildView(callback: (child: View) => boolean) {
if (this._content) { const content = this._content;
callback(this._content); if (content) {
callback(content);
} }
} }

View File

@ -91,6 +91,8 @@ export function eachDescendant(view: ViewBaseDefinition, callback: (child: ViewB
view.eachChild(localCallback); view.eachChild(localCallback);
} }
let viewIdCounter = 0;
export class ViewBase extends Observable implements ViewBaseDefinition { export class ViewBase extends Observable implements ViewBaseDefinition {
private _updatingJSPropertiesDict = {}; private _updatingJSPropertiesDict = {};
private _style: Style; private _style: Style;
@ -106,9 +108,12 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public id: string; public id: string;
public className: string; public className: string;
public _domId: number;
public _cssState: CssState; public _cssState: CssState;
constructor() { constructor() {
super(); super();
this._domId = viewIdCounter++;
this._style = new Style(this); this._style = new Style(this);
} }
@ -154,17 +159,11 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
this._emit("loaded"); this._emit("loaded");
} }
get _childrenCount(): number {
return 0;
}
public _loadEachChildView() { public _loadEachChildView() {
if (this._childrenCount > 0) { this.eachChild((child) => {
this.eachChild((child) => { child.onLoaded();
child.onLoaded(); return true;
return true; });
});
}
} }
public onUnloaded() { public onUnloaded() {
@ -175,15 +174,13 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
} }
private _unloadEachChildView() { private _unloadEachChildView() {
if (this._childrenCount > 0) { this.eachChild((child) => {
this.eachChild((child) => { if (child.isLoaded) {
if (child.isLoaded) { child.onUnloaded();
child.onUnloaded(); }
}
return true; return true;
}); });
}
} }
public _applyStyleFromScope() { public _applyStyleFromScope() {

View File

@ -66,8 +66,6 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator
} }
} }
let viewIdCounter = 0;
export abstract class ViewCommon extends ViewBase implements ViewDefinition { export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// Dynamic properties. // Dynamic properties.
left: Length; left: Length;
@ -97,9 +95,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
private _cssType: string; private _cssType: string;
private _updatingInheritedProperties: boolean; private _updatingInheritedProperties: boolean;
public _domId: number;
public _isAddedToNativeVisualTree: boolean; public _isAddedToNativeVisualTree: boolean;
public _gestureObservers = {}; public _gestureObservers = {};
@ -107,8 +102,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
constructor() { constructor() {
super(); super();
this._domId = viewIdCounter++;
this._goToVisualState("normal"); this._goToVisualState("normal");
} }
@ -794,13 +787,13 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
//@endios //@endios
public eachChild(callback: (child: ViewBase) => boolean): void { public eachChild(callback: (child: ViewBase) => boolean): void {
this._eachChildView(<any>callback); this.eachChildView(<any>callback);
} }
public _eachChildView(callback: (view: ViewDefinition) => boolean) { public eachChildView(callback: (view: ViewDefinition) => boolean) {
// //
} }
_childIndexToNativeChildIndex(index?: number): number { _childIndexToNativeChildIndex(index?: number): number {
return index; return index;
} }
@ -857,8 +850,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// // return true; // // return true;
// // }); // // });
// } // }
/** /**
* Method is intended to be overridden by inheritors and used as "protected". * Method is intended to be overridden by inheritors and used as "protected".
*/ */
public _addViewToNativeVisualTree(view: ViewDefinition, atIndex?: number): boolean { public _addViewToNativeVisualTree(view: ViewDefinition, atIndex?: number): boolean {

View File

@ -148,23 +148,18 @@ export class View extends ViewCommon {
traceNotifyEvent(this, "_onAttached"); traceNotifyEvent(this, "_onAttached");
} }
if (this._childrenCount > 0) { // Notify each child for the _onAttached event
// Notify each child for the _onAttached event this.eachChildView((child) => {
let that = this; child._onAttached(context);
// TODO: This should be done in a call if (!child._isAddedToNativeVisualTree) {
let eachChild = (child: View): boolean => { // since we have lazy loading of the android widgets, we need to add the native instances at this point.
child._onAttached(context); child._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(child);
if (!child._isAddedToNativeVisualTree) {
// since we have lazy loading of the android widgets, we need to add the native instances at this point.
child._isAddedToNativeVisualTree = that._addViewToNativeVisualTree(child);
}
// copy all the locally cached values to the native android widget
return true;
} }
this._eachChildView(eachChild);
}
return true;
});
// copy all the locally cached values to the native android widget
applyNativeSetters(this); applyNativeSetters(this);
} }
@ -173,20 +168,16 @@ export class View extends ViewCommon {
traceWrite(`${this}._onDetached(force)`, traceCategories.VisualTreeEvents); traceWrite(`${this}._onDetached(force)`, traceCategories.VisualTreeEvents);
} }
if (this._childrenCount > 0) { // Detach children first
// Detach children first this.eachChildView((child: View) => {
let that = this; if (child._isAddedToNativeVisualTree) {
let eachChild = function (child: View): boolean { this._removeViewFromNativeVisualTree(child);
if (child._isAddedToNativeVisualTree) {
that._removeViewFromNativeVisualTree(child);
}
if (child._context) {
child._onDetached(force);
}
return true;
} }
this._eachChildView(eachChild); if (child._context) {
} child._onDetached(force);
}
return true;
});
this._clearAndroidReference(); this._clearAndroidReference();
this._context = undefined; this._context = undefined;
@ -215,6 +206,7 @@ export class View extends ViewCommon {
if (traceEnabled) { if (traceEnabled) {
traceWrite(`${this}._onContextChanged`, traceCategories.VisualTreeEvents); traceWrite(`${this}._onContextChanged`, traceCategories.VisualTreeEvents);
} }
this._createUI(); this._createUI();
// Ensure layout params // Ensure layout params
if (this._nativeView && !this._nativeView.getLayoutParams()) { if (this._nativeView && !this._nativeView.getLayoutParams()) {
@ -432,7 +424,7 @@ export class View extends ViewCommon {
return Visibility.HIDDEN; return Visibility.HIDDEN;
case android.view.View.GONE: case android.view.View.GONE:
return Visibility.COLLAPSE; return Visibility.COLLAPSE;
default: default:
throw new Error(`Unsupported android.view.View visibility: ${nativeVisibility}. Currently supported values are android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE.`); throw new Error(`Unsupported android.view.View visibility: ${nativeVisibility}. Currently supported values are android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE.`);
} }
} }
@ -447,7 +439,7 @@ export class View extends ViewCommon {
case Visibility.COLLAPSE: case Visibility.COLLAPSE:
this.nativeView.setVisibility(android.view.View.GONE); this.nativeView.setVisibility(android.view.View.GONE);
break; break;
default: default:
throw new Error(`Invalid visibility value: ${value}. Valid values are: "${Visibility.VISIBLE}", "${Visibility.HIDDEN}", "${Visibility.COLLAPSE}".`); throw new Error(`Invalid visibility value: ${value}. Valid values are: "${Visibility.VISIBLE}", "${Visibility.HIDDEN}", "${Visibility.COLLAPSE}".`);
} }
} }

View File

@ -562,32 +562,27 @@ declare module "ui/core/view" {
_removeFromSuperview(); _removeFromSuperview();
public _applyXmlAttribute(attribute: string, value: any): boolean; public _applyXmlAttribute(attribute: string, value: any): boolean;
public eachChildView(callback: (view: View) => boolean): void;
//@private //@private
/** /**
* A property has changed on the native side directly - e.g. the user types in a TextField. * A property has changed on the native side directly - e.g. the user types in a TextField.
*/ */
public nativePropertyChanged(property: Property<any, any>, newValue: any): void; public nativePropertyChanged(property: Property<any, any>, newValue: any): void;
public bind(options: BindingOptions, source?: any): void;
public unbind(property: string): void;
isCollapsed: boolean;
isLayoutRequired: boolean; isLayoutRequired: boolean;
_gestureObservers: any; _gestureObservers: any;
// _isInheritedChange(): boolean; // _isInheritedChange(): boolean;
_domId: number;
_isAddedToNativeVisualTree: boolean; _isAddedToNativeVisualTree: boolean;
/** /**
* Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise. * Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise.
*/ */
_addViewToNativeVisualTree(view: View, atIndex?: number): boolean; _addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
_removeViewFromNativeVisualTree(view: View): void; _removeViewFromNativeVisualTree(view: ViewBase): void;
_eachChildView(callback: (child: View) => boolean);
_childrenCount: number;
_onAttached(context: any /* android.content.Context */): void; _onAttached(context: any /* android.content.Context */): void;
_onContextChanged(): void; _onContextChanged(): void;

View File

@ -103,7 +103,7 @@ declare module "ui/core/view-base" {
public _removeView(view: ViewBase): void; public _removeView(view: ViewBase): void;
public _parentChanged(oldParent: ViewBase): void; public _parentChanged(oldParent: ViewBase): void;
_childrenCount: number; _domId: number;
_cssState: any /* "ui/styling/style-scope" */; _cssState: any /* "ui/styling/style-scope" */;
_setCssState(next: any /* "ui/styling/style-scope" */); _setCssState(next: any /* "ui/styling/style-scope" */);

View File

@ -396,7 +396,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
return 0; return 0;
} }
public _eachChildView(callback: (child: View) => boolean) { public eachChildView(callback: (child: View) => boolean) {
if (this.currentPage) { if (this.currentPage) {
callback(this.currentPage); callback(this.currentPage);
} }

View File

@ -90,7 +90,7 @@ export class LayoutBaseCommon extends CustomLayoutView implements LayoutBaseDefi
return result; return result;
} }
public _eachChildView(callback: (child: View) => boolean): void { public eachChildView(callback: (child: View) => boolean): void {
for (let i = 0, length = this._subViews.length; i < length; i++) { for (let i = 0, length = this._subViews.length; i < length; i++) {
const retVal = callback(this._subViews[i]); const retVal = callback(this._subViews[i]);
if (retVal === false) { if (retVal === false) {
@ -98,11 +98,11 @@ export class LayoutBaseCommon extends CustomLayoutView implements LayoutBaseDefi
} }
} }
} }
public eachLayoutChild(callback: (child: View, isLast: boolean) => void): void { public eachLayoutChild(callback: (child: View, isLast: boolean) => void): void {
var lastChild: View = null; var lastChild: View = null;
this._eachChildView((cv) => { this.eachChildView((cv) => {
cv._eachLayoutView((lv) => { cv._eachLayoutView((lv) => {
if (lastChild && !lastChild.isCollapsed) { if (lastChild && !lastChild.isCollapsed) {
callback(lastChild, false); callback(lastChild, false);

View File

@ -90,7 +90,7 @@ export class ListView extends ListViewBase {
return this._realizedItems.size; return this._realizedItems.size;
} }
public _eachChildView(callback: (child: View) => boolean): void { public eachChildView(callback: (child: View) => boolean): void {
this._realizedItems.forEach((view, nativeView, map) => { this._realizedItems.forEach((view, nativeView, map) => {
if (view.parent instanceof ListView) { if (view.parent instanceof ListView) {
callback(view); callback(view);

View File

@ -227,7 +227,7 @@ export class ListView extends ListViewBase {
return this._map.size; return this._map.size;
} }
public _eachChildView(callback: (child: View) => boolean): void { public eachChildView(callback: (child: View) => boolean): void {
this._map.forEach((view, key) => { this._map.forEach((view, key) => {
callback(view); callback(view);
}); });

View File

@ -252,8 +252,8 @@ export class PageBase extends ContentView implements PageDefinition {
return this._styleScope; return this._styleScope;
} }
public _eachChildView(callback: (child: View) => boolean) { public eachChildView(callback: (child: View) => boolean) {
super._eachChildView(callback); super.eachChildView(callback);
callback(this.actionBar); callback(this.actionBar);
} }

View File

@ -38,7 +38,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
public _getNativeViewsCount(): number { public _getNativeViewsCount(): number {
let result = 0; let result = 0;
this._eachChildView((cv) => { this.eachChildView((cv) => {
result += cv._getNativeViewsCount(); result += cv._getNativeViewsCount();
return true; return true;
}); });
@ -47,7 +47,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
} }
public _eachLayoutView(callback: (View) => void): void { public _eachLayoutView(callback: (View) => void): void {
this._eachChildView((cv) => { this.eachChildView((cv) => {
if (!cv.isCollapsed) { if (!cv.isCollapsed) {
cv._eachLayoutView(callback); cv._eachLayoutView(callback);
} }
@ -99,7 +99,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
public _addToSuperview(superview: any, atIndex?: number): boolean { public _addToSuperview(superview: any, atIndex?: number): boolean {
let index = 0; let index = 0;
this._eachChildView((cv) => { this.eachChildView((cv) => {
if (!cv._isAddedToNativeVisualTree) { if (!cv._isAddedToNativeVisualTree) {
cv._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(cv, index++); cv._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(cv, index++);
} }
@ -110,7 +110,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
} }
public _removeFromSuperview() { public _removeFromSuperview() {
this._eachChildView((cv) => { this.eachChildView((cv) => {
if (cv._isAddedToNativeVisualTree) { if (cv._isAddedToNativeVisualTree) {
this._removeViewFromNativeVisualTree(cv); this._removeViewFromNativeVisualTree(cv);
} }
@ -147,12 +147,12 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
const oldLayout = <LayoutBase>oldParent; const oldLayout = <LayoutBase>oldParent;
if (addingToParent && newLayout instanceof LayoutBase) { if (addingToParent && newLayout instanceof LayoutBase) {
this._eachChildView((child) => { this.eachChildView((child) => {
newLayout._registerLayoutChild(child); newLayout._registerLayoutChild(child);
return true; return true;
}); });
} else if (oldLayout instanceof LayoutBase) { } else if (oldLayout instanceof LayoutBase) {
this._eachChildView((child) => { this.eachChildView((child) => {
oldLayout._unregisterLayoutChild(child); oldLayout._unregisterLayoutChild(child);
return true; return true;
}); });

View File

@ -92,7 +92,7 @@ export class Repeater extends CustomLayoutView implements RepeaterDefinition {
return count; return count;
} }
public _eachChildView(callback: (child: View) => boolean) { public eachChildView(callback: (child: View) => boolean) {
if (this.itemsLayout) { if (this.itemsLayout) {
callback(this.itemsLayout); callback(this.itemsLayout);
} }

View File

@ -12,7 +12,6 @@ export module knownCollections {
export abstract class SegmentedBarItemBase extends ViewBase implements SegmentedBarItemDefinition { export abstract class SegmentedBarItemBase extends ViewBase implements SegmentedBarItemDefinition {
private _title: string = ""; private _title: string = "";
public _parent: SegmentedBarBase;
get title(): string { get title(): string {
return this._title; return this._title;
@ -64,8 +63,8 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin
} }
// TODO: Make _addView to keep its children so this method is not needed! // TODO: Make _addView to keep its children so this method is not needed!
public _eachChildView(callback: (child: ViewBase) => boolean): void { public eachChild(callback: (child: ViewBase) => boolean): void {
let items = this.items; const items = this.items;
if (items) { if (items) {
items.forEach((item, i) => { items.forEach((item, i) => {
callback(item); callback(item);

View File

@ -9,11 +9,12 @@ export * from "./segmented-bar-common";
export class SegmentedBarItem extends SegmentedBarItemBase { export class SegmentedBarItem extends SegmentedBarItemBase {
public _update() { public _update() {
if (this._parent) { const parent = <SegmentedBar>this.parent;
let tabIndex = this._parent.items.indexOf(this); if (parent) {
let tabIndex = parent.items.indexOf(this);
let title = this.title; let title = this.title;
title = (title === null || title === undefined) ? "" : title; title = (title === null || title === undefined) ? "" : title;
this._parent.ios.setTitleForSegmentAtIndex(title, tabIndex); parent.ios.setTitleForSegmentAtIndex(title, tabIndex);
} }
} }
} }

View File

@ -87,37 +87,6 @@ export class TabViewBase extends View implements TabViewDefinition, AddChildFrom
} }
} }
public _removeTabs(oldItems: Array<TabViewItemDefinition>) {
for (let i = 0, length = oldItems.length; i < length; i++) {
let oldItem = oldItems[i];
if (!oldItem) {
throw new Error("TabViewItem at index " + i + " is undefined.");
}
if (!oldItem.view) {
throw new Error("TabViewItem at index " + i + " does not have a view.");
}
this._removeView(oldItem);
}
}
public _addTabs(newItems: Array<TabViewItemDefinition>) {
// Validate that all items are ok before the native _addTabs code runs.
for (let i = 0, length = newItems.length; i < length; i++) {
let newItem = newItems[i];
if (!newItem) {
throw new Error(`TabViewItem at index ${i} is undefined.`);
}
if (!newItem.view) {
throw new Error(`TabViewItem at index ${i} does not have a view.`);
}
this._addView(newItem);
}
}
get _selectedView(): View { get _selectedView(): View {
let selectedIndex = this.selectedIndex; let selectedIndex = this.selectedIndex;
return selectedIndex > -1 ? this.items[selectedIndex].view : null; return selectedIndex > -1 ? this.items[selectedIndex].view : null;
@ -131,17 +100,21 @@ export class TabViewBase extends View implements TabViewDefinition, AddChildFrom
return 0; return 0;
} }
public _eachChildView(callback: (child: ViewBase) => boolean) { public eachChild(callback: (child: ViewBase) => boolean) {
const items = this.items; const items = this.items;
if (!items) { if (items) {
return; items.forEach((item, i) => {
}
for (let i = 0, length = items.length; i < length; i++) {
let item = items[i];
if (item) {
callback(item); callback(item);
} });
}
}
public eachChildView(callback: (child: View) => boolean) {
const items = this.items;
if (items) {
items.forEach((item, i) => {
callback(item.view);
});
} }
} }

View File

@ -16,11 +16,11 @@ const PRIMARY_COLOR = "colorPrimary";
const DEFAULT_ELEVATION = 4; const DEFAULT_ELEVATION = 4;
export class TabViewItem extends TabViewItemBase { export class TabViewItem extends TabViewItemBase {
public _parent: TabView;
public _update() { public _update() {
if (this._parent) { const parent = <TabView>this.parent;
this._parent._updateTabForItem(this); if (parent) {
parent._updateTabForItem(this);
} }
} }
} }
@ -62,9 +62,9 @@ function ensurePagerAdapterClass() {
} }
let item = this.items[index]; let item = this.items[index];
if (item.view.parent !== this.owner) { // if (item.view.parent !== this.owner) {
this.owner._addView(item.view); // this.owner._addView(item.view);
} // }
if (this[VIEWS_STATES]) { if (this[VIEWS_STATES]) {
if (traceEnabled) { if (traceEnabled) {
@ -92,9 +92,9 @@ function ensurePagerAdapterClass() {
// Note: this.owner._removeView will clear item.view._nativeView. // Note: this.owner._removeView will clear item.view._nativeView.
// So call this after the native instance is removed form the container. // So call this after the native instance is removed form the container.
if (item.view.parent === this.owner) { // if (item.view.parent === this.owner) {
this.owner._removeView(item.view); // this.owner._removeView(item.view);
} // }
} }
isViewFromObject(view: android.view.View, _object: any) { isViewFromObject(view: android.view.View, _object: any) {
@ -122,7 +122,8 @@ function ensurePagerAdapterClass() {
} }
return true; return true;
} }
owner._eachChildView(childCallback);
owner.eachChildView(childCallback);
let bundle = new android.os.Bundle(); let bundle = new android.os.Bundle();
bundle.putSparseParcelableArray(VIEWS_STATES, viewStates); bundle.putSparseParcelableArray(VIEWS_STATES, viewStates);
@ -191,12 +192,12 @@ export class TabView extends TabViewBase {
this.setElevation(); this.setElevation();
let accentColor = ad.resources.getPalleteColor(ACCENT_COLOR, this._context); const accentColor = ad.resources.getPalleteColor(ACCENT_COLOR, this._context);
if (accentColor) { if (accentColor) {
this._tabLayout.setSelectedIndicatorColors([accentColor]); this._tabLayout.setSelectedIndicatorColors([accentColor]);
} }
let primaryColor = ad.resources.getPalleteColor(PRIMARY_COLOR, this._context); const primaryColor = ad.resources.getPalleteColor(PRIMARY_COLOR, this._context);
if (primaryColor) { if (primaryColor) {
this._tabLayout.setBackgroundColor(primaryColor); this._tabLayout.setBackgroundColor(primaryColor);
} }
@ -207,7 +208,7 @@ export class TabView extends TabViewBase {
this._viewPager = new android.support.v4.view.ViewPager(this._context); this._viewPager = new android.support.v4.view.ViewPager(this._context);
this._viewPager.setId(this._androidViewId); this._viewPager.setId(this._androidViewId);
let lp = new org.nativescript.widgets.CommonLayoutParams(); const lp = new org.nativescript.widgets.CommonLayoutParams();
lp.row = 1; lp.row = 1;
this._viewPager.setLayoutParams(lp); this._viewPager.setLayoutParams(lp);
this._grid.addView(this._viewPager); this._grid.addView(this._viewPager);
@ -218,7 +219,7 @@ export class TabView extends TabViewBase {
} }
private setElevation() { private setElevation() {
let compat = <any>android.support.v4.view.ViewCompat; const compat = <any>android.support.v4.view.ViewCompat;
if (compat.setElevation) { if (compat.setElevation) {
let val = DEFAULT_ELEVATION * layout.getDisplayDensity(); let val = DEFAULT_ELEVATION * layout.getDisplayDensity();
compat.setElevation(this._grid, val); compat.setElevation(this._grid, val);
@ -226,48 +227,31 @@ export class TabView extends TabViewBase {
} }
} }
public _onItemsPropertyChangedSetNativeValue() { private setAdapter(items: Array<TabViewItem>) {
throw new Error("Compilation error: Can't find this.previousItems"); const length = items ? items.length : 0;
// let oldItems = <TabViewItem[]>this.previousItems; if (length === 0) {
// if (oldItems) { this._viewPager.setAdapter(null);
// oldItems.forEach((oldItem) => { this._pagerAdapter = null;
// // _removeView is called within destroyItem method this._tabLayout.setItems(null, null);
// oldItem._parent = null; return;
// }); }
// this._viewPager.setAdapter(null); const tabItems = new Array<org.nativescript.widgets.TabItemSpec>();
// this._pagerAdapter = null; items.forEach((item, idx, arr) => {
// this._tabLayout.setItems(null, null); tabItems.push(this.createTabItem(item));
// } });
// let items = <TabViewItem[]>this.items; ensurePagerAdapterClass();
// if (items) { // TODO: optimize by reusing the adapter and calling setAdapter(null) then the same adapter.
// let tabItems = new Array<org.nativescript.widgets.TabItemSpec>(); this._pagerAdapter = new PagerAdapterClass(this, items);
// items.forEach((item, idx, arr) => { this._viewPager.setAdapter(this._pagerAdapter);
// if (!item.view) {
// throw new Error("View of TabViewItem at index " + idx + " is " + item.view);
// }
// item._parent = this; this._tabLayout.setItems(tabItems, this._viewPager);
// if (item.view.parent !== this) {
// this._addView(item.view, idx);
// }
// tabItems.push(this.createTabItem(item));
// });
// ensurePagerAdapterClass(); let selectedIndex = this.selectedIndex;
// // TODO: optimize by reusing the adapter and calling setAdapter(null) then the same adapter. if (selectedIndex < 0) {
// this._pagerAdapter = new PagerAdapterClass(this, items); this.selectedIndex = this._viewPager.getCurrentItem();
// this._viewPager.setAdapter(this._pagerAdapter); }
// this._tabLayout.setItems(tabItems, this._viewPager);
// }
// let nativeSelectedIndex = this._viewPager.getCurrentItem();
// let selectedIndex = this.selectedIndex;
// if (selectedIndex < 0) {
// this.selectedIndex = nativeSelectedIndex;
// }
} }
public _updateTabForItem(item: TabViewItem) { public _updateTabForItem(item: TabViewItem) {
@ -320,7 +304,7 @@ export class TabView extends TabViewBase {
return null; return null;
} }
set [itemsProperty.native](value: TabViewItemBase[]) { set [itemsProperty.native](value: TabViewItemBase[]) {
this._onItemsPropertyChangedSetNativeValue(); this.setAdapter(value);
} }
get [tabTextColorProperty.native](): number { get [tabTextColorProperty.native](): number {

View File

@ -121,23 +121,24 @@ function updateItemTitlePosition(tabBarItem: UITabBarItem): void {
} }
export class TabViewItem extends TabViewItemBase { export class TabViewItem extends TabViewItemBase {
public _controller: UIViewController;
public _parent: TabView;
public _update() { public _update() {
if (this._parent && this._controller) { const parent = <TabView>this.parent;
const icon = this._parent._getIcon(this.iconSource); let controller = <UIViewController>this.nativeView;
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((this.title), icon, this._parent.items.indexOf(this)); if (parent && controller) {
const icon = parent._getIcon(this.iconSource);
const index = parent.items.indexOf(this);
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(this.title, icon, index);
if (!icon) { if (!icon) {
updateItemTitlePosition(tabBarItem); updateItemTitlePosition(tabBarItem);
} }
// TODO: Repeating code. Make TabViewItemBase - ViewBase and move the colorProperty on tabViewItem. // TODO: Repeating code. Make TabViewItemBase - ViewBase and move the colorProperty on tabViewItem.
// Delete the repeating code. // Delete the repeating code.
const states = getTitleAttributesForStates(this._parent); const states = getTitleAttributesForStates(parent);
tabBarItem.setTitleTextAttributesForState(states.normalState, UIControlState.Normal); tabBarItem.setTitleTextAttributesForState(states.normalState, UIControlState.Normal);
tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected); tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected);
this._controller.tabBarItem = tabBarItem; controller.tabBarItem = tabBarItem;
} }
} }
} }
@ -226,47 +227,30 @@ export class TabView extends TabViewBase {
} }
} }
public _removeTabs(oldItems: Array<TabViewItem>) { private setViewControllers(items: Array<TabViewItem>) {
if (traceEnabled) { const length = items ? items.length : 0;
traceWrite("TabView._removeTabs(" + oldItems + ");", traceCategories.Debug); if (length === 0) {
} this._ios.viewControllers = null;
super._removeTabs(oldItems); return;
for (let i = 0, length = oldItems.length; i < length; i++) {
let oldItem = oldItems[i];
oldItem._parent = null;
oldItem._controller = null;
} }
this._ios.viewControllers = null; const controllers = NSMutableArray.alloc<UIViewController>().initWithCapacity(length);
}
public _addTabs(newItems: Array<TabViewItem>) {
if (traceEnabled) {
traceWrite("TabView._addTabs(" + newItems + ");", traceCategories.Debug);
}
super._addTabs(newItems);
const length = newItems.length;
const newControllers: NSMutableArray<any> = NSMutableArray.alloc().initWithCapacity(length);
const states = getTitleAttributesForStates(this); const states = getTitleAttributesForStates(this);
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const item = <TabViewItem>newItems[i]; const item = items[i];
let newController: UIViewController; let newController: UIViewController;
if (item.view.ios instanceof UIViewController) { if (item.view.ios instanceof UIViewController) {
newController = <UIViewController>item.view.ios; newController = item.view.ios;
} else { } else {
newController = UIViewController.new(); newController = UIViewController.new();
newController.view.addSubview(item.view.ios); newController.view.addSubview(item.view.ios);
} }
item._parent = this; item.nativeView = newController;
item._controller = newController;
const icon = this._getIcon(item.iconSource); const icon = this._getIcon(item.iconSource);
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i); const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag((item.title || ""), icon, i);
if (!icon) { if (!icon) {
updateItemTitlePosition(tabBarItem); updateItemTitlePosition(tabBarItem);
@ -275,17 +259,17 @@ export class TabView extends TabViewBase {
tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected); tabBarItem.setTitleTextAttributesForState(states.selectedState, UIControlState.Selected);
newController.tabBarItem = tabBarItem; newController.tabBarItem = tabBarItem;
newControllers.addObject(newController); controllers.addObject(newController);
} }
this._ios.viewControllers = newControllers; this._ios.viewControllers = controllers;
this._ios.customizableViewControllers = null; this._ios.customizableViewControllers = null;
// When we set this._ios.viewControllers, someone is clearing the moreNavigationController.delegate, so we have to reassign it each time here. // When we set this._ios.viewControllers, someone is clearing the moreNavigationController.delegate, so we have to reassign it each time here.
this._ios.moreNavigationController.delegate = this._moreNavigationControllerDelegate; this._ios.moreNavigationController.delegate = this._moreNavigationControllerDelegate;
if (this._ios.selectedIndex !== this.selectedIndex) { if (this.selectedIndex < 0) {
this._ios.selectedIndex = this.selectedIndex; this.selectedIndex = this._ios.selectedIndex;
} }
} }
@ -399,19 +383,6 @@ export class TabView extends TabViewBase {
} }
} }
private _onItemsPropertyChangedSetNativeValue() {
throw new Error("Compilation error: Can't find this.previousItems");
// let oldValue = <TabViewItem[]>this.previousItems;
// if (oldValue) {
// this._removeTabs(oldValue);
// }
// let newValue = <TabViewItem[]>this.items;
// if (newValue) {
// this._addTabs(newValue);
// }
}
get [selectedIndexProperty.native](): number { get [selectedIndexProperty.native](): number {
return -1; return -1;
} }
@ -429,7 +400,7 @@ export class TabView extends TabViewBase {
return null; return null;
} }
set [itemsProperty.native](value: TabViewItemBase[]) { set [itemsProperty.native](value: TabViewItemBase[]) {
this._onItemsPropertyChangedSetNativeValue(); this.setViewControllers(value);
} }
get [tabTextColorProperty.native](): UIColor { get [tabTextColorProperty.native](): UIColor {
@ -479,7 +450,7 @@ export class TabView extends TabViewBase {
let items = this.items; let items = this.items;
if (items && items.length) { if (items && items.length) {
for (let i = 0, length = items.length; i < length; i++) { for (let i = 0, length = items.length; i < length; i++) {
const item = items[i]; const item = items[i];
if (items[i].iconSource) { if (items[i].iconSource) {
(<TabViewItem>items[i])._update(); (<TabViewItem>items[i])._update();
} }
@ -495,7 +466,7 @@ function getTitleAttributesForStates(tabView: TabView): { normalState: any, sele
} }
const selectedState = {}; const selectedState = {};
let tabItemTextColor = tabView.style.tabTextColor; let tabItemTextColor = tabView.style.tabTextColor;
if (tabItemTextColor instanceof Color) { if (tabItemTextColor instanceof Color) {
selectedState[UITextAttributeTextColor] = tabItemTextColor.ios; selectedState[UITextAttributeTextColor] = tabItemTextColor.ios;
} }