mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
chore: format
This commit is contained in:
File diff suppressed because it is too large
Load Diff
1624
nativescript-core/ui/core/view/index.d.ts
vendored
1624
nativescript-core/ui/core/view/index.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,63 +1,108 @@
|
||||
import { View } from "..";
|
||||
|
||||
export class ViewHelper {
|
||||
/**
|
||||
* Measure a child by taking into account its margins and a given measureSpecs.
|
||||
* @param parent This parameter is not used. You can pass null.
|
||||
* @param child The view to be measured.
|
||||
* @param measuredWidth The measured width that the parent layout specifies for this view.
|
||||
* @param measuredHeight The measured height that the parent layout specifies for this view.
|
||||
*/
|
||||
public static measureChild(parent: View, child: View, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number };
|
||||
/**
|
||||
* Measure a child by taking into account its margins and a given measureSpecs.
|
||||
* @param parent This parameter is not used. You can pass null.
|
||||
* @param child The view to be measured.
|
||||
* @param measuredWidth The measured width that the parent layout specifies for this view.
|
||||
* @param measuredHeight The measured height that the parent layout specifies for this view.
|
||||
*/
|
||||
public static measureChild(
|
||||
parent: View,
|
||||
child: View,
|
||||
widthMeasureSpec: number,
|
||||
heightMeasureSpec: number
|
||||
): { measuredWidth: number; measuredHeight: number };
|
||||
|
||||
/**
|
||||
* Layout a child by taking into account its margins, horizontal and vertical alignments and a given bounds.
|
||||
* @param parent This parameter is not used. You can pass null.
|
||||
* @param left Left position, relative to parent
|
||||
* @param top Top position, relative to parent
|
||||
* @param right Right position, relative to parent
|
||||
* @param bottom Bottom position, relative to parent
|
||||
*/
|
||||
public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void;
|
||||
/**
|
||||
* Layout a child by taking into account its margins, horizontal and vertical alignments and a given bounds.
|
||||
* @param parent This parameter is not used. You can pass null.
|
||||
* @param left Left position, relative to parent
|
||||
* @param top Top position, relative to parent
|
||||
* @param right Right position, relative to parent
|
||||
* @param bottom Bottom position, relative to parent
|
||||
*/
|
||||
public static layoutChild(
|
||||
parent: View,
|
||||
child: View,
|
||||
left: number,
|
||||
top: number,
|
||||
right: number,
|
||||
bottom: number
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Utility to reconcile a desired size and state, with constraints imposed
|
||||
* by a MeasureSpec. Will take the desired size, unless a different size
|
||||
* is imposed by the constraints. The returned value is a compound integer,
|
||||
* with the resolved size in the MEASURED_SIZE_MASK bits and
|
||||
* optionally the bit MEASURED_STATE_TOO_SMALL set if the resulting
|
||||
* size is smaller than the size the view wants to be.
|
||||
*/
|
||||
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number;
|
||||
/**
|
||||
* Utility to reconcile a desired size and state, with constraints imposed
|
||||
* by a MeasureSpec. Will take the desired size, unless a different size
|
||||
* is imposed by the constraints. The returned value is a compound integer,
|
||||
* with the resolved size in the MEASURED_SIZE_MASK bits and
|
||||
* optionally the bit MEASURED_STATE_TOO_SMALL set if the resulting
|
||||
* size is smaller than the size the view wants to be.
|
||||
*/
|
||||
public static resolveSizeAndState(
|
||||
size: number,
|
||||
specSize: number,
|
||||
specMode: number,
|
||||
childMeasuredState: number
|
||||
): number;
|
||||
|
||||
public static combineMeasuredStates(curState: number, newState): number;
|
||||
public static combineMeasuredStates(curState: number, newState): number;
|
||||
}
|
||||
|
||||
export namespace IOSHelper {
|
||||
/**
|
||||
* String value used when hooking to traitCollectionColorAppearanceChangedEvent event.
|
||||
*/
|
||||
export const traitCollectionColorAppearanceChangedEvent: string;
|
||||
/**
|
||||
* String value used when hooking to traitCollectionColorAppearanceChangedEvent event.
|
||||
*/
|
||||
export const traitCollectionColorAppearanceChangedEvent: string;
|
||||
|
||||
/**
|
||||
* Returns a view with viewController or undefined if no such found along the view's parent chain.
|
||||
* @param view The view form which to start the search.
|
||||
*/
|
||||
export function getParentWithViewController(view: View): View
|
||||
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void
|
||||
export function updateConstraints(controller: any /* UIViewController */, owner: View): void;
|
||||
export function layoutView(controller: any /* UIViewController */, owner: View): void;
|
||||
export function getPositionFromFrame(frame: any /* CGRect */): { left, top, right, bottom };
|
||||
export function getFrameFromPosition(position: { left, top, right, bottom }, insets?: { left, top, right, bottom }): any /* CGRect */;
|
||||
export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any /* CGRect */;
|
||||
export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any /* CGRect */;
|
||||
export class UILayoutViewController {
|
||||
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController;
|
||||
}
|
||||
export class UIAdaptivePresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp;
|
||||
}
|
||||
export class UIPopoverPresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIPopoverPresentationControllerDelegateImp;
|
||||
}
|
||||
/**
|
||||
* Returns a view with viewController or undefined if no such found along the view's parent chain.
|
||||
* @param view The view form which to start the search.
|
||||
*/
|
||||
export function getParentWithViewController(view: View): View;
|
||||
export function updateAutoAdjustScrollInsets(
|
||||
controller: any /* UIViewController */,
|
||||
owner: View
|
||||
): void;
|
||||
export function updateConstraints(
|
||||
controller: any /* UIViewController */,
|
||||
owner: View
|
||||
): void;
|
||||
export function layoutView(
|
||||
controller: any /* UIViewController */,
|
||||
owner: View
|
||||
): void;
|
||||
export function getPositionFromFrame(
|
||||
frame: any /* CGRect */
|
||||
): { left; top; right; bottom };
|
||||
export function getFrameFromPosition(
|
||||
position: { left; top; right; bottom },
|
||||
insets?: { left; top; right; bottom }
|
||||
): any; /* CGRect */
|
||||
export function shrinkToSafeArea(
|
||||
view: View,
|
||||
frame: any /* CGRect */
|
||||
): any; /* CGRect */
|
||||
export function expandBeyondSafeArea(
|
||||
view: View,
|
||||
frame: any /* CGRect */
|
||||
): any; /* CGRect */
|
||||
export class UILayoutViewController {
|
||||
public static initWithOwner(
|
||||
owner: WeakRef<View>
|
||||
): UILayoutViewController;
|
||||
}
|
||||
export class UIAdaptivePresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(
|
||||
owner: WeakRef<View>,
|
||||
whenClosedCallback: Function
|
||||
): UIAdaptivePresentationControllerDelegateImp;
|
||||
}
|
||||
export class UIPopoverPresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(
|
||||
owner: WeakRef<View>,
|
||||
whenClosedCallback: Function
|
||||
): UIPopoverPresentationControllerDelegateImp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,362 +3,518 @@ import { View } from "..";
|
||||
|
||||
// Requires
|
||||
import { ViewHelper } from "./view-helper-common";
|
||||
import {
|
||||
ios as iosUtils,
|
||||
layout
|
||||
} from "../../../../utils/utils";
|
||||
import {
|
||||
Trace
|
||||
} from "../../../../trace";
|
||||
import { ios as iosUtils, layout } from "../../../../utils/utils";
|
||||
import { Trace } from "../../../../trace";
|
||||
|
||||
export * from "./view-helper-common";
|
||||
|
||||
const majorVersion = iosUtils.MajorVersion;
|
||||
|
||||
export namespace IOSHelper {
|
||||
export const traitCollectionColorAppearanceChangedEvent = "traitCollectionColorAppearanceChanged";
|
||||
|
||||
export function getParentWithViewController(view: View): View {
|
||||
while (view && !view.viewController) {
|
||||
view = view.parent as View;
|
||||
}
|
||||
|
||||
// Note: Might return undefined if no parent with viewController is found
|
||||
return view;
|
||||
}
|
||||
|
||||
export function updateAutoAdjustScrollInsets(controller: UIViewController, owner: View): void {
|
||||
if (majorVersion <= 10) {
|
||||
owner._automaticallyAdjustsScrollViewInsets = false;
|
||||
// This API is deprecated, but has no alternative for <= iOS 10
|
||||
// Defaults to true and results to appliyng the insets twice together with our logic
|
||||
// for iOS 11+ we use the contentInsetAdjustmentBehavior property in scrollview
|
||||
// https://developer.apple.com/documentation/uikit/uiviewcontroller/1621372-automaticallyadjustsscrollviewin
|
||||
controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function updateConstraints(controller: UIViewController, owner: View): void {
|
||||
if (majorVersion <= 10) {
|
||||
const layoutGuide = initLayoutGuide(controller);
|
||||
(<any>controller.view).safeAreaLayoutGuide = layoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
function initLayoutGuide(controller: UIViewController) {
|
||||
const rootView = controller.view;
|
||||
const layoutGuide = UILayoutGuide.alloc().init();
|
||||
rootView.addLayoutGuide(layoutGuide);
|
||||
NSLayoutConstraint.activateConstraints(<any>[
|
||||
layoutGuide.topAnchor.constraintEqualToAnchor(controller.topLayoutGuide.bottomAnchor),
|
||||
layoutGuide.bottomAnchor.constraintEqualToAnchor(controller.bottomLayoutGuide.topAnchor),
|
||||
layoutGuide.leadingAnchor.constraintEqualToAnchor(rootView.leadingAnchor),
|
||||
layoutGuide.trailingAnchor.constraintEqualToAnchor(rootView.trailingAnchor)
|
||||
]);
|
||||
|
||||
return layoutGuide;
|
||||
}
|
||||
|
||||
export function layoutView(controller: UIViewController, owner: View): void {
|
||||
let layoutGuide = controller.view.safeAreaLayoutGuide;
|
||||
if (!layoutGuide) {
|
||||
Trace.write(`safeAreaLayoutGuide during layout of ${owner}. Creating fallback constraints, but layout might be wrong.`,
|
||||
Trace.categories.Layout, Trace.messageType.error);
|
||||
|
||||
layoutGuide = initLayoutGuide(controller);
|
||||
}
|
||||
const safeArea = layoutGuide.layoutFrame;
|
||||
let position = IOSHelper.getPositionFromFrame(safeArea);
|
||||
const safeAreaSize = safeArea.size;
|
||||
|
||||
const hasChildViewControllers = controller.childViewControllers.count > 0;
|
||||
if (hasChildViewControllers) {
|
||||
const fullscreen = controller.view.frame;
|
||||
position = IOSHelper.getPositionFromFrame(fullscreen);
|
||||
}
|
||||
|
||||
const safeAreaWidth = layout.round(layout.toDevicePixels(safeAreaSize.width));
|
||||
const safeAreaHeight = layout.round(layout.toDevicePixels(safeAreaSize.height));
|
||||
|
||||
const widthSpec = layout.makeMeasureSpec(safeAreaWidth, layout.EXACTLY);
|
||||
const heightSpec = layout.makeMeasureSpec(safeAreaHeight, layout.EXACTLY);
|
||||
|
||||
ViewHelper.measureChild(null, owner, widthSpec, heightSpec);
|
||||
ViewHelper.layoutChild(null, owner, position.left, position.top, position.right, position.bottom);
|
||||
|
||||
if (owner.parent) {
|
||||
owner.parent._layoutParent();
|
||||
}
|
||||
}
|
||||
|
||||
export function getPositionFromFrame(frame: CGRect): { left, top, right, bottom } {
|
||||
const left = layout.round(layout.toDevicePixels(frame.origin.x));
|
||||
const top = layout.round(layout.toDevicePixels(frame.origin.y));
|
||||
const right = layout.round(layout.toDevicePixels(frame.origin.x + frame.size.width));
|
||||
const bottom = layout.round(layout.toDevicePixels(frame.origin.y + frame.size.height));
|
||||
|
||||
return { left, right, top, bottom };
|
||||
}
|
||||
|
||||
export function getFrameFromPosition(position: { left, top, right, bottom }, insets?: { left, top, right, bottom }): CGRect {
|
||||
insets = insets || { left: 0, top: 0, right: 0, bottom: 0 };
|
||||
|
||||
const left = layout.toDeviceIndependentPixels(position.left + insets.left);
|
||||
const top = layout.toDeviceIndependentPixels(position.top + insets.top);
|
||||
const width = layout.toDeviceIndependentPixels(position.right - position.left - insets.left - insets.right);
|
||||
const height = layout.toDeviceIndependentPixels(position.bottom - position.top - insets.top - insets.bottom);
|
||||
|
||||
return CGRectMake(left, top, width, height);
|
||||
}
|
||||
|
||||
export function shrinkToSafeArea(view: View, frame: CGRect): CGRect {
|
||||
const insets = view.getSafeAreaInsets();
|
||||
if (insets.left || insets.top) {
|
||||
const position = IOSHelper.getPositionFromFrame(frame);
|
||||
const adjustedFrame = IOSHelper.getFrameFromPosition(position, insets);
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(this + " :shrinkToSafeArea: " + JSON.stringify(IOSHelper.getPositionFromFrame(adjustedFrame)), Trace.categories.Layout);
|
||||
}
|
||||
|
||||
return adjustedFrame;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function expandBeyondSafeArea(view: View, frame: CGRect): CGRect {
|
||||
const availableSpace = getAvailableSpaceFromParent(view, frame);
|
||||
const safeArea = availableSpace.safeArea;
|
||||
const fullscreen = availableSpace.fullscreen;
|
||||
const inWindow = availableSpace.inWindow;
|
||||
|
||||
const position = IOSHelper.getPositionFromFrame(frame);
|
||||
const safeAreaPosition = IOSHelper.getPositionFromFrame(safeArea);
|
||||
const fullscreenPosition = IOSHelper.getPositionFromFrame(fullscreen);
|
||||
const inWindowPosition = IOSHelper.getPositionFromFrame(inWindow);
|
||||
|
||||
const adjustedPosition = position;
|
||||
|
||||
if (position.left && inWindowPosition.left <= safeAreaPosition.left) {
|
||||
adjustedPosition.left = fullscreenPosition.left;
|
||||
}
|
||||
|
||||
if (position.top && inWindowPosition.top <= safeAreaPosition.top) {
|
||||
adjustedPosition.top = fullscreenPosition.top;
|
||||
}
|
||||
|
||||
if (inWindowPosition.right < fullscreenPosition.right && inWindowPosition.right >= safeAreaPosition.right + fullscreenPosition.left) {
|
||||
adjustedPosition.right += fullscreenPosition.right - inWindowPosition.right;
|
||||
}
|
||||
|
||||
if (inWindowPosition.bottom < fullscreenPosition.bottom && inWindowPosition.bottom >= safeAreaPosition.bottom + fullscreenPosition.top) {
|
||||
adjustedPosition.bottom += fullscreenPosition.bottom - inWindowPosition.bottom;
|
||||
}
|
||||
|
||||
const adjustedFrame = CGRectMake(layout.toDeviceIndependentPixels(adjustedPosition.left), layout.toDeviceIndependentPixels(adjustedPosition.top), layout.toDeviceIndependentPixels(adjustedPosition.right - adjustedPosition.left), layout.toDeviceIndependentPixels(adjustedPosition.bottom - adjustedPosition.top));
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(view + " :expandBeyondSafeArea: " + JSON.stringify(IOSHelper.getPositionFromFrame(adjustedFrame)), Trace.categories.Layout);
|
||||
}
|
||||
|
||||
return adjustedFrame;
|
||||
}
|
||||
|
||||
function getAvailableSpaceFromParent(view: View, frame: CGRect): { safeArea: CGRect, fullscreen: CGRect, inWindow: CGRect } {
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scrollView = null;
|
||||
let viewControllerView = null;
|
||||
|
||||
if (view.viewController) {
|
||||
viewControllerView = view.viewController.view;
|
||||
} else {
|
||||
let parent = view.parent as View;
|
||||
while (parent && !parent.viewController && !(parent.nativeViewProtected instanceof UIScrollView)) {
|
||||
parent = parent.parent as View;
|
||||
}
|
||||
|
||||
if (parent.nativeViewProtected instanceof UIScrollView) {
|
||||
scrollView = parent.nativeViewProtected;
|
||||
} else if (parent.viewController) {
|
||||
viewControllerView = parent.viewController.view;
|
||||
}
|
||||
}
|
||||
|
||||
let fullscreen = null;
|
||||
let safeArea = null;
|
||||
|
||||
if (viewControllerView) {
|
||||
safeArea = viewControllerView.safeAreaLayoutGuide.layoutFrame;
|
||||
fullscreen = viewControllerView.frame;
|
||||
}
|
||||
else if (scrollView) {
|
||||
const insets = scrollView.safeAreaInsets;
|
||||
safeArea = CGRectMake(insets.left, insets.top, scrollView.contentSize.width - insets.left - insets.right, scrollView.contentSize.height - insets.top - insets.bottom);
|
||||
fullscreen = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height);
|
||||
}
|
||||
|
||||
const locationInWindow = view.getLocationInWindow();
|
||||
let inWindowLeft = locationInWindow.x;
|
||||
let inWindowTop = locationInWindow.y;
|
||||
|
||||
if (scrollView) {
|
||||
inWindowLeft += scrollView.contentOffset.x;
|
||||
inWindowTop += scrollView.contentOffset.y;
|
||||
}
|
||||
|
||||
const inWindow = CGRectMake(inWindowLeft, inWindowTop, frame.size.width, frame.size.height);
|
||||
|
||||
return { safeArea: safeArea, fullscreen: fullscreen, inWindow: inWindow };
|
||||
}
|
||||
|
||||
export class UILayoutViewController extends UIViewController {
|
||||
public owner: WeakRef<View>;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController {
|
||||
const controller = <UILayoutViewController>UILayoutViewController.new();
|
||||
controller.owner = owner;
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
public viewDidLoad(): void {
|
||||
super.viewDidLoad();
|
||||
|
||||
// Unify translucent and opaque bars layout
|
||||
// this.edgesForExtendedLayout = UIRectEdgeBottom;
|
||||
this.extendedLayoutIncludesOpaqueBars = true;
|
||||
}
|
||||
|
||||
public viewWillLayoutSubviews(): void {
|
||||
super.viewWillLayoutSubviews();
|
||||
const owner = this.owner.get();
|
||||
if (owner) {
|
||||
updateConstraints(this, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
super.viewDidLayoutSubviews();
|
||||
const owner = this.owner.get();
|
||||
if (owner) {
|
||||
if (majorVersion >= 11) {
|
||||
// Handle nested UILayoutViewController safe area application.
|
||||
// Currently, UILayoutViewController can be nested only in a TabView.
|
||||
// The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout).
|
||||
const tabViewItem = owner.parent;
|
||||
const tabView = tabViewItem && tabViewItem.parent;
|
||||
let parent = tabView && tabView.parent;
|
||||
|
||||
// Handle Angular scenario where TabView is in a ProxyViewContainer
|
||||
// It is possible to wrap components in ProxyViewContainers indefinitely
|
||||
// Not using instanceof ProxyViewContainer to avoid circular dependency
|
||||
// TODO: Try moving UILayoutViewController out of view module
|
||||
while (parent && !parent.nativeViewProtected) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
const parentPageInsetsTop = parent.nativeViewProtected.safeAreaInsets.top;
|
||||
const currentInsetsTop = this.view.safeAreaInsets.top;
|
||||
const additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0);
|
||||
|
||||
const parentPageInsetsBottom = parent.nativeViewProtected.safeAreaInsets.bottom;
|
||||
const currentInsetsBottom = this.view.safeAreaInsets.bottom;
|
||||
const additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0);
|
||||
|
||||
if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) {
|
||||
const additionalInsets = new UIEdgeInsets({ top: additionalInsetsTop, left: 0, bottom: additionalInsetsBottom, right: 0 });
|
||||
this.additionalSafeAreaInsets = additionalInsets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutView(this, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
const owner = this.owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateAutoAdjustScrollInsets(this, owner);
|
||||
|
||||
if (!owner.parent) {
|
||||
owner.callLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidDisappear(animated: boolean): void {
|
||||
super.viewDidDisappear(animated);
|
||||
const owner = this.owner.get();
|
||||
if (owner && !owner.parent) {
|
||||
owner.callUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
// Mind implementation for other controllers
|
||||
public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void {
|
||||
super.traitCollectionDidChange(previousTraitCollection);
|
||||
|
||||
if (majorVersion >= 13) {
|
||||
const owner = this.owner.get();
|
||||
if (owner &&
|
||||
this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection &&
|
||||
this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
|
||||
owner.notify({ eventName: traitCollectionColorAppearanceChangedEvent, object: owner });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UIAdaptivePresentationControllerDelegateImp extends NSObject implements UIAdaptivePresentationControllerDelegate {
|
||||
public static ObjCProtocols = [UIAdaptivePresentationControllerDelegate];
|
||||
|
||||
private owner: WeakRef<View>;
|
||||
private closedCallback: Function;
|
||||
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp {
|
||||
const instance = <UIAdaptivePresentationControllerDelegateImp>super.new();
|
||||
instance.owner = owner;
|
||||
instance.closedCallback = whenClosedCallback;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public presentationControllerDidDismiss(presentationController: UIPresentationController) {
|
||||
const owner = this.owner.get();
|
||||
if (owner && typeof this.closedCallback === "function") {
|
||||
this.closedCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate {
|
||||
public static ObjCProtocols = [UIPopoverPresentationControllerDelegate];
|
||||
|
||||
private owner: WeakRef<View>;
|
||||
private closedCallback: Function;
|
||||
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIPopoverPresentationControllerDelegateImp {
|
||||
const instance = <UIPopoverPresentationControllerDelegateImp>super.new();
|
||||
instance.owner = owner;
|
||||
instance.closedCallback = whenClosedCallback;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
|
||||
const owner = this.owner.get();
|
||||
if (owner && typeof this.closedCallback === "function") {
|
||||
this.closedCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export const traitCollectionColorAppearanceChangedEvent =
|
||||
"traitCollectionColorAppearanceChanged";
|
||||
|
||||
export function getParentWithViewController(view: View): View {
|
||||
while (view && !view.viewController) {
|
||||
view = view.parent as View;
|
||||
}
|
||||
|
||||
// Note: Might return undefined if no parent with viewController is found
|
||||
return view;
|
||||
}
|
||||
|
||||
export function updateAutoAdjustScrollInsets(
|
||||
controller: UIViewController,
|
||||
owner: View
|
||||
): void {
|
||||
if (majorVersion <= 10) {
|
||||
owner._automaticallyAdjustsScrollViewInsets = false;
|
||||
// This API is deprecated, but has no alternative for <= iOS 10
|
||||
// Defaults to true and results to appliyng the insets twice together with our logic
|
||||
// for iOS 11+ we use the contentInsetAdjustmentBehavior property in scrollview
|
||||
// https://developer.apple.com/documentation/uikit/uiviewcontroller/1621372-automaticallyadjustsscrollviewin
|
||||
controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function updateConstraints(
|
||||
controller: UIViewController,
|
||||
owner: View
|
||||
): void {
|
||||
if (majorVersion <= 10) {
|
||||
const layoutGuide = initLayoutGuide(controller);
|
||||
(<any>controller.view).safeAreaLayoutGuide = layoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
function initLayoutGuide(controller: UIViewController) {
|
||||
const rootView = controller.view;
|
||||
const layoutGuide = UILayoutGuide.alloc().init();
|
||||
rootView.addLayoutGuide(layoutGuide);
|
||||
NSLayoutConstraint.activateConstraints(<any>[
|
||||
layoutGuide.topAnchor.constraintEqualToAnchor(
|
||||
controller.topLayoutGuide.bottomAnchor
|
||||
),
|
||||
layoutGuide.bottomAnchor.constraintEqualToAnchor(
|
||||
controller.bottomLayoutGuide.topAnchor
|
||||
),
|
||||
layoutGuide.leadingAnchor.constraintEqualToAnchor(
|
||||
rootView.leadingAnchor
|
||||
),
|
||||
layoutGuide.trailingAnchor.constraintEqualToAnchor(
|
||||
rootView.trailingAnchor
|
||||
),
|
||||
]);
|
||||
|
||||
return layoutGuide;
|
||||
}
|
||||
|
||||
export function layoutView(
|
||||
controller: UIViewController,
|
||||
owner: View
|
||||
): void {
|
||||
let layoutGuide = controller.view.safeAreaLayoutGuide;
|
||||
if (!layoutGuide) {
|
||||
Trace.write(
|
||||
`safeAreaLayoutGuide during layout of ${owner}. Creating fallback constraints, but layout might be wrong.`,
|
||||
Trace.categories.Layout,
|
||||
Trace.messageType.error
|
||||
);
|
||||
|
||||
layoutGuide = initLayoutGuide(controller);
|
||||
}
|
||||
const safeArea = layoutGuide.layoutFrame;
|
||||
let position = IOSHelper.getPositionFromFrame(safeArea);
|
||||
const safeAreaSize = safeArea.size;
|
||||
|
||||
const hasChildViewControllers =
|
||||
controller.childViewControllers.count > 0;
|
||||
if (hasChildViewControllers) {
|
||||
const fullscreen = controller.view.frame;
|
||||
position = IOSHelper.getPositionFromFrame(fullscreen);
|
||||
}
|
||||
|
||||
const safeAreaWidth = layout.round(
|
||||
layout.toDevicePixels(safeAreaSize.width)
|
||||
);
|
||||
const safeAreaHeight = layout.round(
|
||||
layout.toDevicePixels(safeAreaSize.height)
|
||||
);
|
||||
|
||||
const widthSpec = layout.makeMeasureSpec(safeAreaWidth, layout.EXACTLY);
|
||||
const heightSpec = layout.makeMeasureSpec(
|
||||
safeAreaHeight,
|
||||
layout.EXACTLY
|
||||
);
|
||||
|
||||
ViewHelper.measureChild(null, owner, widthSpec, heightSpec);
|
||||
ViewHelper.layoutChild(
|
||||
null,
|
||||
owner,
|
||||
position.left,
|
||||
position.top,
|
||||
position.right,
|
||||
position.bottom
|
||||
);
|
||||
|
||||
if (owner.parent) {
|
||||
owner.parent._layoutParent();
|
||||
}
|
||||
}
|
||||
|
||||
export function getPositionFromFrame(
|
||||
frame: CGRect
|
||||
): { left; top; right; bottom } {
|
||||
const left = layout.round(layout.toDevicePixels(frame.origin.x));
|
||||
const top = layout.round(layout.toDevicePixels(frame.origin.y));
|
||||
const right = layout.round(
|
||||
layout.toDevicePixels(frame.origin.x + frame.size.width)
|
||||
);
|
||||
const bottom = layout.round(
|
||||
layout.toDevicePixels(frame.origin.y + frame.size.height)
|
||||
);
|
||||
|
||||
return { left, right, top, bottom };
|
||||
}
|
||||
|
||||
export function getFrameFromPosition(
|
||||
position: { left; top; right; bottom },
|
||||
insets?: { left; top; right; bottom }
|
||||
): CGRect {
|
||||
insets = insets || { left: 0, top: 0, right: 0, bottom: 0 };
|
||||
|
||||
const left = layout.toDeviceIndependentPixels(
|
||||
position.left + insets.left
|
||||
);
|
||||
const top = layout.toDeviceIndependentPixels(position.top + insets.top);
|
||||
const width = layout.toDeviceIndependentPixels(
|
||||
position.right - position.left - insets.left - insets.right
|
||||
);
|
||||
const height = layout.toDeviceIndependentPixels(
|
||||
position.bottom - position.top - insets.top - insets.bottom
|
||||
);
|
||||
|
||||
return CGRectMake(left, top, width, height);
|
||||
}
|
||||
|
||||
export function shrinkToSafeArea(view: View, frame: CGRect): CGRect {
|
||||
const insets = view.getSafeAreaInsets();
|
||||
if (insets.left || insets.top) {
|
||||
const position = IOSHelper.getPositionFromFrame(frame);
|
||||
const adjustedFrame = IOSHelper.getFrameFromPosition(
|
||||
position,
|
||||
insets
|
||||
);
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(
|
||||
this +
|
||||
" :shrinkToSafeArea: " +
|
||||
JSON.stringify(
|
||||
IOSHelper.getPositionFromFrame(adjustedFrame)
|
||||
),
|
||||
Trace.categories.Layout
|
||||
);
|
||||
}
|
||||
|
||||
return adjustedFrame;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function expandBeyondSafeArea(view: View, frame: CGRect): CGRect {
|
||||
const availableSpace = getAvailableSpaceFromParent(view, frame);
|
||||
const safeArea = availableSpace.safeArea;
|
||||
const fullscreen = availableSpace.fullscreen;
|
||||
const inWindow = availableSpace.inWindow;
|
||||
|
||||
const position = IOSHelper.getPositionFromFrame(frame);
|
||||
const safeAreaPosition = IOSHelper.getPositionFromFrame(safeArea);
|
||||
const fullscreenPosition = IOSHelper.getPositionFromFrame(fullscreen);
|
||||
const inWindowPosition = IOSHelper.getPositionFromFrame(inWindow);
|
||||
|
||||
const adjustedPosition = position;
|
||||
|
||||
if (position.left && inWindowPosition.left <= safeAreaPosition.left) {
|
||||
adjustedPosition.left = fullscreenPosition.left;
|
||||
}
|
||||
|
||||
if (position.top && inWindowPosition.top <= safeAreaPosition.top) {
|
||||
adjustedPosition.top = fullscreenPosition.top;
|
||||
}
|
||||
|
||||
if (
|
||||
inWindowPosition.right < fullscreenPosition.right &&
|
||||
inWindowPosition.right >=
|
||||
safeAreaPosition.right + fullscreenPosition.left
|
||||
) {
|
||||
adjustedPosition.right +=
|
||||
fullscreenPosition.right - inWindowPosition.right;
|
||||
}
|
||||
|
||||
if (
|
||||
inWindowPosition.bottom < fullscreenPosition.bottom &&
|
||||
inWindowPosition.bottom >=
|
||||
safeAreaPosition.bottom + fullscreenPosition.top
|
||||
) {
|
||||
adjustedPosition.bottom +=
|
||||
fullscreenPosition.bottom - inWindowPosition.bottom;
|
||||
}
|
||||
|
||||
const adjustedFrame = CGRectMake(
|
||||
layout.toDeviceIndependentPixels(adjustedPosition.left),
|
||||
layout.toDeviceIndependentPixels(adjustedPosition.top),
|
||||
layout.toDeviceIndependentPixels(
|
||||
adjustedPosition.right - adjustedPosition.left
|
||||
),
|
||||
layout.toDeviceIndependentPixels(
|
||||
adjustedPosition.bottom - adjustedPosition.top
|
||||
)
|
||||
);
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(
|
||||
view +
|
||||
" :expandBeyondSafeArea: " +
|
||||
JSON.stringify(
|
||||
IOSHelper.getPositionFromFrame(adjustedFrame)
|
||||
),
|
||||
Trace.categories.Layout
|
||||
);
|
||||
}
|
||||
|
||||
return adjustedFrame;
|
||||
}
|
||||
|
||||
function getAvailableSpaceFromParent(
|
||||
view: View,
|
||||
frame: CGRect
|
||||
): { safeArea: CGRect; fullscreen: CGRect; inWindow: CGRect } {
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scrollView = null;
|
||||
let viewControllerView = null;
|
||||
|
||||
if (view.viewController) {
|
||||
viewControllerView = view.viewController.view;
|
||||
} else {
|
||||
let parent = view.parent as View;
|
||||
while (
|
||||
parent &&
|
||||
!parent.viewController &&
|
||||
!(parent.nativeViewProtected instanceof UIScrollView)
|
||||
) {
|
||||
parent = parent.parent as View;
|
||||
}
|
||||
|
||||
if (parent.nativeViewProtected instanceof UIScrollView) {
|
||||
scrollView = parent.nativeViewProtected;
|
||||
} else if (parent.viewController) {
|
||||
viewControllerView = parent.viewController.view;
|
||||
}
|
||||
}
|
||||
|
||||
let fullscreen = null;
|
||||
let safeArea = null;
|
||||
|
||||
if (viewControllerView) {
|
||||
safeArea = viewControllerView.safeAreaLayoutGuide.layoutFrame;
|
||||
fullscreen = viewControllerView.frame;
|
||||
} else if (scrollView) {
|
||||
const insets = scrollView.safeAreaInsets;
|
||||
safeArea = CGRectMake(
|
||||
insets.left,
|
||||
insets.top,
|
||||
scrollView.contentSize.width - insets.left - insets.right,
|
||||
scrollView.contentSize.height - insets.top - insets.bottom
|
||||
);
|
||||
fullscreen = CGRectMake(
|
||||
0,
|
||||
0,
|
||||
scrollView.contentSize.width,
|
||||
scrollView.contentSize.height
|
||||
);
|
||||
}
|
||||
|
||||
const locationInWindow = view.getLocationInWindow();
|
||||
let inWindowLeft = locationInWindow.x;
|
||||
let inWindowTop = locationInWindow.y;
|
||||
|
||||
if (scrollView) {
|
||||
inWindowLeft += scrollView.contentOffset.x;
|
||||
inWindowTop += scrollView.contentOffset.y;
|
||||
}
|
||||
|
||||
const inWindow = CGRectMake(
|
||||
inWindowLeft,
|
||||
inWindowTop,
|
||||
frame.size.width,
|
||||
frame.size.height
|
||||
);
|
||||
|
||||
return {
|
||||
safeArea: safeArea,
|
||||
fullscreen: fullscreen,
|
||||
inWindow: inWindow,
|
||||
};
|
||||
}
|
||||
|
||||
export class UILayoutViewController extends UIViewController {
|
||||
public owner: WeakRef<View>;
|
||||
|
||||
public static initWithOwner(
|
||||
owner: WeakRef<View>
|
||||
): UILayoutViewController {
|
||||
const controller = <UILayoutViewController>(
|
||||
UILayoutViewController.new()
|
||||
);
|
||||
controller.owner = owner;
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
public viewDidLoad(): void {
|
||||
super.viewDidLoad();
|
||||
|
||||
// Unify translucent and opaque bars layout
|
||||
// this.edgesForExtendedLayout = UIRectEdgeBottom;
|
||||
this.extendedLayoutIncludesOpaqueBars = true;
|
||||
}
|
||||
|
||||
public viewWillLayoutSubviews(): void {
|
||||
super.viewWillLayoutSubviews();
|
||||
const owner = this.owner.get();
|
||||
if (owner) {
|
||||
updateConstraints(this, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidLayoutSubviews(): void {
|
||||
super.viewDidLayoutSubviews();
|
||||
const owner = this.owner.get();
|
||||
if (owner) {
|
||||
if (majorVersion >= 11) {
|
||||
// Handle nested UILayoutViewController safe area application.
|
||||
// Currently, UILayoutViewController can be nested only in a TabView.
|
||||
// The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout).
|
||||
const tabViewItem = owner.parent;
|
||||
const tabView = tabViewItem && tabViewItem.parent;
|
||||
let parent = tabView && tabView.parent;
|
||||
|
||||
// Handle Angular scenario where TabView is in a ProxyViewContainer
|
||||
// It is possible to wrap components in ProxyViewContainers indefinitely
|
||||
// Not using instanceof ProxyViewContainer to avoid circular dependency
|
||||
// TODO: Try moving UILayoutViewController out of view module
|
||||
while (parent && !parent.nativeViewProtected) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
const parentPageInsetsTop =
|
||||
parent.nativeViewProtected.safeAreaInsets.top;
|
||||
const currentInsetsTop = this.view.safeAreaInsets.top;
|
||||
const additionalInsetsTop = Math.max(
|
||||
parentPageInsetsTop - currentInsetsTop,
|
||||
0
|
||||
);
|
||||
|
||||
const parentPageInsetsBottom =
|
||||
parent.nativeViewProtected.safeAreaInsets.bottom;
|
||||
const currentInsetsBottom = this.view.safeAreaInsets
|
||||
.bottom;
|
||||
const additionalInsetsBottom = Math.max(
|
||||
parentPageInsetsBottom - currentInsetsBottom,
|
||||
0
|
||||
);
|
||||
|
||||
if (
|
||||
additionalInsetsTop > 0 ||
|
||||
additionalInsetsBottom > 0
|
||||
) {
|
||||
const additionalInsets = new UIEdgeInsets({
|
||||
top: additionalInsetsTop,
|
||||
left: 0,
|
||||
bottom: additionalInsetsBottom,
|
||||
right: 0,
|
||||
});
|
||||
this.additionalSafeAreaInsets = additionalInsets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutView(this, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
const owner = this.owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateAutoAdjustScrollInsets(this, owner);
|
||||
|
||||
if (!owner.parent) {
|
||||
owner.callLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
public viewDidDisappear(animated: boolean): void {
|
||||
super.viewDidDisappear(animated);
|
||||
const owner = this.owner.get();
|
||||
if (owner && !owner.parent) {
|
||||
owner.callUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
// Mind implementation for other controllers
|
||||
public traitCollectionDidChange(
|
||||
previousTraitCollection: UITraitCollection
|
||||
): void {
|
||||
super.traitCollectionDidChange(previousTraitCollection);
|
||||
|
||||
if (majorVersion >= 13) {
|
||||
const owner = this.owner.get();
|
||||
if (
|
||||
owner &&
|
||||
this.traitCollection
|
||||
.hasDifferentColorAppearanceComparedToTraitCollection &&
|
||||
this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(
|
||||
previousTraitCollection
|
||||
)
|
||||
) {
|
||||
owner.notify({
|
||||
eventName: traitCollectionColorAppearanceChangedEvent,
|
||||
object: owner,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UIAdaptivePresentationControllerDelegateImp extends NSObject
|
||||
implements UIAdaptivePresentationControllerDelegate {
|
||||
public static ObjCProtocols = [
|
||||
UIAdaptivePresentationControllerDelegate,
|
||||
];
|
||||
|
||||
private owner: WeakRef<View>;
|
||||
private closedCallback: Function;
|
||||
|
||||
public static initWithOwnerAndCallback(
|
||||
owner: WeakRef<View>,
|
||||
whenClosedCallback: Function
|
||||
): UIAdaptivePresentationControllerDelegateImp {
|
||||
const instance = <UIAdaptivePresentationControllerDelegateImp>(
|
||||
super.new()
|
||||
);
|
||||
instance.owner = owner;
|
||||
instance.closedCallback = whenClosedCallback;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public presentationControllerDidDismiss(
|
||||
presentationController: UIPresentationController
|
||||
) {
|
||||
const owner = this.owner.get();
|
||||
if (owner && typeof this.closedCallback === "function") {
|
||||
this.closedCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UIPopoverPresentationControllerDelegateImp extends NSObject
|
||||
implements UIPopoverPresentationControllerDelegate {
|
||||
public static ObjCProtocols = [UIPopoverPresentationControllerDelegate];
|
||||
|
||||
private owner: WeakRef<View>;
|
||||
private closedCallback: Function;
|
||||
|
||||
public static initWithOwnerAndCallback(
|
||||
owner: WeakRef<View>,
|
||||
whenClosedCallback: Function
|
||||
): UIPopoverPresentationControllerDelegateImp {
|
||||
const instance = <UIPopoverPresentationControllerDelegateImp>(
|
||||
super.new()
|
||||
);
|
||||
instance.owner = owner;
|
||||
instance.closedCallback = whenClosedCallback;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public popoverPresentationControllerDidDismissPopover(
|
||||
popoverPresentationController: UIPopoverPresentationController
|
||||
) {
|
||||
const owner = this.owner.get();
|
||||
if (owner && typeof this.closedCallback === "function") {
|
||||
this.closedCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,205 +1,290 @@
|
||||
// Types
|
||||
import { View as ViewDefinition } from "..";
|
||||
import {
|
||||
HorizontalAlignment as HorizontalAlignmentDefinition,
|
||||
VerticalAlignment as VerticalAlignmentDefinition
|
||||
import {
|
||||
HorizontalAlignment as HorizontalAlignmentDefinition,
|
||||
VerticalAlignment as VerticalAlignmentDefinition,
|
||||
} from "../../../styling/style-properties";
|
||||
|
||||
// Requires
|
||||
import { layout } from "../../../../utils/utils";
|
||||
import {
|
||||
Trace
|
||||
} from "../../../../trace";
|
||||
import { Trace } from "../../../../trace";
|
||||
|
||||
export class ViewHelper {
|
||||
public static measureChild(
|
||||
parent: ViewDefinition,
|
||||
child: ViewDefinition,
|
||||
widthMeasureSpec: number,
|
||||
heightMeasureSpec: number
|
||||
): { measuredWidth: number; measuredHeight: number } {
|
||||
let measureWidth = 0;
|
||||
let measureHeight = 0;
|
||||
|
||||
public static measureChild(parent: ViewDefinition, child: ViewDefinition, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number } {
|
||||
let measureWidth = 0;
|
||||
let measureHeight = 0;
|
||||
if (child && !child.isCollapsed) {
|
||||
const widthSpec = parent
|
||||
? parent._currentWidthMeasureSpec
|
||||
: widthMeasureSpec;
|
||||
const heightSpec = parent
|
||||
? parent._currentHeightMeasureSpec
|
||||
: heightMeasureSpec;
|
||||
|
||||
if (child && !child.isCollapsed) {
|
||||
const width = layout.getMeasureSpecSize(widthSpec);
|
||||
const widthMode = layout.getMeasureSpecMode(widthSpec);
|
||||
|
||||
const widthSpec = parent ? parent._currentWidthMeasureSpec : widthMeasureSpec;
|
||||
const heightSpec = parent ? parent._currentHeightMeasureSpec : heightMeasureSpec;
|
||||
const height = layout.getMeasureSpecSize(heightSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightSpec);
|
||||
|
||||
const width = layout.getMeasureSpecSize(widthSpec);
|
||||
const widthMode = layout.getMeasureSpecMode(widthSpec);
|
||||
child._updateEffectiveLayoutValues(
|
||||
width,
|
||||
widthMode,
|
||||
height,
|
||||
heightMode
|
||||
);
|
||||
|
||||
const height = layout.getMeasureSpecSize(heightSpec);
|
||||
const heightMode = layout.getMeasureSpecMode(heightSpec);
|
||||
const style = child.style;
|
||||
const horizontalMargins =
|
||||
child.effectiveMarginLeft + child.effectiveMarginRight;
|
||||
const verticalMargins =
|
||||
child.effectiveMarginTop + child.effectiveMarginBottom;
|
||||
|
||||
child._updateEffectiveLayoutValues(width, widthMode, height, heightMode);
|
||||
const childWidthMeasureSpec = ViewHelper.getMeasureSpec(
|
||||
widthMeasureSpec,
|
||||
horizontalMargins,
|
||||
child.effectiveWidth,
|
||||
style.horizontalAlignment === "stretch"
|
||||
);
|
||||
const childHeightMeasureSpec = ViewHelper.getMeasureSpec(
|
||||
heightMeasureSpec,
|
||||
verticalMargins,
|
||||
child.effectiveHeight,
|
||||
style.verticalAlignment === "stretch"
|
||||
);
|
||||
|
||||
const style = child.style;
|
||||
const horizontalMargins = child.effectiveMarginLeft + child.effectiveMarginRight;
|
||||
const verticalMargins = child.effectiveMarginTop + child.effectiveMarginBottom;
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(
|
||||
`${
|
||||
child.parent
|
||||
} :measureChild: ${child} ${layout.measureSpecToString(
|
||||
childWidthMeasureSpec
|
||||
)}, ${layout.measureSpecToString(childHeightMeasureSpec)}}`,
|
||||
Trace.categories.Layout
|
||||
);
|
||||
}
|
||||
|
||||
const childWidthMeasureSpec = ViewHelper.getMeasureSpec(widthMeasureSpec, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch");
|
||||
const childHeightMeasureSpec = ViewHelper.getMeasureSpec(heightMeasureSpec, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch");
|
||||
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
||||
measureWidth = Math.round(
|
||||
child.getMeasuredWidth() + horizontalMargins
|
||||
);
|
||||
measureHeight = Math.round(
|
||||
child.getMeasuredHeight() + verticalMargins
|
||||
);
|
||||
}
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${child.parent} :measureChild: ${child} ${layout.measureSpecToString(childWidthMeasureSpec)}, ${layout.measureSpecToString(childHeightMeasureSpec)}}`, Trace.categories.Layout);
|
||||
}
|
||||
return { measuredWidth: measureWidth, measuredHeight: measureHeight };
|
||||
}
|
||||
|
||||
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
||||
measureWidth = Math.round(child.getMeasuredWidth() + horizontalMargins);
|
||||
measureHeight = Math.round(child.getMeasuredHeight() + verticalMargins);
|
||||
}
|
||||
public static layoutChild(
|
||||
parent: ViewDefinition,
|
||||
child: ViewDefinition,
|
||||
left: number,
|
||||
top: number,
|
||||
right: number,
|
||||
bottom: number,
|
||||
setFrame: boolean = true
|
||||
): void {
|
||||
if (!child || child.isCollapsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
return { measuredWidth: measureWidth, measuredHeight: measureHeight };
|
||||
}
|
||||
let childStyle = child.style;
|
||||
|
||||
public static layoutChild(parent: ViewDefinition, child: ViewDefinition, left: number, top: number, right: number, bottom: number, setFrame: boolean = true): void {
|
||||
if (!child || child.isCollapsed) {
|
||||
return;
|
||||
}
|
||||
let childTop: number;
|
||||
let childLeft: number;
|
||||
|
||||
let childStyle = child.style;
|
||||
let childWidth = child.getMeasuredWidth();
|
||||
let childHeight = child.getMeasuredHeight();
|
||||
|
||||
let childTop: number;
|
||||
let childLeft: number;
|
||||
let effectiveMarginTop = child.effectiveMarginTop;
|
||||
let effectiveMarginBottom = child.effectiveMarginBottom;
|
||||
|
||||
let childWidth = child.getMeasuredWidth();
|
||||
let childHeight = child.getMeasuredHeight();
|
||||
let vAlignment: VerticalAlignmentDefinition;
|
||||
if (
|
||||
child.effectiveHeight >= 0 &&
|
||||
childStyle.verticalAlignment === "stretch"
|
||||
) {
|
||||
vAlignment = "middle";
|
||||
} else {
|
||||
vAlignment = childStyle.verticalAlignment;
|
||||
}
|
||||
|
||||
let effectiveMarginTop = child.effectiveMarginTop;
|
||||
let effectiveMarginBottom = child.effectiveMarginBottom;
|
||||
switch (vAlignment) {
|
||||
case "top":
|
||||
childTop = top + effectiveMarginTop;
|
||||
break;
|
||||
|
||||
let vAlignment: VerticalAlignmentDefinition;
|
||||
if (child.effectiveHeight >= 0 && childStyle.verticalAlignment === "stretch") {
|
||||
vAlignment = "middle";
|
||||
}
|
||||
else {
|
||||
vAlignment = childStyle.verticalAlignment;
|
||||
}
|
||||
case "middle":
|
||||
childTop =
|
||||
top +
|
||||
(bottom -
|
||||
top -
|
||||
childHeight +
|
||||
(effectiveMarginTop - effectiveMarginBottom)) /
|
||||
2;
|
||||
break;
|
||||
|
||||
switch (vAlignment) {
|
||||
case "top":
|
||||
childTop = top + effectiveMarginTop;
|
||||
break;
|
||||
case "bottom":
|
||||
childTop = bottom - childHeight - effectiveMarginBottom;
|
||||
break;
|
||||
|
||||
case "middle":
|
||||
childTop = top + (bottom - top - childHeight + (effectiveMarginTop - effectiveMarginBottom)) / 2;
|
||||
break;
|
||||
case "stretch":
|
||||
default:
|
||||
childTop = top + effectiveMarginTop;
|
||||
childHeight =
|
||||
bottom - top - (effectiveMarginTop + effectiveMarginBottom);
|
||||
break;
|
||||
}
|
||||
|
||||
case "bottom":
|
||||
childTop = bottom - childHeight - effectiveMarginBottom;
|
||||
break;
|
||||
let effectiveMarginLeft = child.effectiveMarginLeft;
|
||||
let effectiveMarginRight = child.effectiveMarginRight;
|
||||
|
||||
case "stretch":
|
||||
default:
|
||||
childTop = top + effectiveMarginTop;
|
||||
childHeight = bottom - top - (effectiveMarginTop + effectiveMarginBottom);
|
||||
break;
|
||||
}
|
||||
let hAlignment: HorizontalAlignmentDefinition;
|
||||
if (
|
||||
child.effectiveWidth >= 0 &&
|
||||
childStyle.horizontalAlignment === "stretch"
|
||||
) {
|
||||
hAlignment = "center";
|
||||
} else {
|
||||
hAlignment = childStyle.horizontalAlignment;
|
||||
}
|
||||
|
||||
let effectiveMarginLeft = child.effectiveMarginLeft;
|
||||
let effectiveMarginRight = child.effectiveMarginRight;
|
||||
switch (hAlignment) {
|
||||
case "left":
|
||||
childLeft = left + effectiveMarginLeft;
|
||||
break;
|
||||
|
||||
let hAlignment: HorizontalAlignmentDefinition;
|
||||
if (child.effectiveWidth >= 0 && childStyle.horizontalAlignment === "stretch") {
|
||||
hAlignment = "center";
|
||||
}
|
||||
else {
|
||||
hAlignment = childStyle.horizontalAlignment;
|
||||
}
|
||||
case "center":
|
||||
childLeft =
|
||||
left +
|
||||
(right -
|
||||
left -
|
||||
childWidth +
|
||||
(effectiveMarginLeft - effectiveMarginRight)) /
|
||||
2;
|
||||
break;
|
||||
|
||||
switch (hAlignment) {
|
||||
case "left":
|
||||
childLeft = left + effectiveMarginLeft;
|
||||
break;
|
||||
case "right":
|
||||
childLeft = right - childWidth - effectiveMarginRight;
|
||||
break;
|
||||
|
||||
case "center":
|
||||
childLeft = left + (right - left - childWidth + (effectiveMarginLeft - effectiveMarginRight)) / 2;
|
||||
break;
|
||||
case "stretch":
|
||||
default:
|
||||
childLeft = left + effectiveMarginLeft;
|
||||
childWidth =
|
||||
right - left - (effectiveMarginLeft + effectiveMarginRight);
|
||||
break;
|
||||
}
|
||||
|
||||
case "right":
|
||||
childLeft = right - childWidth - effectiveMarginRight;
|
||||
break;
|
||||
let childRight = Math.round(childLeft + childWidth);
|
||||
let childBottom = Math.round(childTop + childHeight);
|
||||
childLeft = Math.round(childLeft);
|
||||
childTop = Math.round(childTop);
|
||||
|
||||
case "stretch":
|
||||
default:
|
||||
childLeft = left + effectiveMarginLeft;
|
||||
childWidth = right - left - (effectiveMarginLeft + effectiveMarginRight);
|
||||
break;
|
||||
}
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(
|
||||
child.parent +
|
||||
" :layoutChild: " +
|
||||
child +
|
||||
" " +
|
||||
childLeft +
|
||||
", " +
|
||||
childTop +
|
||||
", " +
|
||||
childRight +
|
||||
", " +
|
||||
childBottom,
|
||||
Trace.categories.Layout
|
||||
);
|
||||
}
|
||||
|
||||
let childRight = Math.round(childLeft + childWidth);
|
||||
let childBottom = Math.round(childTop + childHeight);
|
||||
childLeft = Math.round(childLeft);
|
||||
childTop = Math.round(childTop);
|
||||
child.layout(childLeft, childTop, childRight, childBottom, setFrame);
|
||||
}
|
||||
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, Trace.categories.Layout);
|
||||
}
|
||||
public static resolveSizeAndState(
|
||||
size: number,
|
||||
specSize: number,
|
||||
specMode: number,
|
||||
childMeasuredState: number
|
||||
): number {
|
||||
let result = size;
|
||||
switch (specMode) {
|
||||
case layout.UNSPECIFIED:
|
||||
result = Math.ceil(size);
|
||||
break;
|
||||
|
||||
child.layout(childLeft, childTop, childRight, childBottom, setFrame);
|
||||
}
|
||||
case layout.AT_MOST:
|
||||
if (specSize < size) {
|
||||
result =
|
||||
Math.ceil(specSize) | layout.MEASURED_STATE_TOO_SMALL;
|
||||
}
|
||||
break;
|
||||
|
||||
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number {
|
||||
let result = size;
|
||||
switch (specMode) {
|
||||
case layout.UNSPECIFIED:
|
||||
result = Math.ceil(size);
|
||||
break;
|
||||
case layout.EXACTLY:
|
||||
result = Math.ceil(specSize);
|
||||
break;
|
||||
}
|
||||
|
||||
case layout.AT_MOST:
|
||||
if (specSize < size) {
|
||||
result = Math.ceil(specSize) | layout.MEASURED_STATE_TOO_SMALL;
|
||||
}
|
||||
break;
|
||||
return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
|
||||
}
|
||||
|
||||
case layout.EXACTLY:
|
||||
result = Math.ceil(specSize);
|
||||
break;
|
||||
}
|
||||
public static combineMeasuredStates(curState: number, newState): number {
|
||||
return curState | newState;
|
||||
}
|
||||
|
||||
return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
|
||||
}
|
||||
private static getMeasureSpec(
|
||||
parentSpec: number,
|
||||
margins: number,
|
||||
childLength: number,
|
||||
stretched: boolean
|
||||
): number {
|
||||
const parentLength = layout.getMeasureSpecSize(parentSpec);
|
||||
const parentSpecMode = layout.getMeasureSpecMode(parentSpec);
|
||||
|
||||
public static combineMeasuredStates(curState: number, newState): number {
|
||||
return curState | newState;
|
||||
}
|
||||
let resultSize: number;
|
||||
let resultMode: number;
|
||||
|
||||
private static getMeasureSpec(parentSpec: number, margins: number, childLength: number, stretched: boolean): number {
|
||||
const parentLength = layout.getMeasureSpecSize(parentSpec);
|
||||
const parentSpecMode = layout.getMeasureSpecMode(parentSpec);
|
||||
// We want a specific size... let be it.
|
||||
if (childLength >= 0) {
|
||||
// If mode !== UNSPECIFIED we take the smaller of parentLength and childLength
|
||||
// Otherwise we will need to clip the view but this is not possible in all Android API levels.
|
||||
// TODO: remove Math.min(parentLength, childLength)
|
||||
resultSize =
|
||||
parentSpecMode === layout.UNSPECIFIED
|
||||
? childLength
|
||||
: Math.min(parentLength, childLength);
|
||||
resultMode = layout.EXACTLY;
|
||||
} else {
|
||||
switch (parentSpecMode) {
|
||||
// Parent has imposed an exact size on us
|
||||
case layout.EXACTLY:
|
||||
resultSize = Math.max(0, parentLength - margins);
|
||||
// if stretched - nativeView wants to be our size. So be it.
|
||||
// else - nativeView wants to determine its own size. It can't be bigger than us.
|
||||
resultMode = stretched ? layout.EXACTLY : layout.AT_MOST;
|
||||
break;
|
||||
|
||||
let resultSize: number;
|
||||
let resultMode: number;
|
||||
// Parent has imposed a maximum size on us
|
||||
case layout.AT_MOST:
|
||||
resultSize = Math.max(0, parentLength - margins);
|
||||
resultMode = layout.AT_MOST;
|
||||
break;
|
||||
|
||||
// We want a specific size... let be it.
|
||||
if (childLength >= 0) {
|
||||
// If mode !== UNSPECIFIED we take the smaller of parentLength and childLength
|
||||
// Otherwise we will need to clip the view but this is not possible in all Android API levels.
|
||||
// TODO: remove Math.min(parentLength, childLength)
|
||||
resultSize = parentSpecMode === layout.UNSPECIFIED ? childLength : Math.min(parentLength, childLength);
|
||||
resultMode = layout.EXACTLY;
|
||||
}
|
||||
else {
|
||||
switch (parentSpecMode) {
|
||||
// Parent has imposed an exact size on us
|
||||
case layout.EXACTLY:
|
||||
resultSize = Math.max(0, parentLength - margins);
|
||||
// if stretched - nativeView wants to be our size. So be it.
|
||||
// else - nativeView wants to determine its own size. It can't be bigger than us.
|
||||
resultMode = stretched ? layout.EXACTLY : layout.AT_MOST;
|
||||
break;
|
||||
// Equivalent to measure with Infinity.
|
||||
case layout.UNSPECIFIED:
|
||||
resultSize = 0;
|
||||
resultMode = layout.UNSPECIFIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Parent has imposed a maximum size on us
|
||||
case layout.AT_MOST:
|
||||
resultSize = Math.max(0, parentLength - margins);
|
||||
resultMode = layout.AT_MOST;
|
||||
break;
|
||||
|
||||
// Equivalent to measure with Infinity.
|
||||
case layout.UNSPECIFIED:
|
||||
resultSize = 0;
|
||||
resultMode = layout.UNSPECIFIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return layout.makeMeasureSpec(resultSize, resultMode);
|
||||
}
|
||||
return layout.makeMeasureSpec(resultSize, resultMode);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user