feat: adapt ToolApp size to screen
This commit is contained in:
@@ -418,9 +418,14 @@ FloatBallAppWM.prototype.addPanel = function(panel, x, y, which) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var panelLp0 = null;
|
||||||
|
try { panelLp0 = panel.getLayoutParams(); } catch (ePanelLp) { panelLp0 = null; }
|
||||||
|
var panelW = (panelLp0 && Number(panelLp0.width) > 0) ? Number(panelLp0.width) : android.view.WindowManager.LayoutParams.WRAP_CONTENT;
|
||||||
|
var panelH = (panelLp0 && Number(panelLp0.height) > 0) ? Number(panelLp0.height) : android.view.WindowManager.LayoutParams.WRAP_CONTENT;
|
||||||
|
|
||||||
var lp = new android.view.WindowManager.LayoutParams(
|
var lp = new android.view.WindowManager.LayoutParams(
|
||||||
android.view.WindowManager.LayoutParams.WRAP_CONTENT,
|
panelW,
|
||||||
android.view.WindowManager.LayoutParams.WRAP_CONTENT,
|
panelH,
|
||||||
android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||||
flags,
|
flags,
|
||||||
android.graphics.PixelFormat.TRANSLUCENT
|
android.graphics.PixelFormat.TRANSLUCENT
|
||||||
@@ -607,6 +612,65 @@ FloatBallAppWM.prototype.setToolAppContent = function(contentView) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FloatBallAppWM.prototype.calculateToolAppLayout = function(shell) {
|
||||||
|
var sw = Math.max(1, Number(this.state.screen && this.state.screen.w || 0));
|
||||||
|
var sh = Math.max(1, Number(this.state.screen && this.state.screen.h || 0));
|
||||||
|
if (sw <= 1 || sh <= 1) {
|
||||||
|
try { var ss = this.getScreenSizePx(); sw = ss.w; sh = ss.h; } catch (eScreen) {}
|
||||||
|
}
|
||||||
|
var shortSide = Math.min(sw, sh);
|
||||||
|
var longSide = Math.max(sw, sh);
|
||||||
|
var isLandscape = sw > sh;
|
||||||
|
|
||||||
|
var marginX = this.dp(12);
|
||||||
|
var marginTop = this.dp(14);
|
||||||
|
var marginBottom = this.dp(14);
|
||||||
|
var targetW;
|
||||||
|
var targetH;
|
||||||
|
|
||||||
|
if (shortSide < this.dp(420)) {
|
||||||
|
// 小屏/分屏:接近全屏,避免内容被裁剪。
|
||||||
|
marginX = this.dp(8);
|
||||||
|
marginTop = this.dp(8);
|
||||||
|
marginBottom = this.dp(8);
|
||||||
|
targetW = sw - marginX * 2;
|
||||||
|
targetH = sh - marginTop - marginBottom;
|
||||||
|
} else if (shortSide >= this.dp(720)) {
|
||||||
|
// 平板/大屏:不要铺满,保持卡片阅读宽度。
|
||||||
|
marginX = this.dp(28);
|
||||||
|
marginTop = this.dp(28);
|
||||||
|
marginBottom = this.dp(28);
|
||||||
|
targetW = Math.min(this.dp(680), Math.floor(sw * (isLandscape ? 0.56 : 0.72)));
|
||||||
|
targetH = Math.min(this.dp(780), sh - marginTop - marginBottom);
|
||||||
|
} else if (isLandscape) {
|
||||||
|
// 手机横屏:高度优先,宽度适当收窄。
|
||||||
|
marginX = this.dp(18);
|
||||||
|
marginTop = this.dp(12);
|
||||||
|
marginBottom = this.dp(12);
|
||||||
|
targetW = Math.min(this.dp(620), Math.floor(sw * 0.68));
|
||||||
|
targetH = sh - marginTop - marginBottom;
|
||||||
|
} else {
|
||||||
|
// 手机竖屏:左右留少量边距,高度随屏幕增长。
|
||||||
|
marginX = this.dp(12);
|
||||||
|
marginTop = this.dp(18);
|
||||||
|
marginBottom = this.dp(18);
|
||||||
|
targetW = Math.min(this.dp(560), sw - marginX * 2);
|
||||||
|
targetH = Math.min(Math.floor(sh * 0.90), sh - marginTop - marginBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetW = Math.max(this.dp(300), Math.min(targetW, sw - marginX * 2));
|
||||||
|
targetH = Math.max(this.dp(360), Math.min(targetH, sh - marginTop - marginBottom));
|
||||||
|
|
||||||
|
var x = Math.floor((sw - targetW) / 2);
|
||||||
|
var y = Math.floor((sh - targetH) / 2);
|
||||||
|
if (x < marginX) x = marginX;
|
||||||
|
if (y < marginTop) y = marginTop;
|
||||||
|
if (x + targetW > sw - marginX) x = Math.max(0, sw - marginX - targetW);
|
||||||
|
if (y + targetH > sh - marginBottom) y = Math.max(0, sh - marginBottom - targetH);
|
||||||
|
|
||||||
|
return { width: targetW, height: targetH, x: x, y: y, marginX: marginX, marginTop: marginTop, marginBottom: marginBottom, isLandscape: isLandscape, shortSide: shortSide, longSide: longSide };
|
||||||
|
};
|
||||||
|
|
||||||
FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
|
FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
|
||||||
if (this.state.closing) return;
|
if (this.state.closing) return;
|
||||||
var r = this.isToolAppRoute(route) ? String(route) : "settings";
|
var r = this.isToolAppRoute(route) ? String(route) : "settings";
|
||||||
@@ -635,27 +699,21 @@ FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
|
|||||||
this.updateToolAppShellChrome(this.getToolAppTitle(r), this.state.toolAppNavStack.length > 1);
|
this.updateToolAppShellChrome(this.getToolAppTitle(r), this.state.toolAppNavStack.length > 1);
|
||||||
this.setToolAppContent(raw);
|
this.setToolAppContent(raw);
|
||||||
|
|
||||||
var maxW = Math.floor(this.state.screen.w * 0.92);
|
var layout = this.calculateToolAppLayout(shell);
|
||||||
var maxH = Math.floor(this.state.screen.h * 0.82);
|
|
||||||
shell.measure(
|
|
||||||
android.view.View.MeasureSpec.makeMeasureSpec(maxW, android.view.View.MeasureSpec.AT_MOST),
|
|
||||||
android.view.View.MeasureSpec.makeMeasureSpec(maxH, android.view.View.MeasureSpec.AT_MOST)
|
|
||||||
);
|
|
||||||
var pw = Math.max(this.dp(300), Math.min(maxW, shell.getMeasuredWidth()));
|
|
||||||
var ph = Math.max(this.dp(380), Math.min(maxH, shell.getMeasuredHeight()));
|
|
||||||
var lp0 = shell.getLayoutParams();
|
var lp0 = shell.getLayoutParams();
|
||||||
if (!lp0) lp0 = new android.view.ViewGroup.LayoutParams(pw, ph);
|
if (!lp0) lp0 = new android.view.ViewGroup.LayoutParams(layout.width, layout.height);
|
||||||
lp0.width = pw; lp0.height = ph;
|
lp0.width = layout.width; lp0.height = layout.height;
|
||||||
shell.setLayoutParams(lp0);
|
shell.setLayoutParams(lp0);
|
||||||
|
|
||||||
if (!this.state.addedViewer || this.state.viewerPanel !== shell) {
|
if (!this.state.addedViewer || this.state.viewerPanel !== shell) {
|
||||||
var pos = this.getBestPanelPosition(pw, ph, this.state.ballLp.x, this.state.ballLp.y, this.getDockInfo().ballSize);
|
this.addPanel(shell, layout.x, layout.y, "tool_app");
|
||||||
this.addPanel(shell, pos.x, pos.y, "tool_app");
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
if (this.state.viewerPanelLp) {
|
if (this.state.viewerPanelLp) {
|
||||||
this.state.viewerPanelLp.width = pw;
|
this.state.viewerPanelLp.width = layout.width;
|
||||||
this.state.viewerPanelLp.height = ph;
|
this.state.viewerPanelLp.height = layout.height;
|
||||||
|
this.state.viewerPanelLp.x = layout.x;
|
||||||
|
this.state.viewerPanelLp.y = layout.y;
|
||||||
this.state.wm.updateViewLayout(shell, this.state.viewerPanelLp);
|
this.state.wm.updateViewLayout(shell, this.state.viewerPanelLp);
|
||||||
}
|
}
|
||||||
} catch (eUpd) { safeLog(this.L, 'w', "tool_app update layout fail: " + String(eUpd)); }
|
} catch (eUpd) { safeLog(this.L, 'w', "tool_app update layout fail: " + String(eUpd)); }
|
||||||
|
|||||||
@@ -58,8 +58,8 @@
|
|||||||
"size": 237123
|
"size": 237123
|
||||||
},
|
},
|
||||||
"th_15_extra.js": {
|
"th_15_extra.js": {
|
||||||
"sha256": "554092fb880e1f645e8665feac752b7412c3a9b604f960ad73f7fa889c8e39a8",
|
"sha256": "44d19f0012f4182b9f9831d4f5a747b43d3b726f98e0480e6c79f54eeff70a5e",
|
||||||
"size": 73786
|
"size": 76125
|
||||||
},
|
},
|
||||||
"th_16_entry.js": {
|
"th_16_entry.js": {
|
||||||
"sha256": "e7c99c3dfbd6aedab05551426955081ae6cae034754f2f557cefa01dc75dc001",
|
"sha256": "e7c99c3dfbd6aedab05551426955081ae6cae034754f2f557cefa01dc75dc001",
|
||||||
@@ -68,5 +68,5 @@
|
|||||||
},
|
},
|
||||||
"keyId": "toolhub-targets-2026-rsa3072",
|
"keyId": "toolhub-targets-2026-rsa3072",
|
||||||
"schema": 2,
|
"schema": 2,
|
||||||
"version": 20260512232713
|
"version": 20260512233123
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Y5kHcy+jDtyrB0AAvEgLdWkQr85CJ7UAvOtzDrp93yI2c+PAItrzWjh+inSskBv7UBa7bHIDXGHVPBN9O5qIPn2Zgb01baozxz+KqO4WomLdEKWTOrRhrla2rJAKf7hcrTLnI2WjHWgg2kMikypvm1S45vcwu1u4uVBEFwE9EExrvjmQiHShqWTJ8ejiMbV3AqQFKCA0QpLujPscedW8YFJOpTJarwKIwSTuAGJdLtKh7ixgzB46kcqkxb+bVdi0+kEblY+heYlTxP5p7F75n0X1aIOuRXYnuGf3ODQi+rioTHYRTQGoJ36SNzxgIHS0JyEDgeduJEhaYaYXLz7BaKFFsJ+2SWPyKS0rx+XW+j91Am9C+wCXKNcPa5/r1y1D/hxtEHSK/YEYVX7IYrCNx3xMJVfILzpCB9lFpEiHNjw0qZU/hjXnRzJhstkK7wrMt6kfvmX7feG34a3t8jlDa0H7Kc3BfkO1wn+wT/P/VurdiaEa5xPU6OPUnyb4fdHY
|
eHDpnB+6LIjkd8UjiiNeI6CUb0vK01Dc+HGNJRLJBbjiRCLMTHYiDulV9qDmMDrNPJy4fKr8Wmdz6Lz0AMkv9cbWEut+Fb7jZJXzwIt967XJsKwyQMdPb/N7wZ5oLvdrDxJXbp833FKlnsNahhcrg92Jw3z5qRgBYJQ5JV4CKPkMAXG6w80Y1d7LUhbJsBUtZwGOA2qMykefFVl1qEO67Ljm0pyeqaFqTKcTmmIn955Ni4f8q/gaaR+mz66WhKy/YUhXIYwXSIsW4eF9kkDe3N2NW77goDNKAn4E1Hg9qsObiWxb452zVwhdR/S8J3nk7vLO6N/FVf0+PxT24W5G+S/BDChTz3fwOsS2DUI2ZkygFn9leF/z8Nmivf2utwkgrZbJ/xkSirvYxZ2KzwRY7VSLM7ESPBBpx52EVK6HlVuZL7v+rlr/Yb+lzXvhgtg9s4btLJH9GGtdJVPGItLEBPYd9fi8J+Xupd6nGDHFVMZqULXoK+3a96FqsQr2pqKB
|
||||||
|
|||||||
35
scripts/verify_toolapp_adaptive_size.py
Normal file
35
scripts/verify_toolapp_adaptive_size.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
extra = (root / "code" / "th_15_extra.js").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
checks = []
|
||||||
|
|
||||||
|
def check(name, ok):
|
||||||
|
checks.append((name, bool(ok)))
|
||||||
|
|
||||||
|
check("has calculateToolAppLayout", "FloatBallAppWM.prototype.calculateToolAppLayout" in extra)
|
||||||
|
check("layout uses screen orientation", "isLandscape" in extra and "shortSide" in extra and "longSide" in extra)
|
||||||
|
check("layout has tiny screen branch", "shortSide < this.dp(420)" in extra)
|
||||||
|
check("layout has tablet/large screen branch", "shortSide >= this.dp(720)" in extra)
|
||||||
|
check("layout applies margins", "marginX" in extra and "marginTop" in extra and "marginBottom" in extra)
|
||||||
|
|
||||||
|
show_match = re.search(r"FloatBallAppWM\.prototype\.showToolApp\s*=\s*function\([^)]*\)\s*\{(?P<body>.*?)\n\};", extra, re.S)
|
||||||
|
show_body = show_match.group("body") if show_match else ""
|
||||||
|
check("showToolApp exists", bool(show_match))
|
||||||
|
check("showToolApp uses calculateToolAppLayout", "this.calculateToolAppLayout" in show_body)
|
||||||
|
check("showToolApp applies adaptive width", "layout.width" in show_body and "layout.height" in show_body)
|
||||||
|
check("showToolApp updates x/y when reusing root", "viewerPanelLp.x = layout.x" in show_body and "viewerPanelLp.y = layout.y" in show_body)
|
||||||
|
check("showToolApp addPanel uses adaptive x/y", "this.addPanel(shell, layout.x, layout.y" in show_body)
|
||||||
|
check("showToolApp no fixed 0.92/0.82 sizing", "0.92" not in show_body and "0.82" not in show_body)
|
||||||
|
|
||||||
|
failed = [name for name, ok in checks if not ok]
|
||||||
|
if failed:
|
||||||
|
print("ToolApp adaptive-size verification FAILED:")
|
||||||
|
for name in failed:
|
||||||
|
print(" - " + name)
|
||||||
|
sys.exit(1)
|
||||||
|
print("ToolApp adaptive-size verification OK")
|
||||||
Reference in New Issue
Block a user