attach/detach in ViewBase

This commit is contained in:
vakrilov
2017-01-03 15:13:27 +02:00
parent f76eb15e05
commit 0b67f6bc0a
7 changed files with 175 additions and 172 deletions

View File

@@ -2,6 +2,7 @@
import * as TKUnit from "../../TKUnit";
import { ActionItem } from "ui/action-bar";
import { Visibility } from "ui/enums";
import { Button } from "ui/button";
global.moduleMerge(actionTestsCommon, exports);
@@ -29,4 +30,46 @@ export function test_navigationButton_visibility() {
TKUnit.assertNotNull(toolbar.getNavigationIcon(), "Visibility does not work");
actionItem.visibility = Visibility.collapse;
TKUnit.assertNull(toolbar.getNavigationIcon(), "Visibility does not work");
}
}
export function test_set_actionView_to_attached_actionItem_propagates_context() {
const actionItem = new ActionItem();
const actionButton = new Button();
actionItem.actionView = actionButton;
const page = actionTestsCommon.createPageAndNavigate();
// sanity check
TKUnit.assertNotNull(page.content._context, "Page content context should not be null");
// assert null before add
TKUnit.assertNull(actionItem._context, "Action Item context should be null before added");
TKUnit.assertNull(actionButton._context, "Action button context should not null before added");
page.actionBar.actionItems.addItem(actionItem);
// assert not null after add
TKUnit.assertNotNull(actionItem._context, "Action Item context should not be null after add");
TKUnit.assertNotNull(actionButton._context, "Action button context should not be null after add");
}
export function test_add_actionItem_with_actionView_propagates_context() {
const actionItem = new ActionItem();
const page = actionTestsCommon.createPageAndNavigate();
// sanity check
TKUnit.assertNotNull(page.content._context, "Page content context should not be null");
// add actionItem to the actionBar
TKUnit.assertNull(actionItem._context, "Action Item context should be null before added");
page.actionBar.actionItems.addItem(actionItem);
TKUnit.assertNotNull(actionItem._context, "Action Item context should not be null after add");
const actionButton = new Button();
// add actionButton to the actionItem
TKUnit.assertNull(actionButton._context, "Action button context should be null before added");
actionItem.actionView = actionButton;
TKUnit.assertNotNull(actionButton._context, "Action button context should not be null after add");
}

View File

@@ -1,6 +1,6 @@
import { ViewBase as ViewBaseDefinition } from "ui/core/view-base";
import { Observable, EventData } from "data/observable";
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritedProperties, resetCSSProperties } from "./properties";
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritedProperties, resetCSSProperties, applyNativeSetters } from "./properties";
import { Binding, BindingOptions, Bindable } from "ui/core/bindable";
import { isIOS, isAndroid } from "platform";
import { fromString as gestureFromString } from "ui/gestures";
@@ -109,6 +109,8 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public className: string;
public _domId: number;
public _context: any;
public _isAddedToNativeVisualTree: any;
public _cssState: CssState;
constructor() {
@@ -154,12 +156,12 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public onLoaded() {
this._isLoaded = true;
this._loadEachChildView();
this._loadEachChild();
this._applyStyleFromScope();
this._emit("loaded");
}
public _loadEachChildView() {
public _loadEachChild() {
this.eachChild((child) => {
child.onLoaded();
return true;
@@ -168,12 +170,12 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public onUnloaded() {
this._setCssState(null);
this._unloadEachChildView();
this._unloadEachChild();
this._isLoaded = false;
this._emit("unloaded");
}
private _unloadEachChildView() {
private _unloadEachChild() {
this.eachChild((child) => {
if (child.isLoaded) {
child.onUnloaded();
@@ -405,6 +407,15 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
}
protected _addViewCore(view: ViewBase, atIndex?: number) {
if (this._context) {
view._onAttached(this._context);
}
if (!view._isAddedToNativeVisualTree) {
let nativeIndex = this._childIndexToNativeChildIndex(atIndex);
view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex);
}
// TODO: Discuss this.
if (this._isLoaded) {
view.onLoaded();
@@ -420,6 +431,7 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
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);
}
@@ -439,6 +451,102 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
}
// view.unsetInheritedProperties();
// Remove the view from the native visual scene first
this._removeViewFromNativeVisualTree(view);
if (view._context) {
view._onDetached();
}
}
public _onAttached(context: any) {
if (!context) {
throw new Error("Expected valid android.content.Context instance.");
}
if (traceEnabled) {
traceWrite(`${this}._onAttached(context)`, traceCategories.VisualTreeEvents);
}
if (this._context === context) {
return;
}
if (this._context) {
this._onDetached(true);
}
this._context = context;
this._onContextChanged();
if (traceEnabled) {
traceNotifyEvent(this, "_onAttached");
}
// Notify each child for the _onAttached event
this.eachChild((child) => {
child._onAttached(context);
if (!child._isAddedToNativeVisualTree) {
// since we have lazy loading of the android widgets, we need to add the native instances at this point.
child._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(child);
}
return true;
});
// copy all the locally cached values to the native android widget
applyNativeSetters(this);
}
public _onDetached(force?: boolean) {
if (traceEnabled) {
traceWrite(`${this}._onDetached(force)`, traceCategories.VisualTreeEvents);
}
// Detach children first
this.eachChild((child: ViewBase) => {
if (child._isAddedToNativeVisualTree) {
this._removeViewFromNativeVisualTree(child);
}
if (child._context) {
child._onDetached(force);
}
return true;
});
this._context = undefined;
if (traceEnabled) {
traceNotifyEvent(this, "_onDetached");
}
}
public _onContextChanged(): void {
//
}
_childIndexToNativeChildIndex(index?: number): number {
return index;
}
/**
* Method is intended to be overridden by inheritors and used as "protected".
*/
public _addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean {
if (view._isAddedToNativeVisualTree) {
throw new Error("Child already added to the native visual tree.");
}
return true;
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewFromNativeVisualTree(view: ViewBase) {
view._isAddedToNativeVisualTree = false;
}
public _goToVisualState(state: string) {

View File

@@ -77,7 +77,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
private _isLayoutValid: boolean;
private _cssType: string;
public _isAddedToNativeVisualTree: boolean;
public _gestureObservers = {};
// public parent: ViewCommon;
@@ -754,31 +753,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return { boundsChanged, sizeChanged };
}
// 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;
public _onAttached(context: android.content.Context) {
//
}
public _onDetached(force?: boolean) {
//
}
public _createUI() {
//
}
public _onContextChanged() {
//
}
//@endandroid
// TODO: We need to implement some kind of build step that includes these members only when building for iOS
//@endios
public eachChild(callback: (child: ViewBase) => boolean): void {
this.eachChildView(<any>callback);
}
@@ -786,10 +764,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public eachChildView(callback: (view: ViewDefinition) => boolean) {
//
}
_childIndexToNativeChildIndex(index?: number): number {
return index;
}
_getNativeViewsCount(): number {
return this._isAddedToNativeVisualTree ? 1 : 0;
@@ -807,33 +781,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// IOS specific
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _addViewCore(view: ViewBase, atIndex?: number) {
if (view instanceof ViewCommon) {
if (!view._isAddedToNativeVisualTree) {
let nativeIndex = this._childIndexToNativeChildIndex(atIndex);
view._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(view, nativeIndex);
}
}
super._addViewCore(view, atIndex);
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewBase) {
if (view instanceof 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);
}
super._removeViewCore(view);
}
// public unsetInheritedProperties(): void {
// // this._setValue(ProxyObject.bindingContextProperty, undefined, ValueSource.Inherited);
// // this._eachSetProperty((property) => {
@@ -843,24 +790,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
// // return true;
// // });
// }
/**
* Method is intended to be overridden by inheritors and used as "protected".
*/
public _addViewToNativeVisualTree(view: ViewDefinition, atIndex?: number): boolean {
if (view._isAddedToNativeVisualTree) {
throw new Error("Child already added to the native visual tree.");
}
return true;
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewFromNativeVisualTree(view: ViewDefinition) {
view._isAddedToNativeVisualTree = false;
}
public _updateLayout() {
// needed for iOS.

View File

@@ -6,7 +6,7 @@ import {
marginRightProperty, marginBottomProperty, horizontalAlignmentProperty, verticalAlignmentProperty,
rotateProperty, scaleXProperty, scaleYProperty,
translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty,
Background, GestureTypes, GestureEventData, applyNativeSetters,
Background, GestureTypes, GestureEventData,
traceEnabled, traceWrite, traceCategories, traceNotifyEvent, Visibility, HorizontalAlignment, VerticalAlignment
} from "./view-common";
@@ -105,83 +105,11 @@ export class View extends ViewCommon {
}
}
public _addViewCore(view: ViewCommon, atIndex?: number) {
if (this._context) {
if (view._onAttached) {
view._onAttached(this._context);
}
}
super._addViewCore(view, atIndex);
}
public _removeViewCore(view: ViewCommon) {
super._removeViewCore(view);
if (view._context) {
view._onDetached();
}
}
public _onAttached(context: android.content.Context) {
if (!context) {
throw new Error("Expected valid android.content.Context instance.");
}
if (traceEnabled) {
traceWrite(`${this}._onAttached(context)`, traceCategories.VisualTreeEvents);
}
if (this._context === context) {
return;
}
if (this._context) {
this._onDetached(true);
}
this._context = context;
this._onContextChanged();
if (traceEnabled) {
traceNotifyEvent(this, "_onAttached");
}
// Notify each child for the _onAttached event
this.eachChildView((child) => {
child._onAttached(context);
if (!child._isAddedToNativeVisualTree) {
// since we have lazy loading of the android widgets, we need to add the native instances at this point.
child._isAddedToNativeVisualTree = this._addViewToNativeVisualTree(child);
}
return true;
});
// copy all the locally cached values to the native android widget
applyNativeSetters(this);
}
public _onDetached(force?: boolean) {
if (traceEnabled) {
traceWrite(`${this}._onDetached(force)`, traceCategories.VisualTreeEvents);
}
// Detach children first
this.eachChildView((child: View) => {
if (child._isAddedToNativeVisualTree) {
this._removeViewFromNativeVisualTree(child);
}
if (child._context) {
child._onDetached(force);
}
return true;
});
// Call super for recursive detach of all children.
super._onDetached(force);
this._clearAndroidReference();
this._context = undefined;
if (traceEnabled) {
traceNotifyEvent(this, "_onDetached");
}
}
// TODO: revise this method

View File

@@ -526,10 +526,6 @@ declare module "ui/core/view" {
public getActualSize(): Size;
// Lifecycle events
_context: any /* android.content.Context */;
_childIndexToNativeChildIndex(index?: number): number;
_getNativeViewsCount(): number;
_eachLayoutView(callback: (View) => void): void;
@@ -550,17 +546,6 @@ declare module "ui/core/view" {
_gestureObservers: any;
// _isInheritedChange(): 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.
*/
_addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
_removeViewFromNativeVisualTree(view: ViewBase): void;
_onAttached(context: any /* android.content.Context */): void;
_onContextChanged(): void;
_onDetached(force?: boolean): void;
_createUI(): void;
_updateLayout(): void;

View File

@@ -26,10 +26,6 @@ export class View extends ViewCommon {
public _removeViewCore(view: ViewCommon) {
super._removeViewCore(view);
// TODO: Detach from the context?
if (view._onDetached) {
view._onDetached();
}
this.requestLayout();
}
@@ -355,7 +351,7 @@ export class View extends ViewCommon {
case Visibility.COLLAPSE:
this.nativeView.hidden = true;
break;
default:
default:
throw new Error(`Invalid visibility value: ${value}. Valid values are: "${Visibility.VISIBLE}", "${Visibility.HIDDEN}", "${Visibility.COLLAPSE}".`);
}
}

View File

@@ -111,6 +111,20 @@ declare module "ui/core/view-base" {
_unregisterAnimation(animation: KeyframeAnimation);
_cancelAllAnimations();
_context: any /* android.content.Context */;
_onAttached(context: any /* android.content.Context */): void;
_onContextChanged(): void;
_onDetached(force?: boolean): void;
_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.
*/
_addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
_removeViewFromNativeVisualTree(view: ViewBase): void;
_childIndexToNativeChildIndex(index?: number): number;
/**
* @protected
* @unstable