Merge branch 'feat/edge-to-edge' into feat/edge-to-edge-back-invoker

This commit is contained in:
Osei Fortune
2025-11-04 18:39:51 -04:00
7 changed files with 370 additions and 509 deletions

View File

@@ -7,6 +7,10 @@ import { makeValidator, makeParser } from '../ui/core/properties';
import { CubicBezierAnimationCurve } from '../ui/animation/animation-interfaces'; import { CubicBezierAnimationCurve } from '../ui/animation/animation-interfaces';
export namespace CoreTypes { export namespace CoreTypes {
type AndroidOverflowSingle = 'ignore' | 'none' | 'dont-apply';
type AndroidOverflowMultiple = 'left' | 'right' | 'top' | 'bottom' | 'left-dont-consume' | 'top-dont-consume' | 'right-dont-consume' | 'bottom-dont-consume' | 'all-but-left' | 'all-but-top' | 'all-but-right' | 'all-but-bottom';
type AndroidOverflowStacked = AndroidOverflowSingle | `${AndroidOverflowSingle},${AndroidOverflowMultiple}`;
export type AndroidOverflow = AndroidOverflowSingle | AndroidOverflowStacked;
export type CSSWideKeywords = 'initial' | 'inherit' | 'unset' | 'revert'; export type CSSWideKeywords = 'initial' | 'inherit' | 'unset' | 'revert';
/** /**

View File

Binary file not shown.

View File

@@ -2,7 +2,7 @@
import type { Point, CustomLayoutView as CustomLayoutViewDefinition, Position } from '.'; import type { Point, CustomLayoutView as CustomLayoutViewDefinition, Position } from '.';
import type { GestureTypes, GestureEventData } from '../../gestures'; import type { GestureTypes, GestureEventData } from '../../gestures';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, AndroidHelper } from './view-common'; import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, AndroidHelper, androidOverflowEdgeProperty } from './view-common';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties'; import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties';
import { layout } from '../../../utils'; import { layout } from '../../../utils';
import { Trace } from '../../../trace'; import { Trace } from '../../../trace';
@@ -372,20 +372,21 @@ const INSET_TOP_CONSUMED = 20;
const INSET_RIGHT_CONSUMED = 24; const INSET_RIGHT_CONSUMED = 24;
const INSET_BOTTOM_CONSUMED = 28; const INSET_BOTTOM_CONSUMED = 28;
const OverflowEdgeIgnore = -1;
const OverflowEdgeNone: number = 0; const OverflowEdgeNone: number = 0;
const OverflowEdgeLeft: number = 1; const OverflowEdgeLeft: number = 1 << 1;
const OverflowEdgeTop: number = 1 << 1; const OverflowEdgeTop: number = 1 << 2;
const OverflowEdgeRight: number = 1 << 2; const OverflowEdgeRight: number = 1 << 3;
const OverflowEdgeBottom: number = 1 << 3; const OverflowEdgeBottom: number = 1 << 4;
const OverflowEdgeDontApply: number = 1 << 4; const OverflowEdgeDontApply: number = 1 << 5;
const OverflowEdgeLeftDontConsume: number = 1 << 5; const OverflowEdgeLeftDontConsume: number = 1 << 6;
const OverflowEdgeTopDontConsume: number = 1 << 6; const OverflowEdgeTopDontConsume: number = 1 << 7;
const OverflowEdgeRightDontConsume: number = 1 << 7; const OverflowEdgeRightDontConsume: number = 1 << 8;
const OverflowEdgeBottomDontConsume: number = 1 << 8; const OverflowEdgeBottomDontConsume: number = 1 << 9;
const OverflowEdgeAllButLeft: number = 1 << 9; const OverflowEdgeAllButLeft: number = 1 << 10;
const OverflowEdgeAllButTop: number = 1 << 10; const OverflowEdgeAllButTop: number = 1 << 11;
const OverflowEdgeAllButRight: number = 1 << 11; const OverflowEdgeAllButRight: number = 1 << 12;
const OverflowEdgeAllButBottom: number = 1 << 12; const OverflowEdgeAllButBottom: number = 1 << 13;
class Inset { class Inset {
private view: DataView; private view: DataView;
@@ -621,132 +622,33 @@ export class View extends ViewCommon {
return manager; return manager;
} }
protected _defaultOverflowEdge: number = OverflowEdgeNone; [androidOverflowEdgeProperty.setNative](value: CoreTypes.AndroidOverflow) {
protected _defaultOverflowEdgeValue: string = 'none'; const nativeView = this.nativeViewProtected as any;
// @ts-ignore if (typeof value !== 'string' || nativeView === null || nativeView == undefined) {
public set androidOverflowEdge(value: string) {
if (typeof value !== 'string') {
return; return;
} }
const nativeView = this.nativeViewProtected as any;
if (nativeView && nativeView.setOverflowEdge) { if (!('setOverflowEdge' in nativeView)) {
if (value === 'none') { return;
}
switch (value) {
case 'none':
nativeView.setOverflowEdge(OverflowEdgeNone); nativeView.setOverflowEdge(OverflowEdgeNone);
} else { break;
const newValue = parseEdges(value); case 'ignore':
if (newValue !== null) { nativeView.setOverflowEdge(OverflowEdgeIgnore);
nativeView.setOverflowEdge(newValue); break;
}
}
} else {
const edge = parseEdges(value);
if (edge === null) {
return;
}
this._defaultOverflowEdgeValue = value;
this._defaultOverflowEdge = edge;
}
}
public get androidOverflowEdge() {
const nativeView = this.nativeViewProtected as any;
if (nativeView && nativeView.getOverflowEdge) {
const overflowEdge = nativeView.getOverflowEdge();
switch (overflowEdge) {
case OverflowEdgeNone:
return 'none';
case OverflowEdgeLeft:
return 'left';
case OverflowEdgeTop:
return 'top';
case OverflowEdgeRight:
return 'right';
case OverflowEdgeBottom:
return 'bottom';
case OverflowEdgeDontApply:
return 'dont-apply';
case OverflowEdgeLeftDontConsume:
return 'left-dont-consume';
case OverflowEdgeTopDontConsume:
return 'top-dont-consume';
case OverflowEdgeRightDontConsume:
return 'right-dont-consume';
case OverflowEdgeBottomDontConsume:
return 'bottom-dont-consume';
case OverflowEdgeAllButLeft:
return 'all-but-left';
case OverflowEdgeAllButTop:
return 'all-but-top';
case OverflowEdgeAllButRight:
return 'all-but-right';
case OverflowEdgeAllButBottom:
return 'all-but-bottom';
default: default:
{ {
let value = ''; const edge = parseEdges(value);
const overflowLeftConsume = (overflowEdge & OverflowEdgeLeft) == OverflowEdgeLeft;
const overflowTopConsume = (overflowEdge & OverflowEdgeTop) == OverflowEdgeTop;
const overflowRightConsume = (overflowEdge & OverflowEdgeRight) == OverflowEdgeRight;
const overflowBottomConsume = (overflowEdge & OverflowEdgeBottom) == OverflowEdgeBottom;
const overflowLeft = (overflowEdge & OverflowEdgeLeftDontConsume) == OverflowEdgeLeftDontConsume; if (edge != null) {
const overflowTop = (overflowEdge & OverflowEdgeTopDontConsume) == OverflowEdgeTopDontConsume; nativeView.setOverflowEdge(edge);
const overflowRight = (overflowEdge & OverflowEdgeRightDontConsume) == OverflowEdgeRightDontConsume;
const overflowBottom = (overflowEdge & OverflowEdgeBottomDontConsume) == OverflowEdgeBottomDontConsume;
if (overflowLeftConsume) {
value += 'left';
}
if (overflowTopConsume) {
if (value.length > 0) {
value += ',';
}
value += 'top';
}
if (overflowRightConsume) {
if (value.length > 0) {
value += ',';
}
value += 'right';
}
if (overflowBottomConsume) {
if (value.length > 0) {
value += ',';
}
value += 'bottom';
}
if (overflowLeft) {
value += 'left-dont-consume';
}
if (overflowTop) {
if (value.length > 0) {
value += ',';
}
value += 'top-dont-consume';
}
if (overflowRight) {
if (value.length > 0) {
value += ',';
}
value += 'right-dont-consume';
}
if (overflowBottom) {
if (value.length > 0) {
value += ',';
}
value += 'bottom-dont-consume';
} }
} }
break; break;
} }
} else {
if (this._defaultOverflowEdgeValue) {
return this._defaultOverflowEdgeValue;
}
}
return 'none';
} }
@profile @profile
@@ -802,13 +704,6 @@ export class View extends ViewCommon {
if (!this.insetListenerIsSet && this.needsInsetListener) { if (!this.insetListenerIsSet && this.needsInsetListener) {
this.setInsetListener(); this.setInsetListener();
} }
const nativeView = this.nativeViewProtected as any;
if (typeof this._defaultOverflowEdge === 'number') {
if (nativeView && nativeView.setOverflowEdge) {
nativeView.setOverflowEdge(this._defaultOverflowEdge);
}
}
} }
public needsOnLayoutChangeListener() { public needsOnLayoutChangeListener() {
@@ -1614,113 +1509,43 @@ export class View extends ViewCommon {
} }
} }
function parseEdges(edges: string): number | null { const edgeMap: Record<string, number> = {
const values = edges.trim().split(','); none: OverflowEdgeNone,
let newValue = -1; left: OverflowEdgeLeft,
for (let value of values) { top: OverflowEdgeTop,
const trimmedValue = value.trim(); right: OverflowEdgeRight,
switch (trimmedValue) { bottom: OverflowEdgeBottom,
case 'none': 'dont-apply': OverflowEdgeDontApply,
if (newValue === -1) { 'left-dont-consume': OverflowEdgeLeftDontConsume,
newValue = OverflowEdgeNone; 'top-dont-consume': OverflowEdgeTopDontConsume,
} else { 'right-dont-consume': OverflowEdgeRightDontConsume,
newValue |= OverflowEdgeNone; 'bottom-dont-consume': OverflowEdgeBottomDontConsume,
} 'all-but-left': OverflowEdgeAllButLeft,
break; 'all-but-top': OverflowEdgeAllButTop,
case 'left': 'all-but-right': OverflowEdgeAllButRight,
if (newValue === -1) { 'all-but-bottom': OverflowEdgeAllButBottom,
newValue = OverflowEdgeLeft; };
} else {
newValue |= OverflowEdgeLeft;
}
break;
case 'top':
if (newValue === -1) {
newValue = OverflowEdgeTop;
} else {
newValue |= OverflowEdgeTop;
}
break;
case 'right':
if (newValue === -1) {
newValue = OverflowEdgeRight;
} else {
newValue |= OverflowEdgeRight;
}
break;
case 'bottom':
if (newValue === -1) {
newValue = OverflowEdgeBottom;
} else {
newValue |= OverflowEdgeBottom;
}
break;
case 'dont-apply':
newValue = OverflowEdgeDontApply;
break;
case 'left-dont-consume':
if (newValue === -1) {
newValue = OverflowEdgeLeftDontConsume;
} else {
newValue |= OverflowEdgeLeftDontConsume;
}
break;
case 'top-dont-consume':
if (newValue === -1) {
newValue = OverflowEdgeTopDontConsume;
} else {
newValue |= OverflowEdgeTopDontConsume;
}
break;
case 'right-dont-consume':
if (newValue === -1) {
newValue = OverflowEdgeRightDontConsume;
} else {
newValue |= OverflowEdgeRightDontConsume;
}
break;
case 'bottom-dont-consume':
if (newValue === -1) {
newValue = OverflowEdgeBottomDontConsume;
} else {
newValue |= OverflowEdgeBottomDontConsume;
}
case 'all-but-left':
if (newValue === -1) {
newValue = OverflowEdgeAllButLeft;
} else {
newValue |= OverflowEdgeAllButLeft;
}
case 'all-but-top':
if (newValue === -1) {
newValue = OverflowEdgeAllButTop;
} else {
newValue |= OverflowEdgeAllButTop;
}
case 'all-but-right':
if (newValue === -1) {
newValue = OverflowEdgeAllButRight;
} else {
newValue |= OverflowEdgeAllButRight;
}
case 'all-but-bottom':
if (newValue === -1) {
newValue = OverflowEdgeAllButBottom;
} else {
newValue |= OverflowEdgeAllButBottom;
}
break;
}
}
if (newValue === -1) { function parseEdges(edges: string): number | null {
return null; let result = 0;
const values = edges.split(',');
for (const raw of values) {
const value = edgeMap[raw.trim()];
if (value === undefined) continue;
// dont-apply overrides everything else
if (value === OverflowEdgeDontApply) return value;
result |= value;
} }
return newValue; return result === 0 ? null : result;
} }
export class ContainerView extends View { export class ContainerView extends View {
public iosOverflowSafeArea: boolean; public iosOverflowSafeArea: boolean;
constructor() {
super();
this.androidOverflowEdge = 'none';
}
} }
export class CustomLayoutView extends ContainerView implements CustomLayoutViewDefinition { export class CustomLayoutView extends ContainerView implements CustomLayoutViewDefinition {

View File

@@ -987,7 +987,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public iosOverflowSafeArea: boolean; public iosOverflowSafeArea: boolean;
public iosOverflowSafeAreaEnabled: boolean; public iosOverflowSafeAreaEnabled: boolean;
public iosIgnoreSafeArea: boolean; public iosIgnoreSafeArea: boolean;
public androidOverflowEdge: string; public androidOverflowEdge: CoreTypes.AndroidOverflow;
get isLayoutValid(): boolean { get isLayoutValid(): boolean {
return this._isLayoutValid; return this._isLayoutValid;
@@ -1315,6 +1315,12 @@ export const iosIgnoreSafeAreaProperty = new InheritedProperty({
}); });
iosIgnoreSafeAreaProperty.register(ViewCommon); iosIgnoreSafeAreaProperty.register(ViewCommon);
export const androidOverflowEdgeProperty = new Property<ViewCommon, CoreTypes.AndroidOverflow>({
name: 'androidOverflowEdge',
defaultValue: 'ignore',
});
androidOverflowEdgeProperty.register(ViewCommon);
export const visionHoverStyleProperty = new Property<ViewCommon, string | VisionHoverOptions>({ export const visionHoverStyleProperty = new Property<ViewCommon, string | VisionHoverOptions>({
name: 'visionHoverStyle', name: 'visionHoverStyle',
valueChanged(view, oldValue, newValue) { valueChanged(view, oldValue, newValue) {

View File

@@ -93,8 +93,7 @@ export class Frame extends FrameBase {
constructor() { constructor() {
super(); super();
this._android = new AndroidFrame(this); this._android = new AndroidFrame(this);
this._defaultOverflowEdge = 1 << 4; this.androidOverflowEdge = 'ignore';
this._defaultOverflowEdgeValue = 'dont-apply';
} }
public static reloadPage(context?: ModuleContext): void { public static reloadPage(context?: ModuleContext): void {

View File

@@ -176,56 +176,83 @@ export function isRealDevice(): boolean {
const DefaultLightScrim = new Color(0xe6, 0xff, 0xff, 0xff); const DefaultLightScrim = new Color(0xe6, 0xff, 0xff, 0xff);
const DefaultDarkScrim = new Color(0x80, 0x1b, 0x1b, 0x1b); const DefaultDarkScrim = new Color(0x80, 0x1b, 0x1b, 0x1b);
const DefaultStatusBarLight = new Color(0);
const DefaultStatusBarDark = new Color(0);
let statusBarDarkColor: Color | null = null; interface ISystemColor {
let statusBarLightColor: Color | null = null; navigationBarLight: Color;
navigationBarDark: Color;
statusBarLight: Color;
statusBarDark: Color;
handler?: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean;
}
const systemColors = new WeakMap<androidx.appcompat.app.AppCompatActivity, ISystemColor>();
export function setStatusBarColor(lightColor: Color | null = null, darkColor: Color | null = null): void { function setEnableEdgeToEdge(activity: androidx.appcompat.app.AppCompatActivity, existingColors: ISystemColor) {
statusBarLightColor = lightColor;
statusBarDarkColor = darkColor;
const activity = getCurrentActivity();
if (activity) {
enableEdgeToEdge(activity, { enableEdgeToEdge(activity, {
statusBarLightColor: lightColor, statusBarLightColor: existingColors.statusBarLight,
statusBarDarkColor: darkColor, statusBarDarkColor: existingColors.statusBarDark,
navigationBarLightColor, navigationBarLightColor: existingColors.navigationBarLight,
navigationBarDarkColor, navigationBarDarkColor: existingColors.navigationBarDark,
handleDarkMode: darkModeHandler, handleDarkMode: existingColors?.handler ?? null,
}); });
}
export function setStatusBarColor(options?: { activity?: androidx.appcompat.app.AppCompatActivity; lightColor?: Color; darkColor?: Color }): void {
const statusBarLightColor = options?.lightColor ?? null;
const statusBarDarkColor = options?.darkColor ?? null;
const activity = options?.activity ?? getCurrentActivity();
if (activity) {
const existingColors = systemColors.get(activity) ?? {
navigationBarLight: DefaultLightScrim,
navigationBarDark: DefaultDarkScrim,
statusBarLight: DefaultStatusBarLight,
statusBarDark: DefaultStatusBarDark,
};
existingColors.statusBarLight ??= statusBarLightColor;
existingColors.statusBarDark ??= statusBarDarkColor;
systemColors.set(getCurrentActivity(), existingColors);
setEnableEdgeToEdge(activity, existingColors);
} }
} }
let navigationBarDarkColor: Color | null = null; export function setNavigationBarColor(options?: { activity?: androidx.appcompat.app.AppCompatActivity; lightColor?: Color; darkColor?: Color }): void {
let navigationBarLightColor: Color | null = null; const navigationBarLightColor = options?.lightColor ?? null;
const navigationBarDarkColor = options?.darkColor ?? null;
export function setNavigationBarColor(lightColor: Color | null = null, darkColor: Color | null = null): void { const activity = options?.activity ?? getCurrentActivity();
navigationBarLightColor = lightColor;
navigationBarDarkColor = darkColor;
const activity = getCurrentActivity();
if (activity) { if (activity) {
enableEdgeToEdge(activity, { const existingColors = systemColors.get(activity) ?? {
statusBarLightColor, navigationBarLight: DefaultLightScrim,
statusBarDarkColor, navigationBarDark: DefaultDarkScrim,
navigationBarLightColor: navigationBarLightColor, statusBarLight: DefaultStatusBarLight,
navigationBarDarkColor: navigationBarDarkColor, statusBarDark: DefaultStatusBarDark,
handleDarkMode: darkModeHandler, };
}); existingColors.navigationBarLight ??= navigationBarLightColor;
existingColors.navigationBarDark ??= navigationBarDarkColor;
systemColors.set(getCurrentActivity(), existingColors);
setEnableEdgeToEdge(activity, existingColors);
} }
} }
let darkModeHandler: ((bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean) | null = null; export function setDarkModeHandler(options?: { activity?: androidx.appcompat.app.AppCompatActivity; handler: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean }): void {
const darkModeHandler = options?.handler ?? null;
export function setDarkModeHandler(handler: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean): void { const activity = options?.activity ?? getCurrentActivity();
darkModeHandler = handler;
const activity = getCurrentActivity();
if (activity) { if (activity) {
enableEdgeToEdge(activity, { const existingColors = systemColors.get(activity) ?? {
statusBarLightColor, navigationBarLight: DefaultLightScrim,
statusBarDarkColor, navigationBarDark: DefaultDarkScrim,
navigationBarLightColor, statusBarLight: DefaultStatusBarLight,
navigationBarDarkColor, statusBarDark: DefaultStatusBarDark,
handleDarkMode: handler, };
});
existingColors.handler ??= darkModeHandler;
systemColors.set(getCurrentActivity(), existingColors);
setEnableEdgeToEdge(activity, existingColors);
} }
} }
@@ -240,21 +267,10 @@ export function enableEdgeToEdge(
}, },
): void { ): void {
let handleDarkMode: org.nativescript.widgets.Utils.HandleDarkMode; let handleDarkMode: org.nativescript.widgets.Utils.HandleDarkMode;
let statusBarLight: number = statusBarLightColor?.android ?? 0; let statusBarLight: number = 0;
let statusBarDark: number = statusBarDarkColor?.android ?? 0; let statusBarDark: number = 0;
let navigationBarLight: number = navigationBarLightColor?.android ?? DefaultLightScrim.android; let navigationBarLight: number = DefaultLightScrim.android;
let navigationBarDark: number = navigationBarDarkColor?.android ?? DefaultDarkScrim.android; let navigationBarDark: number = DefaultDarkScrim.android;
if (darkModeHandler) {
handleDarkMode = new org.nativescript.widgets.Utils.HandleDarkMode({
onHandle(bar, resources) {
if (bar === 0) {
return darkModeHandler('status', resources);
} else {
return darkModeHandler('navigation', resources);
}
},
});
}
if (options) { if (options) {
if (typeof options.handleDarkMode === 'function') { if (typeof options.handleDarkMode === 'function') {
handleDarkMode = new org.nativescript.widgets.Utils.HandleDarkMode({ handleDarkMode = new org.nativescript.widgets.Utils.HandleDarkMode({

View File

@@ -26,20 +26,21 @@ public abstract class LayoutBase extends ViewGroup {
private boolean passThroughParent; private boolean passThroughParent;
boolean applyingEdges; boolean applyingEdges;
public static final int OverflowEdgeIgnore = -1;
public static final int OverflowEdgeNone = 0; public static final int OverflowEdgeNone = 0;
public static final int OverflowEdgeLeft = 1; public static final int OverflowEdgeLeft = 1 << 1;
public static final int OverflowEdgeTop = 1 << 1; public static final int OverflowEdgeTop = 1 << 2;
public static final int OverflowEdgeRight = 1 << 2; public static final int OverflowEdgeRight = 1 << 3;
public static final int OverflowEdgeBottom = 1 << 3; public static final int OverflowEdgeBottom = 1 << 4;
public static final int OverflowEdgeDontApply = 1 << 4; public static final int OverflowEdgeDontApply = 1 << 5;
public static final int OverflowEdgeLeftDontConsume = 1 << 5; public static final int OverflowEdgeLeftDontConsume = 1 << 6;
public static final int OverflowEdgeTopDontConsume = 1 << 6; public static final int OverflowEdgeTopDontConsume = 1 << 7;
public static final int OverflowEdgeRightDontConsume = 1 << 7; public static final int OverflowEdgeRightDontConsume = 1 << 8;
public static final int OverflowEdgeBottomDontConsume = 1 << 8; public static final int OverflowEdgeBottomDontConsume = 1 << 9;
public static final int OverflowEdgeAllButLeft = 1 << 9; public static final int OverflowEdgeAllButLeft = 1 << 10;
public static final int OverflowEdgeAllButTop = 1 << 10; public static final int OverflowEdgeAllButTop = 1 << 11;
public static final int OverflowEdgeAllButRight = 1 << 11; public static final int OverflowEdgeAllButRight = 1 << 12;
public static final int OverflowEdgeAllButBottom = 1 << 12; public static final int OverflowEdgeAllButBottom = 1 << 13;
public static final class BufferOffset { public static final class BufferOffset {
public static final int INSET_LEFT = 0; public static final int INSET_LEFT = 0;
@@ -53,8 +54,6 @@ public abstract class LayoutBase extends ViewGroup {
public static final int INSET_BOTTOM_CONSUMED = 28; public static final int INSET_BOTTOM_CONSUMED = 28;
} }
;
int mPaddingLeft = 0; int mPaddingLeft = 0;
int mPaddingTop = 0; int mPaddingTop = 0;
int mPaddingRight = 0; int mPaddingRight = 0;
@@ -62,11 +61,12 @@ public abstract class LayoutBase extends ViewGroup {
Insets edgeInsets = Insets.NONE; Insets edgeInsets = Insets.NONE;
int overflowEdge = OverflowEdgeNone; int overflowEdge = OverflowEdgeIgnore;
private final ByteBuffer insetBuffer = ByteBuffer.allocateDirect(32); private final ByteBuffer insetBuffer = ByteBuffer.allocateDirect(32);
private WindowInsetListener insetListener = null; private WindowInsetListener insetListener = null;
private androidx.core.view.OnApplyWindowInsetsListener windowInsetsListener = null;
public void setInsetListener(@Nullable WindowInsetListener insetListener) { public void setInsetListener(@Nullable WindowInsetListener insetListener) {
this.insetListener = insetListener; this.insetListener = insetListener;
@@ -98,9 +98,116 @@ public abstract class LayoutBase extends ViewGroup {
public LayoutBase(Context context, AttributeSet attrs, int defStyleAttr) { public LayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
insetBuffer.order(ByteOrder.nativeOrder()); insetBuffer.order(ByteOrder.nativeOrder());
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
return super.onApplyWindowInsets(insets);
}
public LayoutBase(Context context) {
super(context);
}
public Insets getEdgeInsets() {
return edgeInsets;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(LayoutParams p) {
return p instanceof CommonLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams from) {
if (from instanceof CommonLayoutParams)
return new CommonLayoutParams((CommonLayoutParams) from);
if (from instanceof FrameLayout.LayoutParams)
return new CommonLayoutParams((FrameLayout.LayoutParams) from);
if (from instanceof ViewGroup.MarginLayoutParams)
return new CommonLayoutParams((ViewGroup.MarginLayoutParams) from);
return new CommonLayoutParams(from);
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!this.passThroughParent) {
return super.onTouchEvent(event);
}
// LayoutBase.onTouchEvent(ev) execution means no interactive child view handled
// the event so we let the event pass through to parent view of the layout container
// because passThroughParent is set to true
return false;
}
protected static int getGravity(View view) {
int gravity = -1;
LayoutParams params = view.getLayoutParams();
if (params instanceof FrameLayout.LayoutParams) {
gravity = ((FrameLayout.LayoutParams) params).gravity;
}
if (gravity == -1) {
gravity = Gravity.FILL;
}
return gravity;
}
public boolean getPassThroughParent() {
return this.passThroughParent;
}
public void setPassThroughParent(boolean value) {
this.passThroughParent = value;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (!applyingEdges) {
mPaddingLeft = left;
mPaddingTop = top;
mPaddingRight = right;
mPaddingBottom = bottom;
}
super.setPadding(left, top, right, bottom);
}
public void setOverflowEdge(int value) {
overflowEdge = value;
if (value == OverflowEdgeIgnore) {
ViewCompat.setOnApplyWindowInsetsListener(this, null);
ViewCompat.requestApplyInsets(this);
} else if (windowInsetsListener == null) {
// if incoming inset is empty and previous inset is empty return consumed // if incoming inset is empty and previous inset is empty return consumed
// an incoming empty inset is one way to detect a consumed inset e.g multiple views consumed top/bottom // an incoming empty inset is one way to detect a consumed inset e.g multiple views consumed top/bottom
androidx.core.view.OnApplyWindowInsetsListener windowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() { windowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() {
@NonNull @NonNull
@Override @Override
public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) {
@@ -110,6 +217,11 @@ public abstract class LayoutBase extends ViewGroup {
if (v instanceof LayoutBase) { if (v instanceof LayoutBase) {
LayoutBase base = (LayoutBase) v; LayoutBase base = (LayoutBase) v;
// should not occur but if it does return the inset
if (overflowEdge == OverflowEdgeIgnore) {
return insets;
}
Insets statusBar = insets.getInsets(WindowInsetsCompat.Type.statusBars()); Insets statusBar = insets.getInsets(WindowInsetsCompat.Type.statusBars());
Insets navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); Insets navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime()); Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());
@@ -293,107 +405,6 @@ public abstract class LayoutBase extends ViewGroup {
ViewCompat.setOnApplyWindowInsetsListener(this, windowInsetsListener); ViewCompat.setOnApplyWindowInsetsListener(this, windowInsetsListener);
} }
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
return super.onApplyWindowInsets(insets);
}
public LayoutBase(Context context) {
super(context);
}
public Insets getEdgeInsets() {
return edgeInsets;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CommonLayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(LayoutParams p) {
return p instanceof CommonLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams from) {
if (from instanceof CommonLayoutParams)
return new CommonLayoutParams((CommonLayoutParams) from);
if (from instanceof FrameLayout.LayoutParams)
return new CommonLayoutParams((FrameLayout.LayoutParams) from);
if (from instanceof ViewGroup.MarginLayoutParams)
return new CommonLayoutParams((ViewGroup.MarginLayoutParams) from);
return new CommonLayoutParams(from);
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!this.passThroughParent) {
return super.onTouchEvent(event);
}
// LayoutBase.onTouchEvent(ev) execution means no interactive child view handled
// the event so we let the event pass through to parent view of the layout container
// because passThroughParent is set to true
return false;
}
protected static int getGravity(View view) {
int gravity = -1;
LayoutParams params = view.getLayoutParams();
if (params instanceof FrameLayout.LayoutParams) {
gravity = ((FrameLayout.LayoutParams) params).gravity;
}
if (gravity == -1) {
gravity = Gravity.FILL;
}
return gravity;
}
public boolean getPassThroughParent() {
return this.passThroughParent;
}
public void setPassThroughParent(boolean value) {
this.passThroughParent = value;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (!applyingEdges) {
mPaddingLeft = left;
mPaddingTop = top;
mPaddingRight = right;
mPaddingBottom = bottom;
}
super.setPadding(left, top, right, bottom);
}
public void setOverflowEdge(int value) {
overflowEdge = value;
if (pendingInsetApply) { if (pendingInsetApply) {
return; return;
} }