Revert "fix: edge handling"

This reverts commit a25a89146b.
This commit is contained in:
Osei Fortune
2025-11-04 18:25:48 -04:00
parent a25a89146b
commit b7d1929b7c
7 changed files with 511 additions and 371 deletions

View File

@@ -7,10 +7,6 @@ import { makeValidator, makeParser } from '../ui/core/properties';
import { CubicBezierAnimationCurve } from '../ui/animation/animation-interfaces';
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';
/**

View File

Binary file not shown.

View File

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

View File

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

View File

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

View File

@@ -26,21 +26,20 @@ public abstract class LayoutBase extends ViewGroup {
private boolean passThroughParent;
boolean applyingEdges;
public static final int OverflowEdgeIgnore = -1;
public static final int OverflowEdgeNone = 0;
public static final int OverflowEdgeLeft = 1 << 1;
public static final int OverflowEdgeTop = 1 << 2;
public static final int OverflowEdgeRight = 1 << 3;
public static final int OverflowEdgeBottom = 1 << 4;
public static final int OverflowEdgeDontApply = 1 << 5;
public static final int OverflowEdgeLeftDontConsume = 1 << 6;
public static final int OverflowEdgeTopDontConsume = 1 << 7;
public static final int OverflowEdgeRightDontConsume = 1 << 8;
public static final int OverflowEdgeBottomDontConsume = 1 << 9;
public static final int OverflowEdgeAllButLeft = 1 << 10;
public static final int OverflowEdgeAllButTop = 1 << 11;
public static final int OverflowEdgeAllButRight = 1 << 12;
public static final int OverflowEdgeAllButBottom = 1 << 13;
public static final int OverflowEdgeLeft = 1;
public static final int OverflowEdgeTop = 1 << 1;
public static final int OverflowEdgeRight = 1 << 2;
public static final int OverflowEdgeBottom = 1 << 3;
public static final int OverflowEdgeDontApply = 1 << 4;
public static final int OverflowEdgeLeftDontConsume = 1 << 5;
public static final int OverflowEdgeTopDontConsume = 1 << 6;
public static final int OverflowEdgeRightDontConsume = 1 << 7;
public static final int OverflowEdgeBottomDontConsume = 1 << 8;
public static final int OverflowEdgeAllButLeft = 1 << 9;
public static final int OverflowEdgeAllButTop = 1 << 10;
public static final int OverflowEdgeAllButRight = 1 << 11;
public static final int OverflowEdgeAllButBottom = 1 << 12;
public static final class BufferOffset {
public static final int INSET_LEFT = 0;
@@ -54,6 +53,8 @@ public abstract class LayoutBase extends ViewGroup {
public static final int INSET_BOTTOM_CONSUMED = 28;
}
;
int mPaddingLeft = 0;
int mPaddingTop = 0;
int mPaddingRight = 0;
@@ -61,12 +62,11 @@ public abstract class LayoutBase extends ViewGroup {
Insets edgeInsets = Insets.NONE;
int overflowEdge = OverflowEdgeIgnore;
int overflowEdge = OverflowEdgeNone;
private final ByteBuffer insetBuffer = ByteBuffer.allocateDirect(32);
private WindowInsetListener insetListener = null;
private androidx.core.view.OnApplyWindowInsetsListener windowInsetsListener = null;
public void setInsetListener(@Nullable WindowInsetListener insetListener) {
this.insetListener = insetListener;
@@ -98,6 +98,199 @@ public abstract class LayoutBase extends ViewGroup {
public LayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
insetBuffer.order(ByteOrder.nativeOrder());
// 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
androidx.core.view.OnApplyWindowInsetsListener windowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() {
@NonNull
@Override
public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) {
if (insets.isConsumed()) {
return insets;
}
if (v instanceof LayoutBase) {
LayoutBase base = (LayoutBase) v;
Insets statusBar = insets.getInsets(WindowInsetsCompat.Type.statusBars());
Insets navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());
int insetLeft = navBar.left;
int insetRight = navBar.right;
int insetBottom = Math.max(navBar.bottom, ime.bottom);
insetBuffer.put(EMPTY_INSETS, 0, 32);
insetBuffer.rewind();
if (overflowEdge == OverflowEdgeNone) {
base.applyingEdges = true;
v.setPadding(mPaddingLeft + insetLeft, mPaddingTop + statusBar.top, mPaddingRight + insetRight, mPaddingBottom + insetBottom);
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
base.applyingEdges = false;
return WindowInsetsCompat.CONSUMED;
}
if (base.insetListener != null) {
if (overflowEdge == OverflowEdgeDontApply) {
// 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
if (Insets.NONE.equals(statusBar) && Insets.NONE.equals(navBar) && Insets.NONE.equals(ime) && Insets.NONE.equals(edgeInsets)) {
return WindowInsetsCompat.CONSUMED;
}
IntBuffer insetData = insetBuffer.asIntBuffer();
boolean leftPreviouslyConsumed = insetLeft == 0;
boolean topPreviouslyConsumed = statusBar.top == 0;
boolean rightPreviouslyConsumed = insetRight == 0;
boolean bottomPreviouslyConsumed = insetBottom == 0;
insetData.put(0, insetLeft).put(1, statusBar.top).put(2, insetRight).put(3, insetBottom).put(4, leftPreviouslyConsumed ? 1 : 0).put(5, topPreviouslyConsumed ? 1 : 0).put(6, rightPreviouslyConsumed ? 1 : 0).put(7, bottomPreviouslyConsumed ? 1 : 0);
base.insetListener.onApplyWindowInsets(insetBuffer);
int leftInset = insetData.get(0);
int topInset = insetData.get(1);
int rightInset = insetData.get(2);
int bottomInset = insetData.get(3);
boolean leftConsumed = insetData.get(4) > 0;
boolean topConsumed = insetData.get(5) > 0;
boolean rightConsumed = insetData.get(6) > 0;
boolean bottomConsumed = insetData.get(7) > 0;
if (leftConsumed && topConsumed && rightConsumed && bottomConsumed) {
edgeInsets = Insets.of(leftInset, topInset, rightInset, bottomInset);
base.setPadding(leftInset, topInset, rightInset, bottomInset);
return new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE).build();
}
base.setPadding(leftPreviouslyConsumed ? 0 : leftInset, topPreviouslyConsumed ? 0 : topInset, rightPreviouslyConsumed ? 0 : rightInset, bottomPreviouslyConsumed ? 0 : bottomInset);
// restore inset edge if not consumed
if (!(leftPreviouslyConsumed || leftConsumed)) {
leftInset = insetLeft;
}
if (!(topPreviouslyConsumed || topConsumed)) {
topInset = statusBar.top;
}
if (!(rightPreviouslyConsumed || rightConsumed)) {
rightInset = insetRight;
}
if (!(bottomPreviouslyConsumed || bottomConsumed)) {
bottomInset = insetBottom;
}
edgeInsets = Insets.of(leftPreviouslyConsumed ? 0 : leftInset, topPreviouslyConsumed ? 0 : topInset, rightPreviouslyConsumed ? 0 : rightInset, bottomPreviouslyConsumed ? 0 : bottomInset);
return new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), Insets.of(leftPreviouslyConsumed || leftConsumed ? 0 : leftInset, topPreviouslyConsumed || topConsumed ? 0 : topInset, rightPreviouslyConsumed || rightConsumed ? 0 : rightInset, bottomPreviouslyConsumed || bottomConsumed ? 0 : bottomInset)).build();
}
}
boolean overflowLeftConsume = (overflowEdge & OverflowEdgeLeft) == OverflowEdgeLeft;
boolean overflowTopConsume = (overflowEdge & OverflowEdgeTop) == OverflowEdgeTop;
boolean overflowRightConsume = (overflowEdge & OverflowEdgeRight) == OverflowEdgeRight;
boolean overflowBottomConsume = (overflowEdge & OverflowEdgeBottom) == OverflowEdgeBottom;
boolean overflowLeft = (overflowEdge & OverflowEdgeLeftDontConsume) == OverflowEdgeLeftDontConsume;
boolean overflowTop = (overflowEdge & OverflowEdgeTopDontConsume) == OverflowEdgeTopDontConsume;
boolean overflowRight = (overflowEdge & OverflowEdgeRightDontConsume) == OverflowEdgeRightDontConsume;
boolean overflowBottom = (overflowEdge & OverflowEdgeBottomDontConsume) == OverflowEdgeBottomDontConsume;
boolean overflowAllButLeft = (overflowEdge & OverflowEdgeAllButLeft) == OverflowEdgeAllButLeft;
boolean overflowAllButTop = (overflowEdge & OverflowEdgeAllButTop) == OverflowEdgeAllButTop;
boolean overflowAllButRight = (overflowEdge & OverflowEdgeAllButRight) == OverflowEdgeAllButRight;
boolean overflowAllButBottom = (overflowEdge & OverflowEdgeAllButBottom) == OverflowEdgeAllButBottom;
WindowInsetsCompat ret = insets;
base.applyingEdges = true;
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
if (overflowAllButLeft || overflowAllButTop || overflowAllButRight || overflowAllButBottom) {
Insets newInset;
if (overflowAllButLeft) {
left = mPaddingLeft + insetLeft;
edgeInsets = Insets.of(insetLeft, 0, 0, 0);
newInset = Insets.of(0, statusBar.top, insetRight, insetBottom);
} else if (overflowAllButTop) {
top = mPaddingTop + statusBar.top;
edgeInsets = Insets.of(0, statusBar.top, 0, 0);
newInset = Insets.of(insetLeft, 0, insetRight, insetBottom);
} else if (overflowAllButRight) {
right = mPaddingRight + insetRight;
edgeInsets = Insets.of(0, 0, insetRight, 0);
newInset = Insets.of(insetLeft, statusBar.top, 0, insetBottom);
} else {
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(0, 0, 0, insetBottom);
newInset = Insets.of(insetLeft, statusBar.top, insetRight, 0);
}
ret = new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), newInset).build();
base.setPadding(left, top, right, bottom);
base.applyingEdges = false;
if (newInset == Insets.NONE) {
return WindowInsetsCompat.CONSUMED;
}
return ret;
}
if (overflowLeftConsume || overflowLeft) {
top = mPaddingTop + statusBar.top;
right = mPaddingRight + insetRight;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowRightConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowTopConsume || overflowTop) {
left = mPaddingLeft + insetLeft;
right = mPaddingRight + insetRight;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowTopConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowRightConsume || overflowRight) {
left = mPaddingLeft + insetLeft;
top = mPaddingTop + statusBar.top;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowRightConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowBottomConsume || overflowBottom) {
left = mPaddingLeft + insetLeft;
top = mPaddingTop + statusBar.top;
right = mPaddingRight + insetRight;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowBottomConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
base.setPadding(left, top, right, bottom);
base.applyingEdges = false;
return ret;
}
return insets;
}
};
ViewCompat.setOnApplyWindowInsetsListener(this, windowInsetsListener);
}
@Override
@@ -107,6 +300,7 @@ public abstract class LayoutBase extends ViewGroup {
public LayoutBase(Context context) {
super(context);
}
public Insets getEdgeInsets() {
@@ -200,210 +394,6 @@ public abstract class LayoutBase extends ViewGroup {
public void setOverflowEdge(int value) {
overflowEdge = value;
if (value == OverflowEdgeIgnore) {
ViewCompat.setOnApplyWindowInsetsListener(this, null);
} else if (windowInsetsListener == null) {
// 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
windowInsetsListener = new androidx.core.view.OnApplyWindowInsetsListener() {
@NonNull
@Override
public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) {
if (insets.isConsumed()) {
return insets;
}
if (v instanceof LayoutBase) {
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 navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());
int insetLeft = navBar.left;
int insetRight = navBar.right;
int insetBottom = Math.max(navBar.bottom, ime.bottom);
insetBuffer.put(EMPTY_INSETS, 0, 32);
insetBuffer.rewind();
if (overflowEdge == OverflowEdgeNone) {
base.applyingEdges = true;
v.setPadding(mPaddingLeft + insetLeft, mPaddingTop + statusBar.top, mPaddingRight + insetRight, mPaddingBottom + insetBottom);
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
base.applyingEdges = false;
return WindowInsetsCompat.CONSUMED;
}
if (base.insetListener != null) {
if (overflowEdge == OverflowEdgeDontApply) {
// 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
if (Insets.NONE.equals(statusBar) && Insets.NONE.equals(navBar) && Insets.NONE.equals(ime) && Insets.NONE.equals(edgeInsets)) {
return WindowInsetsCompat.CONSUMED;
}
IntBuffer insetData = insetBuffer.asIntBuffer();
boolean leftPreviouslyConsumed = insetLeft == 0;
boolean topPreviouslyConsumed = statusBar.top == 0;
boolean rightPreviouslyConsumed = insetRight == 0;
boolean bottomPreviouslyConsumed = insetBottom == 0;
insetData.put(0, insetLeft).put(1, statusBar.top).put(2, insetRight).put(3, insetBottom).put(4, leftPreviouslyConsumed ? 1 : 0).put(5, topPreviouslyConsumed ? 1 : 0).put(6, rightPreviouslyConsumed ? 1 : 0).put(7, bottomPreviouslyConsumed ? 1 : 0);
base.insetListener.onApplyWindowInsets(insetBuffer);
int leftInset = insetData.get(0);
int topInset = insetData.get(1);
int rightInset = insetData.get(2);
int bottomInset = insetData.get(3);
boolean leftConsumed = insetData.get(4) > 0;
boolean topConsumed = insetData.get(5) > 0;
boolean rightConsumed = insetData.get(6) > 0;
boolean bottomConsumed = insetData.get(7) > 0;
if (leftConsumed && topConsumed && rightConsumed && bottomConsumed) {
edgeInsets = Insets.of(leftInset, topInset, rightInset, bottomInset);
base.setPadding(leftInset, topInset, rightInset, bottomInset);
return new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE).build();
}
base.setPadding(leftPreviouslyConsumed ? 0 : leftInset, topPreviouslyConsumed ? 0 : topInset, rightPreviouslyConsumed ? 0 : rightInset, bottomPreviouslyConsumed ? 0 : bottomInset);
// restore inset edge if not consumed
if (!(leftPreviouslyConsumed || leftConsumed)) {
leftInset = insetLeft;
}
if (!(topPreviouslyConsumed || topConsumed)) {
topInset = statusBar.top;
}
if (!(rightPreviouslyConsumed || rightConsumed)) {
rightInset = insetRight;
}
if (!(bottomPreviouslyConsumed || bottomConsumed)) {
bottomInset = insetBottom;
}
edgeInsets = Insets.of(leftPreviouslyConsumed ? 0 : leftInset, topPreviouslyConsumed ? 0 : topInset, rightPreviouslyConsumed ? 0 : rightInset, bottomPreviouslyConsumed ? 0 : bottomInset);
return new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), Insets.of(leftPreviouslyConsumed || leftConsumed ? 0 : leftInset, topPreviouslyConsumed || topConsumed ? 0 : topInset, rightPreviouslyConsumed || rightConsumed ? 0 : rightInset, bottomPreviouslyConsumed || bottomConsumed ? 0 : bottomInset)).build();
}
}
boolean overflowLeftConsume = (overflowEdge & OverflowEdgeLeft) == OverflowEdgeLeft;
boolean overflowTopConsume = (overflowEdge & OverflowEdgeTop) == OverflowEdgeTop;
boolean overflowRightConsume = (overflowEdge & OverflowEdgeRight) == OverflowEdgeRight;
boolean overflowBottomConsume = (overflowEdge & OverflowEdgeBottom) == OverflowEdgeBottom;
boolean overflowLeft = (overflowEdge & OverflowEdgeLeftDontConsume) == OverflowEdgeLeftDontConsume;
boolean overflowTop = (overflowEdge & OverflowEdgeTopDontConsume) == OverflowEdgeTopDontConsume;
boolean overflowRight = (overflowEdge & OverflowEdgeRightDontConsume) == OverflowEdgeRightDontConsume;
boolean overflowBottom = (overflowEdge & OverflowEdgeBottomDontConsume) == OverflowEdgeBottomDontConsume;
boolean overflowAllButLeft = (overflowEdge & OverflowEdgeAllButLeft) == OverflowEdgeAllButLeft;
boolean overflowAllButTop = (overflowEdge & OverflowEdgeAllButTop) == OverflowEdgeAllButTop;
boolean overflowAllButRight = (overflowEdge & OverflowEdgeAllButRight) == OverflowEdgeAllButRight;
boolean overflowAllButBottom = (overflowEdge & OverflowEdgeAllButBottom) == OverflowEdgeAllButBottom;
WindowInsetsCompat ret = insets;
base.applyingEdges = true;
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
if (overflowAllButLeft || overflowAllButTop || overflowAllButRight || overflowAllButBottom) {
Insets newInset;
if (overflowAllButLeft) {
left = mPaddingLeft + insetLeft;
edgeInsets = Insets.of(insetLeft, 0, 0, 0);
newInset = Insets.of(0, statusBar.top, insetRight, insetBottom);
} else if (overflowAllButTop) {
top = mPaddingTop + statusBar.top;
edgeInsets = Insets.of(0, statusBar.top, 0, 0);
newInset = Insets.of(insetLeft, 0, insetRight, insetBottom);
} else if (overflowAllButRight) {
right = mPaddingRight + insetRight;
edgeInsets = Insets.of(0, 0, insetRight, 0);
newInset = Insets.of(insetLeft, statusBar.top, 0, insetBottom);
} else {
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(0, 0, 0, insetBottom);
newInset = Insets.of(insetLeft, statusBar.top, insetRight, 0);
}
ret = new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars(), newInset).build();
base.setPadding(left, top, right, bottom);
base.applyingEdges = false;
if (newInset == Insets.NONE) {
return WindowInsetsCompat.CONSUMED;
}
return ret;
}
if (overflowLeftConsume || overflowLeft) {
top = mPaddingTop + statusBar.top;
right = mPaddingRight + insetRight;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowRightConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowTopConsume || overflowTop) {
left = mPaddingLeft + insetLeft;
right = mPaddingRight + insetRight;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowTopConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowRightConsume || overflowRight) {
left = mPaddingLeft + insetLeft;
top = mPaddingTop + statusBar.top;
bottom = mPaddingBottom + insetBottom;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowRightConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
if (overflowBottomConsume || overflowBottom) {
left = mPaddingLeft + insetLeft;
top = mPaddingTop + statusBar.top;
right = mPaddingRight + insetRight;
edgeInsets = Insets.of(insetLeft, statusBar.top, insetRight, insetBottom);
if (overflowBottomConsume) {
ret = WindowInsetsCompat.CONSUMED;
}
}
base.setPadding(left, top, right, bottom);
base.applyingEdges = false;
return ret;
}
return insets;
}
};
ViewCompat.setOnApplyWindowInsetsListener(this, windowInsetsListener);
}
if (pendingInsetApply) {
return;
}