|
|
@ -56,16 +56,22 @@ function readEdges(source: EdgeSource): ViewPaddings { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* فضای امن هر ضلع = بیشینهی viewInsets و safeArea همان ضلع. |
|
|
|
|
|
* منطبق بر منطق مستند: navigationBarHeight = max(insets, padding). |
|
|
|
|
|
* هیچ عدد ثابتی استفاده نمیشود؛ همهچیز از دیتای دریافتی محاسبه میشود. |
|
|
|
|
|
|
|
|
* مقادیر safe-area در `initial_config` (فیلد safeArea) از فلاتر در واحد منطقی |
|
|
|
|
|
* (CSS px / dp) میآیند و مستقیماً برای CSS درستاند. اما مسیر قدیمی |
|
|
|
|
|
* `get_view_paddings` مقادیر را در پیکسل فیزیکی (× devicePixelRatio) میفرستد؛ |
|
|
|
|
|
* این تابع آنها را به px منطقی برمیگرداند تا با CSS همخوان شوند. |
|
|
*/ |
|
|
*/ |
|
|
function mergeEdges(insets: ViewPaddings, safe: ViewPaddings): ViewPaddings { |
|
|
|
|
|
|
|
|
function toLogical(edges: ViewPaddings): ViewPaddings { |
|
|
|
|
|
const ratio = |
|
|
|
|
|
typeof window !== "undefined" && window.devicePixelRatio > 0 |
|
|
|
|
|
? window.devicePixelRatio |
|
|
|
|
|
: 1; |
|
|
|
|
|
|
|
|
return { |
|
|
return { |
|
|
top: Math.max(insets.top, safe.top), |
|
|
|
|
|
bottom: Math.max(insets.bottom, safe.bottom), |
|
|
|
|
|
left: Math.max(insets.left, safe.left), |
|
|
|
|
|
right: Math.max(insets.right, safe.right), |
|
|
|
|
|
|
|
|
top: edges.top / ratio, |
|
|
|
|
|
bottom: edges.bottom / ratio, |
|
|
|
|
|
left: edges.left / ratio, |
|
|
|
|
|
right: edges.right / ratio, |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -83,6 +89,7 @@ class ViewPaddingsBridge { |
|
|
private listeners: Array<(paddings: ViewPaddings) => void> = []; |
|
|
private listeners: Array<(paddings: ViewPaddings) => void> = []; |
|
|
private configListeners: Array<(config: InitialConfig) => void> = []; |
|
|
private configListeners: Array<(config: InitialConfig) => void> = []; |
|
|
private flutterUnsubscribe?: () => void; |
|
|
private flutterUnsubscribe?: () => void; |
|
|
|
|
|
private hasInitialConfig = false; |
|
|
|
|
|
|
|
|
constructor() { |
|
|
constructor() { |
|
|
this.init(); |
|
|
this.init(); |
|
|
@ -108,14 +115,34 @@ class ViewPaddingsBridge { |
|
|
this.flutterUnsubscribe = win.addFlutterResponseListener((event) => { |
|
|
this.flutterUnsubscribe = win.addFlutterResponseListener((event) => { |
|
|
if (!event || event.success === false || !event.data) return; |
|
|
if (!event || event.success === false || !event.data) return; |
|
|
|
|
|
|
|
|
if (event.action === "initial_config") { |
|
|
|
|
|
this.applyInitialConfig(event.data); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
switch (event.action) { |
|
|
|
|
|
case "initial_config": |
|
|
|
|
|
this.applyInitialConfig(event.data); |
|
|
|
|
|
this.hasInitialConfig = true; |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
// بهروزرسانی فضای امن هنگام چرخش/تغییر notch (px منطقی).
|
|
|
|
|
|
case "safe_area_changed": |
|
|
|
|
|
this.applySafeArea(readEdges(event.data)); |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
// ارتفاع کیبورد بهصورت زنده (px منطقی).
|
|
|
|
|
|
case "keyboard_changed": { |
|
|
|
|
|
const height = num(event.data.height); |
|
|
|
|
|
this.config.keyboardHeight = height; |
|
|
|
|
|
this.applyKeyboard(height); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// سازگاری عقبرو: پاسخ سادهی get_view_paddings (px فیزیکی، فقط لبهها).
|
|
|
|
|
|
// اگر initial_config رسیده باشد، آن مرجع است و این نادیده گرفته میشود.
|
|
|
|
|
|
case "get_view_paddings": |
|
|
|
|
|
if (this.hasInitialConfig) return; |
|
|
|
|
|
this.applyEdges(toLogical(readEdges(event.data))); |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
// سازگاری عقبرو: پاسخ سادهی get_view_paddings که فقط لبهها را دارد
|
|
|
|
|
|
if (event.action === "get_view_paddings") { |
|
|
|
|
|
this.applyEdges(readEdges(event.data)); |
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -144,13 +171,16 @@ class ViewPaddingsBridge { |
|
|
private applyInitialConfig(data: any) { |
|
|
private applyInitialConfig(data: any) { |
|
|
const viewInsets = readEdges(data?.viewInsets); |
|
|
const viewInsets = readEdges(data?.viewInsets); |
|
|
const safeArea = readEdges(data?.safeArea); |
|
|
const safeArea = readEdges(data?.safeArea); |
|
|
const paddings = mergeEdges(viewInsets, safeArea); |
|
|
|
|
|
|
|
|
// فیلد safeArea از فلاتر در px منطقی است و واحد درست برای CSS؛ همین مبنا
|
|
|
|
|
|
// برای --safe-* است. (فیلد viewInsets در این payload در px فیزیکی و عملاً
|
|
|
|
|
|
// تکرار همان viewPadding است، پس در محاسبهی فضای امن استفاده نمیشود.)
|
|
|
|
|
|
const paddings = safeArea; |
|
|
|
|
|
|
|
|
this.config = { |
|
|
this.config = { |
|
|
paddings, |
|
|
paddings, |
|
|
viewInsets, |
|
|
viewInsets, |
|
|
safeArea, |
|
|
safeArea, |
|
|
keyboardHeight: viewInsets.bottom, |
|
|
|
|
|
|
|
|
keyboardHeight: 0, |
|
|
layout: data?.layout |
|
|
layout: data?.layout |
|
|
? { |
|
|
? { |
|
|
breakpoint: String(data.layout.breakpoint ?? ""), |
|
|
breakpoint: String(data.layout.breakpoint ?? ""), |
|
|
@ -176,7 +206,14 @@ class ViewPaddingsBridge { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.applyEdges(paddings); |
|
|
this.applyEdges(paddings); |
|
|
this.applyKeyboard(viewInsets.bottom); |
|
|
|
|
|
|
|
|
this.applyKeyboard(0); |
|
|
|
|
|
this.notifyConfigListeners(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private applySafeArea(safeArea: ViewPaddings) { |
|
|
|
|
|
this.config.safeArea = safeArea; |
|
|
|
|
|
this.config.paddings = safeArea; |
|
|
|
|
|
this.applyEdges(safeArea); |
|
|
this.notifyConfigListeners(); |
|
|
this.notifyConfigListeners(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|