Flexbox ios initial commit

Items now appear, but they should initially shrink and they don't

Add 15-ish tests for flexbox layout

Port reasonable flexbox test set

Fixing issues, adding unit tests

Moved from .tsx to .ts and used our ui/builder.parse
This commit is contained in:
Panayot Cankov
2016-10-05 13:58:33 +03:00
parent 4b94fe8c63
commit 791aab04e5
16 changed files with 3457 additions and 36 deletions

View File

@ -62,6 +62,7 @@ allTests["WRAPLAYOUT"] = require("./ui/layouts/wrap-layout-tests");
allTests["ABSOLUTELAYOUT"] = require("./ui/layouts/absolute-layout-tests"); allTests["ABSOLUTELAYOUT"] = require("./ui/layouts/absolute-layout-tests");
allTests["GRIDLAYOUT"] = require("./ui/layouts/grid-layout-tests"); allTests["GRIDLAYOUT"] = require("./ui/layouts/grid-layout-tests");
allTests["STACKLAYOUT"] = require("./ui/layouts/stack-layout-tests"); allTests["STACKLAYOUT"] = require("./ui/layouts/stack-layout-tests");
allTests["FLEXBOXLAYOUT"] = require("./ui/layouts/flexbox-layout-tests");
allTests["STYLE-PROPERTIES"] = require("./ui/styling/style-properties-tests"); allTests["STYLE-PROPERTIES"] = require("./ui/styling/style-properties-tests");
allTests["FRAME"] = require("./ui/frame/frame-tests"); allTests["FRAME"] = require("./ui/frame/frame-tests");
allTests["VIEW"] = require("./ui/view/view-tests"); allTests["VIEW"] = require("./ui/view/view-tests");

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,9 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"diagnostics": true, "diagnostics": true,
"sourceMap": true, "sourceMap": true,
"noLib": true "noLib": true,
"jsx": "react",
"reactNamespace": "UIBuilder"
}, },
"exclude": [ "exclude": [
"node_modules", "node_modules",

View File

@ -727,6 +727,12 @@ export class View extends ProxyObject implements definition.View {
return this._measuredHeight & utils.layout.MEASURED_SIZE_MASK; return this._measuredHeight & utils.layout.MEASURED_SIZE_MASK;
} }
public getMeasuredState(): number {
return (this._measuredWidth & utils.layout.MEASURED_STATE_MASK)
| ((this._measuredHeight >> utils.layout.MEASURED_HEIGHT_STATE_SHIFT)
& (utils.layout.MEASURED_STATE_MASK >> utils.layout.MEASURED_HEIGHT_STATE_SHIFT));
}
public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void { public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void {
this._measuredWidth = measuredWidth; this._measuredWidth = measuredWidth;
this._measuredHeight = measuredHeight; this._measuredHeight = measuredHeight;
@ -786,6 +792,10 @@ export class View extends ProxyObject implements definition.View {
return Math.round(result + 0.499) | (childMeasuredState & utils.layout.MEASURED_STATE_MASK); return Math.round(result + 0.499) | (childMeasuredState & utils.layout.MEASURED_STATE_MASK);
} }
public static combineMeasuredStates(curState: number, newState): number {
return curState | newState;
}
public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void { public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void {
if (!child || !child._isVisible) { if (!child || !child._isVisible) {
return; return;

View File

@ -12,6 +12,8 @@ import background = require("ui/styling/background");
import {CommonLayoutParams} from "ui/styling/style"; import {CommonLayoutParams} from "ui/styling/style";
import {device} from "platform"; import {device} from "platform";
var flexbox;
global.moduleMerge(viewCommon, exports); global.moduleMerge(viewCommon, exports);
var ANDROID = "_android"; var ANDROID = "_android";
@ -607,6 +609,13 @@ export class ViewStyler implements style.Styler {
lp.rightMargin = Math.round(params.rightMargin * utils.layout.getDisplayDensity()); lp.rightMargin = Math.round(params.rightMargin * utils.layout.getDisplayDensity());
lp.bottomMargin = Math.round(params.bottomMargin * utils.layout.getDisplayDensity()); lp.bottomMargin = Math.round(params.bottomMargin * utils.layout.getDisplayDensity());
lp.gravity = gravity; lp.gravity = gravity;
if (lp instanceof org.nativescript.widgets.FlexboxLayout.LayoutParams) {
if (!flexbox) {
flexbox = require("ui/layouts/flexbox-layout");
}
flexbox._setAndroidLayoutParams(lp, view);
}
} }
else { else {
let layoutParams: any = lp; let layoutParams: any = lp;

View File

@ -394,6 +394,8 @@ declare module "ui/core/view" {
*/ */
public getMeasuredHeight(): number; public getMeasuredHeight(): number;
public getMeasuredState(): number;
/** /**
* Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree. * Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree.
*/ */
@ -462,6 +464,8 @@ declare module "ui/core/view" {
*/ */
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number; public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number;
public static combineMeasuredStates(curState: number, newState): number;
/** /**
* Returns the child view with the specified id. * Returns the child view with the specified id.
*/ */

View File

@ -7,6 +7,10 @@ import * as platform from "platform";
export type Basis = "auto" | number; export type Basis = "auto" | number;
const ORDER_DEFAULT = 1;
const FLEX_GROW_DEFAULT = 0.0;
const FLEX_SHRINK_DEFAULT = 1.0;
// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call). // on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout; var AffectsLayout = platform.device.os === platform.platformNames.android ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout;
@ -63,6 +67,11 @@ let validJustifyContent = {
"space-around": true "space-around": true
} }
export type FlexBasisPercent = number;
export namespace FlexBasisPercent {
export const DEFAULT: number = -1;
}
function validateJustifyContent(value: any): boolean { function validateJustifyContent(value: any): boolean {
return value in validJustifyContent; return value in validJustifyContent;
} }
@ -140,10 +149,11 @@ export abstract class FlexboxLayoutBase extends LayoutBase {
public static alignContentProperty = new Property("alignContent", "FlexboxLayout", new PropertyMetadata("stretch", AffectsLayout, undefined, validateAlignContent, (args: any) => args.object.setNativeAlignContent(args.newValue))); public static alignContentProperty = new Property("alignContent", "FlexboxLayout", new PropertyMetadata("stretch", AffectsLayout, undefined, validateAlignContent, (args: any) => args.object.setNativeAlignContent(args.newValue)));
// TODO: Validation: // TODO: Validation:
public static orderProperty = new Property("order", "FlexboxLayout", new PropertyMetadata(1, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onOrderPropertyChanged(element, oldValue, newValue)))); public static orderProperty = new Property("order", "FlexboxLayout", new PropertyMetadata(ORDER_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onOrderPropertyChanged(element, oldValue, newValue))));
public static flexGrowProperty = new Property("flexGrow", "FlexboxLayout", new PropertyMetadata(0, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onFlexGrowPropertyChanged(element, oldValue, newValue)))); public static flexGrowProperty = new Property("flexGrow", "FlexboxLayout", new PropertyMetadata(FLEX_GROW_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onFlexGrowPropertyChanged(element, oldValue, newValue))));
public static flexShrinkProperty = new Property("flexShrink", "FlexboxLayout", new PropertyMetadata(1, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onFlexShrinkPropertyChanged(element, oldValue, newValue)))); public static flexShrinkProperty = new Property("flexShrink", "FlexboxLayout", new PropertyMetadata(FLEX_SHRINK_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<number>((flexbox, element, oldValue, newValue) => flexbox.onFlexShrinkPropertyChanged(element, oldValue, newValue))));
public static alignSelfProperty = new Property("alignSelf", "FlexboxLayout", new PropertyMetadata(-1, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<AlignSelf>((flexbox, element, oldValue, newValue) => flexbox.onAlignSelfPropertyChanged(element, oldValue, newValue)))); public static flexWrapBeforeProperty = new Property("flexWrapBefore", "FlexboxLayout", new PropertyMetadata(false, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<boolean>((flexbox, element, oldValue, newValue) => flexbox.onFlexWrapBeforePropertyChanged(element, oldValue, newValue))))
public static alignSelfProperty = new Property("alignSelf", "FlexboxLayout", new PropertyMetadata(AlignSelf.AUTO, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<AlignSelf>((flexbox, element, oldValue, newValue) => flexbox.onAlignSelfPropertyChanged(element, oldValue, newValue))));
constructor() { constructor() {
super(); super();
@ -177,10 +187,10 @@ export abstract class FlexboxLayoutBase extends LayoutBase {
this._setValue(FlexboxLayoutBase.alignItemsProperty, value); this._setValue(FlexboxLayoutBase.alignItemsProperty, value);
} }
get alignContent(): AlignItems { get alignContent(): AlignContent {
return this._getValue(FlexboxLayoutBase.alignContentProperty); return this._getValue(FlexboxLayoutBase.alignContentProperty);
} }
set alignContent(value: AlignItems) { set alignContent(value: AlignContent) {
this._setValue(FlexboxLayoutBase.alignContentProperty, value); this._setValue(FlexboxLayoutBase.alignContentProperty, value);
} }
@ -212,8 +222,11 @@ export abstract class FlexboxLayoutBase extends LayoutBase {
return validateArgs(view)._getValue(FlexboxLayoutBase.alignSelfProperty); return validateArgs(view)._getValue(FlexboxLayoutBase.alignSelfProperty);
} }
protected onOrderPropertyChanged(element: View, oldValue: number, newValue: number): void { public static setFlexWrapBefore(view: View, wrap: boolean) {
console.log("order changed: " + newValue + " " + element); view._setValue(FlexboxLayoutBase.flexWrapBeforeProperty, wrap);
}
public static getFlexWrapBefore(view: View): boolean {
return view._getValue(FlexboxLayoutBase.flexWrapBeforeProperty);
} }
protected abstract setNativeFlexDirection(flexDirection: FlexDirection); protected abstract setNativeFlexDirection(flexDirection: FlexDirection);
@ -222,17 +235,11 @@ export abstract class FlexboxLayoutBase extends LayoutBase {
protected abstract setNativeAlignItems(alignItems: AlignItems); protected abstract setNativeAlignItems(alignItems: AlignItems);
protected abstract setNativeAlignContent(alignContent: AlignContent); protected abstract setNativeAlignContent(alignContent: AlignContent);
protected onFlexGrowPropertyChanged(element: View, oldValue: number, newValue: number): void { protected abstract onOrderPropertyChanged(element: View, oldValue: number, newValue: number): void;
console.log("flex-grow changed: " + newValue + " " + element); protected abstract onFlexGrowPropertyChanged(element: View, oldValue: number, newValue: number): void;
} protected abstract onFlexShrinkPropertyChanged(element: View, oldValue: number, newValue: number): void;
protected abstract onAlignSelfPropertyChanged(element: View, oldValue: AlignSelf, newValue: AlignSelf): void;
protected onFlexShrinkPropertyChanged(element: View, oldValue: number, newValue: number): void { protected abstract onFlexWrapBeforePropertyChanged(element: View, oldValue: boolean, newValue: boolean): void;
console.log("flex-shrink changed: " + newValue + " " + element);
}
protected onAlignSelfPropertyChanged(element: View, oldValue: AlignSelf, newValue: AlignSelf): void {
console.log("align-self changed: " + newValue + " " + element);
}
private static childHandler<V>(handler: (flexbox: FlexboxLayoutBase, element: View, oldValue: V, newValue: V) => void) { private static childHandler<V>(handler: (flexbox: FlexboxLayoutBase, element: View, oldValue: V, newValue: V) => void) {
return (data: PropertyChangeData) => { return (data: PropertyChangeData) => {
@ -260,4 +267,7 @@ registerSpecialProperty("flexShrink", (instance, propertyValue) => {
registerSpecialProperty("alignSelf", (instance, propertyValue) => { registerSpecialProperty("alignSelf", (instance, propertyValue) => {
FlexboxLayoutBase.setAlignSelf(instance, propertyValue); FlexboxLayoutBase.setAlignSelf(instance, propertyValue);
}); });
registerSpecialProperty("flexWrapBefore", (instance, propertyValue) => {
FlexboxLayoutBase.setFlexWrapBefore(instance, propertyValue);
});
// No flex-basis in our implementation. // No flex-basis in our implementation.

View File

@ -8,6 +8,7 @@ import {
AlignSelf, AlignSelf,
FlexboxLayoutBase FlexboxLayoutBase
} from "./flexbox-layout-common"; } from "./flexbox-layout-common";
import {layout} from "utils/utils";
export * from "./flexbox-layout-common"; export * from "./flexbox-layout-common";
@ -65,7 +66,6 @@ export class FlexboxLayout extends FlexboxLayoutBase {
constructor() { constructor() {
super(); super();
console.log("New FlexBoxLayout!");
} }
get android(): FlexboxLayoutWidget { return this._layout; } get android(): FlexboxLayoutWidget { return this._layout; }
@ -77,56 +77,63 @@ export class FlexboxLayout extends FlexboxLayoutBase {
protected setNativeFlexDirection(flexDirection: FlexDirection) { protected setNativeFlexDirection(flexDirection: FlexDirection) {
let value = flexDirectionMap[flexDirection]; let value = flexDirectionMap[flexDirection];
console.log("setNativeFlexDirection: " + flexDirection + " -> " + value);
this.android.setFlexDirection(value); this.android.setFlexDirection(value);
} }
protected setNativeFlexWrap(flexWrap: FlexWrap) { protected setNativeFlexWrap(flexWrap: FlexWrap) {
console.log("flexWrap: " + flexWrap);
this.android.setFlexWrap(flexWrapMap[flexWrap]); this.android.setFlexWrap(flexWrapMap[flexWrap]);
} }
protected setNativeJustifyContent(justifyContent: JustifyContent) { protected setNativeJustifyContent(justifyContent: JustifyContent) {
console.log("setNativeJustifyContent: " + justifyContent);
this.android.setJustifyContent(justifyContentMap[justifyContent]); this.android.setJustifyContent(justifyContentMap[justifyContent]);
} }
protected setNativeAlignItems(alignItems: AlignItems) { protected setNativeAlignItems(alignItems: AlignItems) {
console.log("setNativeAlignItems: " + alignItems);
this.android.setAlignItems(alignItemsMap[alignItems]); this.android.setAlignItems(alignItemsMap[alignItems]);
} }
protected setNativeAlignContent(alignContent: AlignContent) { protected setNativeAlignContent(alignContent: AlignContent) {
console.log("setNativeAlignContent: " + alignContent);
this.android.setAlignContent(alignContentMap[alignContent]); this.android.setAlignContent(alignContentMap[alignContent]);
} }
protected onOrderPropertyChanged(view: View, oldValue: number, newValue: number): void { protected onOrderPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("order changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.order = newValue); this.setLayoutParamsProperty(view, lp => lp.order = newValue);
} }
protected onFlexGrowPropertyChanged(view: View, oldValue: number, newValue: number): void { protected onFlexGrowPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("flex-grow changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.flexGrow = newValue); this.setLayoutParamsProperty(view, lp => lp.flexGrow = newValue);
} }
protected onFlexShrinkPropertyChanged(view: View, oldValue: number, newValue: number): void { protected onFlexShrinkPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("flex-shrink changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.flexShrink = newValue); this.setLayoutParamsProperty(view, lp => lp.flexShrink = newValue);
} }
protected onAlignSelfPropertyChanged(view: View, oldValue: AlignSelf, newValue: AlignSelf): void { protected onAlignSelfPropertyChanged(view: View, oldValue: AlignSelf, newValue: AlignSelf): void {
console.log("align-self changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.alignSelf = alignSelfMap[newValue]); this.setLayoutParamsProperty(view, lp => lp.alignSelf = alignSelfMap[newValue]);
} }
protected onFlexWrapBeforePropertyChanged(view: View, oldValue: boolean, newValue: boolean): void {
this.setLayoutParamsProperty(view, lp => lp.wrapBefore = newValue);
}
private setLayoutParamsProperty(view: View, setter: (lp: org.nativescript.widgets.FlexboxLayout.LayoutParams) => void) { private setLayoutParamsProperty(view: View, setter: (lp: org.nativescript.widgets.FlexboxLayout.LayoutParams) => void) {
let nativeView: android.view.View = view._nativeView; let nativeView: android.view.View = view._nativeView;
var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.FlexboxLayout.LayoutParams(); if (nativeView) {
if (lp instanceof org.nativescript.widgets.FlexboxLayout.LayoutParams) { var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.FlexboxLayout.LayoutParams();
setter(lp); if (lp instanceof org.nativescript.widgets.FlexboxLayout.LayoutParams) {
nativeView.setLayoutParams(lp); setter(lp);
nativeView.setLayoutParams(lp);
}
} }
} }
} }
export function _setAndroidLayoutParams(lp: org.nativescript.widgets.FlexboxLayout.LayoutParams, view: View) {
lp.order = FlexboxLayout.getOrder(view);
lp.flexGrow = FlexboxLayout.getFlexGrow(view);
lp.flexShrink = FlexboxLayout.getFlexShrink(view);
lp.alignSelf = alignSelfMap[FlexboxLayout.getAlignSelf(view)];
lp.wrapBefore = FlexboxLayout.getFlexWrapBefore(view);
lp.minWidth = layout.toDevicePixels(view.minWidth);
lp.minHeight = layout.toDevicePixels(view.minHeight);
}

View File

@ -1,6 +1,8 @@
declare module "ui/layouts/flexbox-layout" { declare module "ui/layouts/flexbox-layout" {
import {View} from "ui/core/view"; import {View} from "ui/core/view";
import {LayoutBase} from "ui/layouts/layout-base";
import {PropertyMetadata} from "ui/core/proxy";
export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse"; export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse";
export namespace FlexDirection { export namespace FlexDirection {
@ -55,7 +57,18 @@ declare module "ui/layouts/flexbox-layout" {
export const STRETCH: "stretch"; export const STRETCH: "stretch";
} }
export class FlexboxLayout { export class FlexboxLayout extends LayoutBase {
public static flexDirectionProperty: PropertyMetadata;
public static flexWrapProperty: PropertyMetadata;
public static justifyContentProperty: PropertyMetadata;
public static alignItemsProperty: PropertyMetadata;
public flexDirection: FlexDirection;
public flexWrap: FlexWrap;
public justifyContent: JustifyContent;
public alignItems: AlignItems;
public alignContent: AlignContent;
public static setOrder(view: View, order: number); public static setOrder(view: View, order: number);
public static getOrder(view: View): number; public static getOrder(view: View): number;
@ -67,5 +80,8 @@ declare module "ui/layouts/flexbox-layout" {
public static setAlignSelf(view: View, align: AlignSelf); public static setAlignSelf(view: View, align: AlignSelf);
public static getAlignSelf(view: View): AlignSelf; public static getAlignSelf(view: View): AlignSelf;
public static setFlexWrapBefore(view: View, wrap: boolean);
public static getFlexWrapBefore(view: View): boolean;
} }
} }

View File

@ -0,0 +1,6 @@
//@private
declare module "ui/layouts/flex-box" {
import {View} from "ui/core/view";
export function _setAndroidLayoutParams(lp: any /* org.nativescript.widgets.FlexboxLayout.LayoutParams */, view: View);
}
//@endprivate

File diff suppressed because it is too large Load Diff

View File

@ -21,4 +21,4 @@ export class Layout extends layoutBase.LayoutBase implements definition.Layout {
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
// Don't call super because it will measure the native element. // Don't call super because it will measure the native element.
} }
} }

View File

@ -65,6 +65,7 @@ export module layout {
export var EXACTLY = 1 << MODE_SHIFT; export var EXACTLY = 1 << MODE_SHIFT;
export var AT_MOST = 2 << MODE_SHIFT; export var AT_MOST = 2 << MODE_SHIFT;
export var MEASURED_HEIGHT_STATE_SHIFT = 0x00000010; /* 16 */
export var MEASURED_STATE_TOO_SMALL = 0x01000000; export var MEASURED_STATE_TOO_SMALL = 0x01000000;
export var MEASURED_STATE_MASK = 0xff000000; export var MEASURED_STATE_MASK = 0xff000000;
export var MEASURED_SIZE_MASK = 0x00ffffff; export var MEASURED_SIZE_MASK = 0x00ffffff;

View File

@ -28,6 +28,7 @@
/** /**
* Bits that provide the actual measured size. * Bits that provide the actual measured size.
*/ */
export var MEASURED_HEIGHT_STATE_SHIFT: number;
export var MEASURED_SIZE_MASK: number; export var MEASURED_SIZE_MASK: number;
export var MEASURED_STATE_MASK: number; export var MEASURED_STATE_MASK: number;
export var MEASURED_STATE_TOO_SMALL: number; export var MEASURED_STATE_TOO_SMALL: number;

View File

@ -305,6 +305,9 @@
public flexGrow: number; public flexGrow: number;
public flexShrink: number; public flexShrink: number;
public alignSelf: number; public alignSelf: number;
public wrapBefore: boolean;
public minWidth: number;
public minHeight: number;
} }
} }

View File

@ -10,6 +10,8 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"diagnostics": true, "diagnostics": true,
"sourceMap": true, "sourceMap": true,
"jsx": "react",
"reactNamespace": "UIBuilder",
"lib": [ "lib": [
"es2016" "es2016"
] ]