Add flexbox, we have to figure out layout params

This commit is contained in:
Panayot Cankov
2016-09-30 12:20:07 +03:00
parent 83536573d8
commit 943e67a4c8
10 changed files with 726 additions and 0 deletions

View File

@@ -59,6 +59,7 @@
/// <reference path="ui/layouts/absolute-layout/absolute-layout.d.ts" />
/// <reference path="ui/layouts/dock-layout/dock-layout.d.ts" />
/// <reference path="ui/layouts/grid-layout/grid-layout.d.ts" />
/// <reference path="ui/layouts/flexbox-layout/flexbox-layout.d.ts" />
/// <reference path="ui/layouts/layout-base.d.ts" />
/// <reference path="ui/layouts/layout.d.ts" />
/// <reference path="ui/layouts/stack-layout/stack-layout.d.ts" />

View File

@@ -0,0 +1,263 @@
import {LayoutBase} from "ui/layouts/layout-base";
import {View} from "ui/core/view";
import {PropertyMetadata} from "ui/core/proxy";
import {Property, PropertyMetadataSettings, PropertyChangeData} from "ui/core/dependency-observable";
import {registerSpecialProperty} from "ui/builder/special-properties";
import * as platform from "platform";
export type Basis = "auto" | number;
// 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;
export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse";
export namespace FlexDirection {
export const ROW: "row" = "row";
export const ROW_REVERSE: "row-reverse" = "row-reverse";
export const COLUMN: "column" = "column";
export const COLUMN_REVERSE: "column-reverse" = "column-reverse";
}
let validFlexDirection = {
"row": true,
"row-reverse": true,
"column": true,
"column-reverse": true
};
function validateFlexDirection(value: any): boolean {
return value in validFlexDirection;
}
export type FlexWrap = "nowrap" | "wrap" | "wrap-reverse";
export namespace FlexWrap {
export const NOWRAP: "nowrap" = "nowrap";
export const WRAP: "wrap" = "wrap";
export const WRAP_REVERSE: "wrap-reverse" = "wrap-reverse";
}
let validFlexWrap = {
"nowrap": true,
"wrap": true,
"wrap-reverse": true
};
function validateFlexWrap(value: any): boolean {
return value in validFlexWrap;
}
export type JustifyContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around";
export namespace JustifyContent {
export const FLEX_START: "flex-start" = "flex-start";
export const FLEX_END: "flex-end" = "flex-end";
export const CENTER: "center" = "center";
export const SPACE_BETWEEN: "space-between" = "space-between";
export const SPACE_AROUND: "space-around" = "space-around";
}
let validJustifyContent = {
"flex-start": true,
"flex-end": true,
"center": true,
"space-between": true,
"space-around": true
}
function validateJustifyContent(value: any): boolean {
return value in validJustifyContent;
}
export type AlignItems = "flex-start" | "flex-end" | "center" | "baseline" | "stretch";
export namespace AlignItems {
export const FLEX_START: "flex-start" = "flex-start";
export const FLEX_END: "flex-end" = "flex-end";
export const CENTER: "center" = "center";
export const BASELINE: "baseline" = "baseline";
export const STRETCH: "stretch" = "stretch";
}
let validAlignItems = {
"flex-start": true,
"flex-end": true,
"center": true,
"baseline": true,
"stretch": true
};
function validateAlignItems(value: any): boolean {
return value in validAlignItems;
}
export type AlignContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "stretch";
export namespace AlignContent {
export const FLEX_START: "flex-start" = "flex-start";
export const FLEX_END: "flex-end" = "flex-end";
export const CENTER: "center" = "center";
export const SPACE_BETWEEN: "space-between" = "space-between";
export const SPACE_AROUND: "space-around" = "space-around";
export const STRETCH: "stretch" = "stretch";
}
let validAlignContent = {
"flex-start": true,
"flex-end": true,
"center": true,
"space-between": true,
"space-around": true,
"stretch": true
};
function validateAlignContent(value: any): boolean {
return value in validAlignContent;
}
export type AlignSelf = "auto" | AlignItems;
export namespace AlignSelf {
export const AUTO: "auto" = "auto";
export const FLEX_START: "flex-start" = "flex-start";
export const FLEX_END: "flex-end" = "flex-end";
export const CENTER: "center" = "center";
export const BASELINE: "baseline" = "baseline";
export const STRETCH: "stretch" = "stretch";
}
function validateArgs(element: View): View {
if (!element) {
throw new Error("element cannot be null or undefinied.");
}
return element;
}
/**
* A common base class for all cross platform flexbox layout implementations.
*/
export abstract class FlexboxLayoutBase extends LayoutBase {
public static flexDirectionProperty = new Property("flexDirection", "FlexboxLayout", new PropertyMetadata("row", AffectsLayout, undefined, validateFlexDirection, (args: any) => args.object.setNativeFlexDirection(args.newValue)));
public static flexWrapProperty = new Property("flexWrap", "FlexboxLayout", new PropertyMetadata("nowrap", AffectsLayout, undefined, validateFlexWrap, (args: any) => args.object.setNativeFlexWrap(args.newValue)));
public static justifyContentProperty = new Property("justifyContent", "FlexboxLayout", new PropertyMetadata("flex-start", AffectsLayout, undefined, validateJustifyContent, (args: any) => args.object.setNativeJustifyContent(args.newValue)));
public static alignItemsProperty = new Property("alignItems", "FlexboxLayout", new PropertyMetadata("stretch", AffectsLayout, undefined, validateAlignItems, (args: any) => args.object.setNativeAlignItems(args.newValue)));
public static alignContentProperty = new Property("alignContent", "FlexboxLayout", new PropertyMetadata("stretch", AffectsLayout, undefined, validateAlignContent, (args: any) => args.object.setNativeAlignContent(args.newValue)));
// 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 flexGrowProperty = new Property("flexGrow", "FlexboxLayout", new PropertyMetadata(0, 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 alignSelfProperty = new Property("alignSelf", "FlexboxLayout", new PropertyMetadata(-1, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler<AlignSelf>((flexbox, element, oldValue, newValue) => flexbox.onAlignSelfPropertyChanged(element, oldValue, newValue))));
constructor() {
super();
}
get flexDirection(): FlexDirection {
return this._getValue(FlexboxLayoutBase.flexDirectionProperty);
}
set flexDirection(value: FlexDirection) {
this._setValue(FlexboxLayoutBase.flexDirectionProperty, value);
}
get flexWrap(): FlexWrap {
return this._getValue(FlexboxLayoutBase.flexWrapProperty);
}
set flexWrap(value: FlexWrap) {
this._setValue(FlexboxLayoutBase.flexWrapProperty, value);
}
get justifyContent(): JustifyContent {
return this._getValue(FlexboxLayoutBase.justifyContentProperty);
}
set justifyContent(value: JustifyContent) {
this._setValue(FlexboxLayoutBase.justifyContentProperty, value);
}
get alignItems(): AlignItems {
return this._getValue(FlexboxLayoutBase.alignItemsProperty);
}
set alignItems(value: AlignItems) {
this._setValue(FlexboxLayoutBase.alignItemsProperty, value);
}
get alignContent(): AlignItems {
return this._getValue(FlexboxLayoutBase.alignContentProperty);
}
set alignContent(value: AlignItems) {
this._setValue(FlexboxLayoutBase.alignContentProperty, value);
}
public static setOrder(view: View, order: number) {
validateArgs(view)._setValue(FlexboxLayoutBase.orderProperty, order);
}
public static getOrder(view: View): number {
return validateArgs(view)._getValue(FlexboxLayoutBase.orderProperty);
}
public static setFlexGrow(view: View, grow: number) {
validateArgs(view)._setValue(FlexboxLayoutBase.flexGrowProperty, grow);
}
public static getFlexGrow(view: View) {
return validateArgs(view)._getValue(FlexboxLayoutBase.flexGrowProperty);
}
public static setFlexShrink(view: View, shrink: number) {
validateArgs(view)._setValue(FlexboxLayoutBase.flexShrinkProperty, shrink);
}
public static getFlexShrink(view: View): number {
return validateArgs(view)._getValue(FlexboxLayoutBase.flexShrinkProperty);
}
public static setAlignSelf(view: View, align: AlignSelf) {
validateArgs(view)._setValue(FlexboxLayoutBase.alignSelfProperty, align);
}
public static getAlignSelf(view: View): AlignSelf {
return validateArgs(view)._getValue(FlexboxLayoutBase.alignSelfProperty);
}
protected onOrderPropertyChanged(element: View, oldValue: number, newValue: number): void {
console.log("order changed: " + newValue + " " + element);
}
protected abstract setNativeFlexDirection(flexDirection: FlexDirection);
protected abstract setNativeFlexWrap(flexWrap: FlexWrap);
protected abstract setNativeJustifyContent(justifyContent: JustifyContent);
protected abstract setNativeAlignItems(alignItems: AlignItems);
protected abstract setNativeAlignContent(alignContent: AlignContent);
protected onFlexGrowPropertyChanged(element: View, oldValue: number, newValue: number): void {
console.log("flex-grow changed: " + newValue + " " + element);
}
protected onFlexShrinkPropertyChanged(element: View, oldValue: number, newValue: number): 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) {
return (data: PropertyChangeData) => {
let element = data.object as View;
if (!(element instanceof View)) {
throw new Error("Element is not View or its descendant.");
}
let flexbox = element.parent;
if (flexbox instanceof FlexboxLayoutBase) {
handler(flexbox, element, data.oldValue, data.newValue);
}
}
}
}
registerSpecialProperty("order", (instance, propertyValue) => {
FlexboxLayoutBase.setOrder(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("flexGrow", (instance, propertyValue) => {
FlexboxLayoutBase.setFlexGrow(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("flexShrink", (instance, propertyValue) => {
FlexboxLayoutBase.setFlexShrink(instance, !isNaN(+propertyValue) && +propertyValue);
});
registerSpecialProperty("alignSelf", (instance, propertyValue) => {
FlexboxLayoutBase.setAlignSelf(instance, propertyValue);
});
// No flex-basis in our implementation.

View File

@@ -0,0 +1,124 @@
import {View} from "ui/core/view";
import {
FlexDirection,
FlexWrap,
JustifyContent,
AlignItems,
AlignContent,
AlignSelf,
FlexboxLayoutBase
} from "./flexbox-layout-common";
export * from "./flexbox-layout-common";
import FlexboxLayoutWidget = org.nativescript.widgets.FlexboxLayout;
const flexDirectionMap = {
[FlexDirection.ROW]: FlexboxLayoutWidget.FLEX_DIRECTION_ROW,
[FlexDirection.ROW_REVERSE]: FlexboxLayoutWidget.FLEX_DIRECTION_ROW_REVERSE,
[FlexDirection.COLUMN]: FlexboxLayoutWidget.FLEX_DIRECTION_COLUMN,
[FlexDirection.COLUMN_REVERSE]: FlexboxLayoutWidget.FLEX_DIRECTION_COLUMN_REVERSE
}
const flexWrapMap = {
[FlexWrap.NOWRAP]: FlexboxLayoutWidget.FLEX_WRAP_NOWRAP,
[FlexWrap.WRAP]: FlexboxLayoutWidget.FLEX_WRAP_WRAP,
[FlexWrap.WRAP_REVERSE]: FlexboxLayoutWidget.FLEX_WRAP_WRAP_REVERSE
}
const justifyContentMap = {
[JustifyContent.CENTER]: FlexboxLayoutWidget.JUSTIFY_CONTENT_CENTER,
[JustifyContent.FLEX_END]: FlexboxLayoutWidget.JUSTIFY_CONTENT_FLEX_END,
[JustifyContent.FLEX_START]: FlexboxLayoutWidget.JUSTIFY_CONTENT_FLEX_START,
[JustifyContent.SPACE_AROUND]: FlexboxLayoutWidget.JUSTIFY_CONTENT_SPACE_AROUND,
[JustifyContent.SPACE_BETWEEN]: FlexboxLayoutWidget.JUSTIFY_CONTENT_SPACE_BETWEEN
}
const alignItemsMap = {
[AlignItems.BASELINE]: FlexboxLayoutWidget.ALIGN_ITEMS_BASELINE,
[AlignItems.CENTER]: FlexboxLayoutWidget.ALIGN_ITEMS_CENTER,
[AlignItems.FLEX_END]: FlexboxLayoutWidget.ALIGN_ITEMS_FLEX_END,
[AlignItems.FLEX_START]: FlexboxLayoutWidget.ALIGN_ITEMS_FLEX_START,
[AlignItems.STRETCH]: FlexboxLayoutWidget.ALIGN_ITEMS_STRETCH
}
const alignContentMap = {
[AlignContent.CENTER]: FlexboxLayoutWidget.ALIGN_CONTENT_CENTER,
[AlignContent.FLEX_END]: FlexboxLayoutWidget.ALIGN_CONTENT_FLEX_END,
[AlignContent.FLEX_START]: FlexboxLayoutWidget.ALIGN_CONTENT_FLEX_START,
[AlignContent.SPACE_AROUND]: FlexboxLayoutWidget.ALIGN_CONTENT_SPACE_AROUND,
[AlignContent.SPACE_BETWEEN]: FlexboxLayoutWidget.ALIGN_CONTENT_SPACE_BETWEEN,
[AlignContent.STRETCH]: FlexboxLayoutWidget.ALIGN_CONTENT_STRETCH
}
export class FlexboxLayout extends FlexboxLayoutBase {
private _layout: FlexboxLayoutWidget;
constructor() {
super();
console.log("New FlexBoxLayout!");
}
get android(): FlexboxLayoutWidget { return this._layout; }
get _nativeView(): FlexboxLayoutWidget { return this._layout; }
public _createUI() {
this._layout = new org.nativescript.widgets.FlexboxLayout(this._context);
}
protected setNativeFlexDirection(flexDirection: FlexDirection) {
let value = flexDirectionMap[flexDirection];
console.log("setNativeFlexDirection: " + flexDirection + " -> " + value);
this.android.setFlexDirection(value);
}
protected setNativeFlexWrap(flexWrap: FlexWrap) {
console.log("flexWrap: " + flexWrap);
this.android.setFlexWrap(flexWrapMap[flexWrap]);
}
protected setNativeJustifyContent(justifyContent: JustifyContent) {
console.log("setNativeJustifyContent: " + justifyContent);
this.android.setJustifyContent(justifyContentMap[justifyContent]);
}
protected setNativeAlignItems(alignItems: AlignItems) {
console.log("setNativeAlignItems: " + alignItems);
this.android.setAlignItems(alignItemsMap[alignItems]);
}
protected setNativeAlignContent(alignContent: AlignContent) {
console.log("setNativeAlignContent: " + alignContent);
this.android.setAlignContent(alignContentMap[alignContent]);
}
protected onOrderPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("order changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.order = newValue);
}
protected onFlexGrowPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("flex-grow changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.flexGrow = newValue);
}
protected onFlexShrinkPropertyChanged(view: View, oldValue: number, newValue: number): void {
console.log("flex-shrink changed: " + newValue + " " + view);
this.setLayoutParamsProperty(view, lp => lp.flexShrink = newValue);
}
protected onAlignSelfPropertyChanged(view: View, oldValue: AlignSelf, newValue: AlignSelf): void {
console.log("align-self changed: " + newValue + " " + view);
// TODO: Map the align self enum:
// this.setLayoutParamsProperty(view, lp => lp.alignSelf = newValue);
}
private setLayoutParamsProperty(view: View, setter: (lp: org.nativescript.widgets.FlexboxLayout.LayoutParams) => void) {
let nativeView: android.view.View = view._nativeView;
var lp = nativeView.getLayoutParams() || new org.nativescript.widgets.FlexboxLayout.LayoutParams();
if (lp instanceof org.nativescript.widgets.FlexboxLayout.LayoutParams) {
setter(lp);
nativeView.setLayoutParams(lp);
}
}
}

View File

@@ -0,0 +1,71 @@
declare module "ui/layouts/flexbox-layout" {
import {View} from "ui/core/view";
export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse";
export namespace FlexDirection {
export const ROW: "row";
export const ROW_REVERSE: "row-reverse";
export const COLUMN: "column";
export const COLUMN_REVERSE: "column-reverse";
}
export type FlexWrap = "nowrap" | "wrap" | "wrap-reverse";
export namespace FlexWrap {
export const NOWRAP: "nowrap";
export const WRAP: "wrap";
export const WRAP_REVERSE: "wrap-reverse";
}
export type JustifyContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around";
export namespace JustifyContent {
export const FLEX_START: "flex-start";
export const FLEX_END: "flex-end";
export const CENTER: "center";
export const SPACE_BETWEEN: "space-between";
export const SPACE_AROUND: "space-around";
}
export type AlignItems = "flex-start" | "flex-end" | "center" | "baseline" | "stretch";
export namespace AlignItems {
export const FLEX_START: "flex-start";
export const FLEX_END: "flex-end";
export const CENTER: "center";
export const BASELINE: "baseline";
export const STRETCH: "stretch";
}
export type AlignContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "stretch";
export namespace AlignContent {
export const FLEX_START: "flex-start";
export const FLEX_END: "flex-end";
export const CENTER: "center";
export const SPACE_BETWEEN: "space-between";
export const SPACE_AROUND: "space-around";
export const STRETCH: "stretch";
}
export type AlignSelf = "auto" | AlignItems;
export namespace AlignSelf {
export const AUTO: "auto";
export const FLEX_START: "flex-start";
export const FLEX_END: "flex-end";
export const CENTER: "center";
export const BASELINE: "baseline";
export const STRETCH: "stretch";
}
export class FlexboxLayout {
public static setOrder(view: View, order: number);
public static getOrder(view: View): number;
public static setFlexGrow(view: View, grow: number);
public static getFlexGrow(view: View);
public static setFlexShrink(view: View, shrink: number);
public static getFlexShrink(view: View): number;
public static setAlignSelf(view: View, align: AlignSelf);
public static getAlignSelf(view: View): AlignSelf;
}
}

View File

@@ -0,0 +1,2 @@
{ "name" : "flexbox-layout",
"main" : "flexbox-layout" }