11 Commits

Author SHA1 Message Date
7015725
cd3452af45 fix: disable color picker drag translation 2026-05-23 06:28:57 +08:00
7015725
39e62ddaa0 fix: reduce color picker swipe ghosting 2026-05-23 06:22:59 +08:00
7015725
9de9983b50 fix: close color picker on system navigation 2026-05-23 06:19:16 +08:00
7015725
0cc6439619 Add edge swipe dismiss for popup overlays 2026-05-23 06:08:24 +08:00
7015725
095f7ccb39 Pin color picker actions in popup footer 2026-05-23 06:00:44 +08:00
7015725
757a60d15d Align ToolApp back preview shell layout 2026-05-23 05:36:15 +08:00
7015725
d347bb190c Reduce ToolApp back visual displacement 2026-05-23 05:27:10 +08:00
7015725
27dd07da49 Keep back preview during ToolApp pop 2026-05-23 05:20:35 +08:00
7015725
a327ba2657 Avoid scroll restore flicker on ToolApp back 2026-05-23 05:16:59 +08:00
7015725
aa4ef421fe Move ToolApp header upward 2026-05-23 05:09:25 +08:00
7015725
00508c9cde Preserve ToolApp scroll position on back 2026-05-23 04:55:18 +08:00
5 changed files with 422 additions and 100 deletions

View File

@@ -109,6 +109,46 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
var isFollowTheme = !currentColor; var isFollowTheme = !currentColor;
var currentBaseRgbHex = extractTintRgbHex(currentColor); var currentBaseRgbHex = extractTintRgbHex(currentColor);
var currentAlphaByte = extractTintAlphaByte(currentColor); var currentAlphaByte = extractTintAlphaByte(currentColor);
var alphaSeek = null;
var alphaValTv = null;
var updatePreviewFn = function() {};
var updateValueTvFn = function() {};
var refreshRecentGridFn = function() {};
var refreshCommonGridFn = function() {};
var syncRgbSeeksFn = function() {};
// 操作按钮:对齐设置页/按钮管理页的 chip + 主按钮视觉。
function createColorPanelActionButton(label, primary, onClick) {
var b = new android.widget.TextView(context);
b.setText(label);
b.setGravity(android.view.Gravity.CENTER);
b.setSingleLine(true);
b.setTypeface(null, android.graphics.Typeface.BOLD);
try { b.setIncludeFontPadding(false); } catch(eFontPad) {}
if (primary) {
b.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
b.setTextColor(android.graphics.Color.WHITE);
b.setPadding(self.dp(18), 0, self.dp(18), 0);
try { b.setMinHeight(self.dp(52)); } catch(eMinH1) {}
try { b.setBackground(self.ui.createStrokeDrawable(T.primaryDeep, self.withAlpha(T.brown || T.primaryDeep, isDark ? 0.28 : 0.18), self.dp(1), self.dp(26))); } catch(eBg1) {}
try { b.setElevation(self.dp(1)); } catch(eElev) {}
} else {
b.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
b.setTextColor(T.brown || T.sub);
b.setPadding(self.dp(16), 0, self.dp(16), 0);
try { b.setMinHeight(self.dp(52)); } catch(eMinH2) {}
try { b.setBackground(self.ui.createStrokeDrawable(T.card2 || T.card, self.withAlpha(T.stroke || T.brown, isDark ? 0.42 : 0.55), self.dp(1), self.dp(26))); } catch(eBg2) {}
try { b.setElevation(self.dp(1)); } catch(eElev2) {}
}
try { b.setClickable(true); b.setFocusable(true); } catch(eClickable) {}
b.setOnClickListener(new android.view.View.OnClickListener({
onClick: function(v) {
self.touchActivity();
try { if (onClick) onClick(v); } catch(eBtn) { safeLog(self.L, 'e', "color panel action err=" + String(eBtn)); }
}
}));
return b;
}
var popupResult = self.showPopupOverlay({ var popupResult = self.showPopupOverlay({
title: "换颜色", title: "换颜色",
@@ -146,7 +186,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
try { try {
var parsed = android.graphics.Color.parseColor(selectedColor); var parsed = android.graphics.Color.parseColor(selectedColor);
dr.setColorFilter(parsed, android.graphics.PorterDuff.Mode.SRC_IN); dr.setColorFilter(parsed, android.graphics.PorterDuff.Mode.SRC_IN);
} catch(e) { safeLog(null, 'e', "catch " + String(e)); } } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} else { } else {
try { dr.clearColorFilter(); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } try { dr.clearColorFilter(); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} }
@@ -154,8 +194,9 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
} else { } else {
previewIv.setImageDrawable(null); previewIv.setImageDrawable(null);
} }
} catch(e) { safeLog(null, 'e', "catch " + String(e)); } } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} }
updatePreviewFn = updatePreview;
updatePreview(); updatePreview();
// ========== 最近使用颜色 ========== // ========== 最近使用颜色 ==========
@@ -239,6 +280,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
} }
} catch(e) { safeLog(null, 'e', "catch " + String(e)); } } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} }
refreshRecentGridFn = refreshRecentGrid;
refreshRecentGrid(); refreshRecentGrid();
// 21 色常用颜色 // 21 色常用颜色
@@ -328,6 +370,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
} }
} catch(e) { safeLog(null, 'e', "catch " + String(e)); } } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} }
refreshCommonGridFn = refreshCommonGrid;
// 颜色值显示 // 颜色值显示
var valueTv = new android.widget.TextView(context); var valueTv = new android.widget.TextView(context);
@@ -339,6 +382,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
function updateValueTv() { function updateValueTv() {
valueTv.setText(isFollowTheme ? "当前:跟随岛屿主题" : ("当前:" + (selectedColor || "无"))); valueTv.setText(isFollowTheme ? "当前:跟随岛屿主题" : ("当前:" + (selectedColor || "无")));
} }
updateValueTvFn = updateValueTv;
updateValueTv(); updateValueTv();
// RGB 滑块 // RGB 滑块
@@ -412,6 +456,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
rgbValTvs[2].setText(String(initB)); rgbValTvs[2].setText(String(initB));
} catch(e) { safeLog(null, 'e', "catch " + String(e)); } } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} }
syncRgbSeeksFn = syncRgbSeeks;
syncRgbSeeks(); syncRgbSeeks();
// 透明度滑块 // 透明度滑块
@@ -427,14 +472,14 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
alphaLabel.setMinWidth(self.dp(20)); alphaLabel.setMinWidth(self.dp(20));
alphaRow.addView(alphaLabel); alphaRow.addView(alphaLabel);
var alphaSeek = new android.widget.SeekBar(context); alphaSeek = new android.widget.SeekBar(context);
alphaSeek.setMax(255); alphaSeek.setMax(255);
var alphaSeekLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1); var alphaSeekLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1);
alphaSeekLp.setMargins(self.dp(8), 0, self.dp(8), 0); alphaSeekLp.setMargins(self.dp(8), 0, self.dp(8), 0);
alphaSeek.setLayoutParams(alphaSeekLp); alphaSeek.setLayoutParams(alphaSeekLp);
alphaRow.addView(alphaSeek); alphaRow.addView(alphaSeek);
var alphaValTv = new android.widget.TextView(context); alphaValTv = new android.widget.TextView(context);
alphaValTv.setTextColor(subTextColor); alphaValTv.setTextColor(subTextColor);
alphaValTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 11); alphaValTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 11);
alphaValTv.setMinWidth(self.dp(28)); alphaValTv.setMinWidth(self.dp(28));
@@ -460,59 +505,25 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
alphaValTv.setText(String(currentAlphaByte)); alphaValTv.setText(String(currentAlphaByte));
content.addView(alphaRow); content.addView(alphaRow);
// 操作按钮:对齐设置页/按钮管理页的 chip + 主按钮视觉 // 底部操作按钮放到 showPopupOverlay 的固定 footer 中,避免默认首屏看不到
function createColorPanelActionButton(label, primary, onClick) { },
var b = new android.widget.TextView(context); footerBuilder: function(footer, closePopup) {
b.setText(label);
b.setGravity(android.view.Gravity.CENTER);
b.setSingleLine(true);
b.setTypeface(null, android.graphics.Typeface.BOLD);
try { b.setIncludeFontPadding(false); } catch(eFontPad) {}
if (primary) {
// 与设置页底部“保存布置”一致主色胶囊、44dp 高、轻描边。
b.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
// 颜色面板里不用高饱和青色,改成更沉稳的深绿主按钮。
b.setTextColor(android.graphics.Color.WHITE);
b.setPadding(self.dp(18), 0, self.dp(18), 0);
try { b.setMinHeight(self.dp(52)); } catch(eMinH1) {}
try { b.setBackground(self.ui.createStrokeDrawable(T.primaryDeep, self.withAlpha(T.brown || T.primaryDeep, isDark ? 0.28 : 0.18), self.dp(1), self.dp(26))); } catch(eBg1) {}
try { b.setElevation(self.dp(1)); } catch(eElev) {}
} else {
// 与设置页按钮一致:次级也用大圆角按钮,不再做小 chip。
b.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
// 次级按钮走设置页里的奶油/描边感,不再用薄荷绿底。
b.setTextColor(T.brown || T.sub);
b.setPadding(self.dp(16), 0, self.dp(16), 0);
try { b.setMinHeight(self.dp(52)); } catch(eMinH2) {}
try { b.setBackground(self.ui.createStrokeDrawable(T.card2 || T.card, self.withAlpha(T.stroke || T.brown, isDark ? 0.42 : 0.55), self.dp(1), self.dp(26))); } catch(eBg2) {}
try { b.setElevation(self.dp(1)); } catch(eElev2) {}
}
try { b.setClickable(true); b.setFocusable(true); } catch(eClickable) {}
b.setOnClickListener(new android.view.View.OnClickListener({
onClick: function(v) {
self.touchActivity();
try { if (onClick) onClick(v); } catch(eBtn) { safeLog(self.L, 'e', "color panel action err=" + String(eBtn)); }
}
}));
return b;
}
var actionRow = new android.widget.LinearLayout(context); var actionRow = new android.widget.LinearLayout(context);
actionRow.setOrientation(android.widget.LinearLayout.HORIZONTAL); actionRow.setOrientation(android.widget.LinearLayout.HORIZONTAL);
actionRow.setGravity(android.view.Gravity.CENTER_VERTICAL); actionRow.setGravity(android.view.Gravity.CENTER_VERTICAL);
actionRow.setPadding(self.dp(10), self.dp(10), self.dp(10), self.dp(12)); actionRow.setPadding(self.dp(10), self.dp(6), self.dp(10), self.dp(4));
var btnClear = createColorPanelActionButton("恢复默认", false, function() { var btnClear = createColorPanelActionButton("恢复默认", false, function() {
isFollowTheme = true; isFollowTheme = true;
selectedColor = ""; selectedColor = "";
updatePreview();
updateValueTv();
refreshRecentGrid();
refreshCommonGrid();
syncRgbSeeks();
alphaSeek.setProgress(255);
alphaValTv.setText("255");
currentAlphaByte = 255; currentAlphaByte = 255;
try { updatePreviewFn(); } catch(eUp) {}
try { updateValueTvFn(); } catch(eVal) {}
try { refreshRecentGridFn(); } catch(eRecent) {}
try { refreshCommonGridFn(); } catch(eCommon) {}
try { syncRgbSeeksFn(); } catch(eSync) {}
try { if (alphaSeek) alphaSeek.setProgress(255); } catch(eAlphaSeek) {}
try { if (alphaValTv) alphaValTv.setText("255"); } catch(eAlphaTv) {}
}); });
var clearLp = new android.widget.LinearLayout.LayoutParams(0, self.dp(52)); var clearLp = new android.widget.LinearLayout.LayoutParams(0, self.dp(52));
clearLp.weight = 1; clearLp.weight = 1;
@@ -522,13 +533,9 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
var btnOk = createColorPanelActionButton("保存颜色", true, function() { var btnOk = createColorPanelActionButton("保存颜色", true, function() {
try { try {
var finalColor = isFollowTheme ? "" : String(selectedColor || ""); var finalColor = isFollowTheme ? "" : String(selectedColor || "");
if (!isFollowTheme && selectedColor) { if (!isFollowTheme && selectedColor) pushRecentColor(selectedColor);
pushRecentColor(selectedColor);
}
if (typeof onSelect === "function") { if (typeof onSelect === "function") {
try { onSelect(finalColor); } catch(eOnSelect) { try { onSelect(finalColor); } catch(eOnSelect) { safeLog(self.L, 'e', "colorPicker onSelect err=" + String(eOnSelect)); }
safeLog(self.L, 'e', "colorPicker onSelect err=" + String(eOnSelect));
}
} }
} catch(e) { } catch(e) {
safeLog(self.L, 'e', "colorPicker confirm err=" + String(e)); safeLog(self.L, 'e', "colorPicker confirm err=" + String(e));
@@ -540,7 +547,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
okLp.setMargins(self.dp(6), 0, 0, 0); okLp.setMargins(self.dp(6), 0, 0, 0);
actionRow.addView(btnOk, okLp); actionRow.addView(btnOk, okLp);
content.addView(actionRow, new android.widget.LinearLayout.LayoutParams( footer.addView(actionRow, new android.widget.LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
)); ));

View File

@@ -4592,6 +4592,7 @@ FloatBallAppWM.prototype.showPopupOverlay = function(opts) {
var title = String(opt.title || ""); var title = String(opt.title || "");
var onDismiss = (typeof opt.onDismiss === "function") ? opt.onDismiss : null; var onDismiss = (typeof opt.onDismiss === "function") ? opt.onDismiss : null;
var builder = (typeof opt.builder === "function") ? opt.builder : null; var builder = (typeof opt.builder === "function") ? opt.builder : null;
var footerBuilder = (typeof opt.footerBuilder === "function") ? opt.footerBuilder : null;
var PT = this.getIslandPickerTheme ? this.getIslandPickerTheme() : null; var PT = this.getIslandPickerTheme ? this.getIslandPickerTheme() : null;
var isDark = PT ? PT.isDark : this.isDarkTheme(); var isDark = PT ? PT.isDark : this.isDarkTheme();
@@ -4619,12 +4620,168 @@ FloatBallAppWM.prototype.showPopupOverlay = function(opts) {
if (panelHeight > sh - self.dp(48)) panelHeight = sh - self.dp(48); if (panelHeight > sh - self.dp(48)) panelHeight = sh - self.dp(48);
if (panelHeight < self.dp(420)) panelHeight = Math.min(sh - self.dp(24), self.dp(420)); if (panelHeight < self.dp(420)) panelHeight = Math.min(sh - self.dp(24), self.dp(420));
var root = new android.widget.FrameLayout(context); var popupClosed = false;
var popupBackDispatcher = null;
var popupBackCallback = null;
var root = new JavaAdapter(android.widget.FrameLayout, {
onWindowFocusChanged: function(hasFocus) {
try {
android.widget.FrameLayout.prototype.onWindowFocusChanged.call(this, hasFocus);
} catch(eSuperFocus) {}
try {
if (popupClosed) return;
// 用户底部上滑回主页 / 进入后台时overlay 往往会失去焦点;弹窗应自动关闭。
if (!hasFocus) this.post(new java.lang.Runnable({ run: function() { closePopup(); } }));
} catch(eFocusClose) {}
},
onWindowVisibilityChanged: function(visibility) {
try {
android.widget.FrameLayout.prototype.onWindowVisibilityChanged.call(this, visibility);
} catch(eSuperVis) {}
try {
if (popupClosed) return;
if (visibility !== android.view.View.VISIBLE) this.post(new java.lang.Runnable({ run: function() { closePopup(); } }));
} catch(eVisClose) {}
},
onDetachedFromWindow: function() {
try { popupClosed = true; } catch(eMarkDetached) {}
try {
android.widget.FrameLayout.prototype.onDetachedFromWindow.call(this);
} catch(eSuperDetach) {}
}
}, context);
root.setBackgroundColor(self.withAlpha(isDark ? 0xFF000000 : 0xFFFFFFFF, isDark ? 0.58 : 0.42)); root.setBackgroundColor(self.withAlpha(isDark ? 0xFF000000 : 0xFFFFFFFF, isDark ? 0.58 : 0.42));
root.setClickable(true); root.setClickable(true);
try { root.setFocusable(true); root.setFocusableInTouchMode(true); } catch(eRootFocus) {} try { root.setFocusable(true); root.setFocusableInTouchMode(true); } catch(eRootFocus) {}
try {
root.setOnKeyListener(new android.view.View.OnKeyListener({
onKey: function(v, keyCode, event) {
try {
if (keyCode === android.view.KeyEvent.KEYCODE_BACK && event && event.getAction && event.getAction() === android.view.KeyEvent.ACTION_UP) {
closePopup();
return true;
}
} catch(eKey) {}
return false;
}
}));
} catch(eRootKey) {}
var card = new android.widget.LinearLayout(context); var popupBackDownX = 0;
var popupBackDownY = 0;
var popupBackEdge = -1;
var popupBackEligible = false;
var popupBackActive = false;
var popupBackMoved = false;
var popupBackVisualActive = false;
function beginPopupBackVisual(v) {
try {
if (popupBackVisualActive || !v) return;
popupBackVisualActive = true;
// ColorOS overlay 上拖动带 elevation 的圆角卡片容易留下阴影/拖影;拖动期间临时去掉阴影。
try { v.animate().cancel(); } catch(eAnimCancel) {}
try { v.setAlpha(1); v.setScaleX(1); v.setScaleY(1); } catch(eResetVisual) {}
try { v.setElevation(0); } catch(eElev0) {}
try { v.setLayerType(android.view.View.LAYER_TYPE_NONE, null); } catch(eLayer0) {}
try { v.invalidate(); root.invalidate(); } catch(eInv0) {}
} catch(eBeginVisual) {}
}
function endPopupBackVisual(v) {
try {
popupBackVisualActive = false;
if (!v) return;
try { v.setAlpha(1); v.setScaleX(1); v.setScaleY(1); } catch(eResetVisual2) {}
try { v.setElevation(self.dp(10)); } catch(eElevRestore) {}
try { v.setLayerType(android.view.View.LAYER_TYPE_NONE, null); } catch(eLayerRestore) {}
try { v.invalidate(); root.invalidate(); } catch(eInvRestore) {}
} catch(eEndVisual) {}
}
var card = new JavaAdapter(android.widget.LinearLayout, {
onInterceptTouchEvent: function(ev) {
try {
if (!ev) return false;
var action = ev.getActionMasked();
if (action === android.view.MotionEvent.ACTION_DOWN) {
popupBackDownX = ev.getX();
popupBackDownY = ev.getY();
popupBackEdge = -1;
popupBackEligible = false;
popupBackActive = false;
popupBackMoved = false;
var edgeW = self.dp(56);
var cw = 0;
try { cw = this.getWidth(); } catch(eW) { cw = 0; }
if (popupBackDownX <= edgeW) { popupBackEdge = 0; popupBackEligible = true; }
else if (cw > 0 && popupBackDownX >= cw - edgeW) { popupBackEdge = 1; popupBackEligible = true; }
return false;
}
if (action === android.view.MotionEvent.ACTION_MOVE) {
if (!popupBackEligible) return false;
var dx = ev.getX() - popupBackDownX;
var dy = ev.getY() - popupBackDownY;
var adx = Math.abs(dx);
var ady = Math.abs(dy);
var validDir = (popupBackEdge === 0 && dx > 0) || (popupBackEdge === 1 && dx < 0);
var slop = Math.max(self.dp(8), self.dp(Number(self.config.CLICK_SLOP_DP || 6)));
if (validDir && adx > slop && adx > ady * 0.9) {
popupBackActive = true;
popupBackMoved = true;
beginPopupBackVisual(this);
// 只识别滑动关闭手势不再跟手平移卡片ColorOS overlay 平移会留下上一帧拖影。
try { this.setTranslationX(0); this.setAlpha(1); } catch(eTx) {}
return true;
}
}
} catch(eIntercept) { try { safeLog(self.L, 'w', 'popup back intercept fail: ' + String(eIntercept)); } catch(eLog) {} }
return false;
},
onTouchEvent: function(ev) {
try {
if (!ev || !popupBackActive) return false;
var action = ev.getActionMasked();
if (action === android.view.MotionEvent.ACTION_MOVE) {
var mx = ev.getX() - popupBackDownX;
var my = ev.getY() - popupBackDownY;
var validDir2 = (popupBackEdge === 0 && mx > 0) || (popupBackEdge === 1 && mx < 0);
if (validDir2 && Math.abs(mx) > Math.abs(my) * 0.9) {
popupBackMoved = true;
// 手势过程中保持卡片固定,避免 overlay translation 产生拖影/重复影像。
try { this.setTranslationX(0); this.setAlpha(1); } catch(eMoveReset) {}
}
return true;
}
if (action === android.view.MotionEvent.ACTION_UP || action === android.view.MotionEvent.ACTION_CANCEL) {
var ux = ev.getX() - popupBackDownX;
var uy = ev.getY() - popupBackDownY;
var okDir = (popupBackEdge === 0 && ux > self.dp(72)) || (popupBackEdge === 1 && ux < -self.dp(72));
var ok = action === android.view.MotionEvent.ACTION_UP && popupBackMoved && okDir && Math.abs(ux) > Math.abs(uy) * 0.9;
var dir = popupBackEdge === 1 ? -1 : 1;
popupBackActive = false;
popupBackEligible = false;
popupBackMoved = false;
popupBackEdge = -1;
if (ok) {
try { this.setTranslationX(0); this.setAlpha(1); } catch(eOkReset) {}
endPopupBackVisual(this);
closePopup();
} else {
try { this.setTranslationX(0); this.setAlpha(1); } catch(eCancelReset) {}
endPopupBackVisual(this);
}
return true;
}
return true;
} catch(eTouch) {
popupBackActive = false;
popupBackEligible = false;
popupBackMoved = false;
popupBackEdge = -1;
try { this.setTranslationX(0); this.setAlpha(1); endPopupBackVisual(this); } catch(eReset) {}
try { safeLog(self.L, 'w', 'popup back touch fail: ' + String(eTouch)); } catch(eLog2) {}
}
return false;
}
}, context);
card.setOrientation(android.widget.LinearLayout.VERTICAL); card.setOrientation(android.widget.LinearLayout.VERTICAL);
var cardLp = new android.widget.FrameLayout.LayoutParams(panelWidth, panelHeight); var cardLp = new android.widget.FrameLayout.LayoutParams(panelWidth, panelHeight);
cardLp.gravity = android.view.Gravity.CENTER; cardLp.gravity = android.view.Gravity.CENTER;
@@ -4674,6 +4831,15 @@ FloatBallAppWM.prototype.showPopupOverlay = function(opts) {
)); ));
card.addView(scroll); card.addView(scroll);
var footer = new android.widget.LinearLayout(context);
footer.setOrientation(android.widget.LinearLayout.VERTICAL);
footer.setVisibility(android.view.View.GONE);
try { footer.setPadding(0, self.dp(6), 0, 0); } catch(eFooterPad) {}
card.addView(footer, new android.widget.LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
));
root.addView(card); root.addView(card);
root.setOnClickListener(new android.view.View.OnClickListener({ root.setOnClickListener(new android.view.View.OnClickListener({
@@ -4698,8 +4864,35 @@ FloatBallAppWM.prototype.showPopupOverlay = function(opts) {
| android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN; | android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
try { wm.addView(root, lp); } catch(eAdd) { safeLog(self.L, 'e', "popup addView fail: " + String(eAdd)); return null; } try { wm.addView(root, lp); } catch(eAdd) { safeLog(self.L, 'e', "popup addView fail: " + String(eAdd)); return null; }
try {
root.requestFocus();
root.post(new java.lang.Runnable({ run: function() {
try {
if (popupClosed) return;
var dispatcher = null;
try { dispatcher = root.findOnBackInvokedDispatcher(); } catch(eFindBack) { dispatcher = null; }
if (!dispatcher) return;
var cbCls = java.lang.Class.forName("android.window.OnBackInvokedCallback");
var cb = new JavaAdapter(cbCls, { onBackInvoked: function() { closePopup(); } });
var priority = 0;
try { priority = android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT; } catch(ePri) { priority = 0; }
dispatcher.registerOnBackInvokedCallback(priority, cb);
popupBackDispatcher = dispatcher;
popupBackCallback = cb;
} catch(eRegPopupBack) {
try { safeLog(self.L, 'w', 'popup back callback register fail: ' + String(eRegPopupBack)); } catch(eLogReg) {}
}
}}));
} catch(eReqFocus) {}
function closePopup() { function closePopup() {
if (popupClosed) return;
popupClosed = true;
try {
if (popupBackDispatcher && popupBackCallback) popupBackDispatcher.unregisterOnBackInvokedCallback(popupBackCallback);
} catch(eUnregPopupBack) {}
popupBackDispatcher = null;
popupBackCallback = null;
try { wm.removeView(root); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } try { wm.removeView(root); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
if (typeof onDismiss === "function") { if (typeof onDismiss === "function") {
try { onDismiss(); } catch(eD) { safeLog(null, 'e', "catch " + String(eD)); } try { onDismiss(); } catch(eD) { safeLog(null, 'e', "catch " + String(eD)); }
@@ -4709,8 +4902,14 @@ FloatBallAppWM.prototype.showPopupOverlay = function(opts) {
if (typeof builder === "function") { if (typeof builder === "function") {
try { builder(content, closePopup); } catch(eB) { safeLog(self.L, 'e', "popup builder fail: " + String(eB)); } try { builder(content, closePopup); } catch(eB) { safeLog(self.L, 'e', "popup builder fail: " + String(eB)); }
} }
if (typeof footerBuilder === "function") {
try {
footerBuilder(footer, closePopup);
if (footer.getChildCount && footer.getChildCount() > 0) footer.setVisibility(android.view.View.VISIBLE);
} catch(eF) { safeLog(self.L, 'e', "popup footer builder fail: " + String(eF)); }
}
return { close: closePopup, content: content }; return { close: closePopup, content: content, footer: footer };
}; };
// 兼容旧入口:旧 ToolHub.js 不会自动加载新增 th_14_color_picker.js // 兼容旧入口:旧 ToolHub.js 不会自动加载新增 th_14_color_picker.js

View File

@@ -559,6 +559,7 @@ FloatBallAppWM.prototype.closeToolApp = function() {
this.state.toolAppBackPreviewEntryKey = null; this.state.toolAppBackPreviewEntryKey = null;
this.state.toolAppTitleView = null; this.state.toolAppTitleView = null;
this.state.toolAppBackButton = null; this.state.toolAppBackButton = null;
this.state.toolAppScrollY = 0;
} catch (e) { safeLog(this.L, 'e', "closeToolApp fail: " + String(e)); } } catch (e) { safeLog(this.L, 'e', "closeToolApp fail: " + String(e)); }
}; };
@@ -662,7 +663,7 @@ FloatBallAppWM.prototype.bumpToolAppStackVersion = function() {
v = v + 1; v = v + 1;
if (v > 1000000000) v = 1; if (v > 1000000000) v = 1;
this.state.toolAppNavStackVersion = v; this.state.toolAppNavStackVersion = v;
this.clearToolAppBackPreview(false); if (!this.state.keepToolAppBackPreviewDuringPop) this.clearToolAppBackPreview(false);
return v; return v;
} catch(e) {} } catch(e) {}
return 0; return 0;
@@ -673,9 +674,94 @@ FloatBallAppWM.prototype.getToolAppSnapshotKey = function(entry) {
return String(entry && entry.route || ""); return String(entry && entry.route || "");
}; };
FloatBallAppWM.prototype.findToolAppFirstScrollView = function(root) {
try {
if (!root) return null;
if (root instanceof android.widget.ScrollView) return root;
if (!root.getChildCount) return null;
var count = root.getChildCount();
for (var i = 0; i < count; i++) {
var found = this.findToolAppFirstScrollView(root.getChildAt(i));
if (found) return found;
}
} catch(e) {}
return null;
};
FloatBallAppWM.prototype.captureToolAppCurrentScrollY = function() {
try {
var host = this.state ? this.state.toolAppContentHost : null;
var sv = this.findToolAppFirstScrollView ? this.findToolAppFirstScrollView(host) : null;
if (!sv) return null;
var y = Number(sv.getScrollY ? sv.getScrollY() : 0);
if (isNaN(y) || y < 0) y = 0;
return Math.floor(y);
} catch(e) {}
return null;
};
FloatBallAppWM.prototype.saveToolAppCurrentStackScroll = function() {
try {
var st = this.state.toolAppNavStack;
if (!st || !st.length) return false;
var y = this.captureToolAppCurrentScrollY ? this.captureToolAppCurrentScrollY() : null;
if (y === null || y === undefined) return false;
st[st.length - 1].toolAppScrollY = y;
this.state.toolAppScrollY = y;
return true;
} catch(e) {}
return false;
};
FloatBallAppWM.prototype.restoreToolAppScrollLater = function(root, entry, hideUntilRestored) {
try {
if (!root) return false;
var y = 0;
if (entry && entry.toolAppScrollY !== undefined && entry.toolAppScrollY !== null) y = Number(entry.toolAppScrollY);
else if (this.state && this.state.toolAppScrollY !== undefined && this.state.toolAppScrollY !== null) y = Number(this.state.toolAppScrollY);
if (isNaN(y) || y < 0) y = 0;
y = Math.floor(y);
if (y <= 0) return false;
var self = this;
var shouldHide = !!hideUntilRestored;
if (shouldHide) {
try { root.setAlpha(0.01); } catch(eHide) {}
}
root.post(new java.lang.Runnable({ run: function() {
try {
var sv = self.findToolAppFirstScrollView ? self.findToolAppFirstScrollView(root) : null;
if (sv) sv.scrollTo(0, y);
} catch(ePost) {}
try {
root.post(new java.lang.Runnable({ run: function() {
try {
var sv2 = self.findToolAppFirstScrollView ? self.findToolAppFirstScrollView(root) : null;
if (sv2) sv2.scrollTo(0, y);
} catch(ePost2) {}
if (shouldHide) {
try { root.setAlpha(1.0); } catch(eShow) {}
}
}}));
} catch(ePostAgain) {
if (shouldHide) {
try { root.setAlpha(1.0); } catch(eShow2) {}
}
}
}}));
return true;
} catch(e) {
try { if (root) root.setAlpha(1.0); } catch(eRestoreAlpha) {}
}
return false;
};
FloatBallAppWM.prototype.captureToolAppPageSnapshot = function(route) { FloatBallAppWM.prototype.captureToolAppPageSnapshot = function(route) {
var r = this.isToolAppRoute(route) ? String(route) : (this.isToolAppRoute(this.state.toolAppRoute) ? String(this.state.toolAppRoute) : "settings"); var r = this.isToolAppRoute(route) ? String(route) : (this.isToolAppRoute(this.state.toolAppRoute) ? String(this.state.toolAppRoute) : "settings");
var s = this.state || {}; var s = this.state || {};
var liveScrollY = null;
try {
if (String(s.toolAppRoute || "") === r && this.captureToolAppCurrentScrollY) liveScrollY = this.captureToolAppCurrentScrollY();
} catch(eLiveScroll) { liveScrollY = null; }
var entry = { var entry = {
route: r, route: r,
settingsGroupKey: (s.settingsGroupKey !== undefined && s.settingsGroupKey !== null) ? String(s.settingsGroupKey) : "", settingsGroupKey: (s.settingsGroupKey !== undefined && s.settingsGroupKey !== null) ? String(s.settingsGroupKey) : "",
@@ -689,7 +775,8 @@ FloatBallAppWM.prototype.captureToolAppPageSnapshot = function(route) {
toolAppSubPage: (s.toolAppSubPage !== undefined) ? s.toolAppSubPage : null, toolAppSubPage: (s.toolAppSubPage !== undefined) ? s.toolAppSubPage : null,
toolAppSubKey: (s.toolAppSubKey !== undefined) ? s.toolAppSubKey : null, toolAppSubKey: (s.toolAppSubKey !== undefined) ? s.toolAppSubKey : null,
toolAppSubPayload: (s.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(s.toolAppSubPayload, 0) : null, toolAppSubPayload: (s.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(s.toolAppSubPayload, 0) : null,
toolAppPayload: (s.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(s.toolAppPayload, 0) : null toolAppPayload: (s.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(s.toolAppPayload, 0) : null,
toolAppScrollY: (String(s.toolAppRoute || "") === r) ? ((liveScrollY !== null && liveScrollY !== undefined) ? liveScrollY : ((s.toolAppScrollY !== undefined) ? s.toolAppScrollY : 0)) : 0
}; };
if (r !== "settings_group") entry.settingsGroupKey = ""; if (r !== "settings_group") entry.settingsGroupKey = "";
if (r !== "settings") { entry.settingsHomeSelectedItemId = null; entry.settingsHomeSelectedCategoryId = null; } if (r !== "settings") { entry.settingsHomeSelectedItemId = null; entry.settingsHomeSelectedCategoryId = null; }
@@ -719,6 +806,8 @@ FloatBallAppWM.prototype.applyToolAppPageSnapshot = function(entry) {
this.state.toolAppSubKey = (entry.toolAppSubKey !== undefined) ? entry.toolAppSubKey : null; this.state.toolAppSubKey = (entry.toolAppSubKey !== undefined) ? entry.toolAppSubKey : null;
this.state.toolAppSubPayload = (entry.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppSubPayload, 0) : null; this.state.toolAppSubPayload = (entry.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppSubPayload, 0) : null;
this.state.toolAppPayload = (entry.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppPayload, 0) : null; this.state.toolAppPayload = (entry.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppPayload, 0) : null;
this.state.toolAppScrollY = (entry.toolAppScrollY !== undefined && entry.toolAppScrollY !== null) ? Number(entry.toolAppScrollY) : 0;
if (isNaN(this.state.toolAppScrollY) || this.state.toolAppScrollY < 0) this.state.toolAppScrollY = 0;
return true; return true;
} catch(e) { safeLog(this.L, 'w', "apply tool app snapshot fail: " + String(e)); } } catch(e) { safeLog(this.L, 'w', "apply tool app snapshot fail: " + String(e)); }
return false; return false;
@@ -739,7 +828,8 @@ FloatBallAppWM.prototype.cloneToolAppPageSnapshot = function(entry) {
toolAppSubPage: (entry.toolAppSubPage !== undefined) ? entry.toolAppSubPage : null, toolAppSubPage: (entry.toolAppSubPage !== undefined) ? entry.toolAppSubPage : null,
toolAppSubKey: (entry.toolAppSubKey !== undefined) ? entry.toolAppSubKey : null, toolAppSubKey: (entry.toolAppSubKey !== undefined) ? entry.toolAppSubKey : null,
toolAppSubPayload: (entry.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppSubPayload, 0) : null, toolAppSubPayload: (entry.toolAppSubPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppSubPayload, 0) : null,
toolAppPayload: (entry.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppPayload, 0) : null toolAppPayload: (entry.toolAppPayload !== undefined) ? this.cloneToolAppSnapshotValue(entry.toolAppPayload, 0) : null,
toolAppScrollY: (entry.toolAppScrollY !== undefined && entry.toolAppScrollY !== null) ? Number(entry.toolAppScrollY) : 0
}; };
}; };
@@ -762,6 +852,9 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) {
} catch(eSpec) { } catch(eSpec) {
spec = null; spec = null;
} }
var shellPad = spec ? spec.shellPadding : this.dp(6);
var shellTopPad = shellPad;
var outerRadius = spec ? spec.outerRadius : this.dp(26);
var topBarHeight = spec ? spec.topBarHeight : this.dp(56); var topBarHeight = spec ? spec.topBarHeight : this.dp(56);
var isDark = this.isDarkTheme(); var isDark = this.isDarkTheme();
@@ -772,8 +865,8 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) {
var body = new android.widget.LinearLayout(context); var body = new android.widget.LinearLayout(context);
body.setOrientation(android.widget.LinearLayout.VERTICAL); body.setOrientation(android.widget.LinearLayout.VERTICAL);
body.setPadding(this.dp(6), this.dp(6), this.dp(6), this.dp(8)); body.setPadding(shellPad, shellTopPad, shellPad, shellPad);
body.setBackground(this.ui.createStrokeDrawable(T.bg, this.withAlpha(T.stroke, isDark ? 0.42 : 0.70), this.dp(1), this.dp(26))); body.setBackground(this.ui.createStrokeDrawable(T.bg, this.withAlpha(T.stroke, isDark ? 0.30 : 0.46), this.dp(1), outerRadius));
try { body.setClipToOutline(true); } catch(eClip) {} try { body.setClipToOutline(true); } catch(eClip) {}
try { body.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 7 : 10)); } catch (eElev) {} try { body.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 7 : 10)); } catch (eElev) {}
@@ -827,7 +920,7 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) {
try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {} try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {}
bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38))); bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38)));
var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight); var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight);
barLp.setMargins(this.dp(8), this.dp(8), this.dp(8), this.dp(4)); barLp.setMargins(this.dp(8), this.dp(2), this.dp(8), this.dp(4));
body.addView(bar, barLp); body.addView(bar, barLp);
var host = new android.widget.FrameLayout(context); var host = new android.widget.FrameLayout(context);
@@ -835,6 +928,7 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) {
try { raw.setBackground(null); } catch (eBg) {} try { raw.setBackground(null); } catch (eBg) {}
try { raw.setElevation(0); } catch (eEl) {} try { raw.setElevation(0); } catch (eEl) {}
host.addView(raw, new android.widget.FrameLayout.LayoutParams(-1, -1)); host.addView(raw, new android.widget.FrameLayout.LayoutParams(-1, -1));
try { if (this.restoreToolAppScrollLater) this.restoreToolAppScrollLater(raw, entry); } catch(ePreviewScroll) {}
var hostLp = new android.widget.LinearLayout.LayoutParams(-1, 0, 1); var hostLp = new android.widget.LinearLayout.LayoutParams(-1, 0, 1);
hostLp.setMargins((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), 0, (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6)); hostLp.setMargins((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), 0, (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6));
body.addView(host, hostLp); body.addView(host, hostLp);
@@ -1020,10 +1114,9 @@ FloatBallAppWM.prototype.applyToolAppBackPreviewProgress = function(edge, progre
if (body) { if (body) {
try { body.animate().cancel(); } catch(eCancelBody) {} try { body.animate().cancel(); } catch(eCancelBody) {}
body.setTranslationX(bodyMove); body.setTranslationX(bodyMove);
body.setAlpha(1.0 - 0.10 * eased); body.setAlpha(1.0);
var s = 1.0 - 0.015 * eased; body.setScaleX(1.0);
body.setScaleX(s); body.setScaleY(1.0);
body.setScaleY(s);
} }
try { try {
@@ -1045,11 +1138,10 @@ FloatBallAppWM.prototype.applyToolAppBackPreviewProgress = function(edge, progre
} }
} catch(eMoveLog) {} } catch(eMoveLog) {}
if (prev) { if (prev) {
prev.setAlpha(0.88 + 0.12 * eased); prev.setAlpha(1.0);
prev.setTranslationX(-dir * this.dp(24) * (1.0 - eased)); prev.setTranslationX(0);
var ps = 0.975 + 0.025 * eased; prev.setScaleX(1.0);
prev.setScaleX(ps); prev.setScaleY(1.0);
prev.setScaleY(ps);
} }
return true; return true;
} catch (e) { safeLog(this.L, 'w', "apply tool app back preview fail: " + String(e)); } } catch (e) { safeLog(this.L, 'w', "apply tool app back preview fail: " + String(e)); }
@@ -1071,28 +1163,47 @@ FloatBallAppWM.prototype.finishToolAppBackPreview = function(edge, complete) {
try { w = Number((this.state.toolAppRoot && this.state.toolAppRoot.getWidth && this.state.toolAppRoot.getWidth()) || 0); } catch (eW1) {} try { w = Number((this.state.toolAppRoot && this.state.toolAppRoot.getWidth && this.state.toolAppRoot.getWidth()) || 0); } catch (eW1) {}
} }
if (!w || w < this.dp(120)) w = this.dp(320); if (!w || w < this.dp(120)) w = this.dp(320);
try { if (prev) prev.animate().translationX(0).alpha(1).scaleX(1).scaleY(1).setDuration(180).setInterpolator(decel).start(); } catch(ePrev) {} try { if (prev) prev.animate().translationX(0).alpha(1).scaleX(1).scaleY(1).setDuration(120).setInterpolator(decel).start(); } catch(ePrev) {}
body.animate().translationX(dir * w).alpha(0.90).scaleX(0.985).scaleY(0.985).setDuration(180).setInterpolator(decel).withEndAction(new java.lang.Runnable({ body.animate().translationX(dir * w).alpha(1).scaleX(1).scaleY(1).setDuration(160).setInterpolator(decel).withEndAction(new java.lang.Runnable({
run: function() { run: function() {
try { self.resetToolAppBackWindowFollow(); } catch(eResetFollow) {} try { self.resetToolAppBackWindowFollow(); } catch(eResetFollow) {}
try { try { self.state.keepToolAppBackPreviewDuringPop = true; } catch(eKeepPreview) {}
if (self.state.toolAppRoot) self.state.toolAppRoot.setTranslationX(0);
if (self.state.toolAppBody) {
self.state.toolAppBody.setTranslationX(0);
self.state.toolAppBody.setAlpha(1);
self.state.toolAppBody.setScaleX(1);
self.state.toolAppBody.setScaleY(1);
}
} catch(eResetView) {}
try { self.clearToolAppBackPreview(true); } catch (eClear) {}
try { self.popToolAppPage("edge_swipe_back"); } catch (ePop) {} try { self.popToolAppPage("edge_swipe_back"); } catch (ePop) {}
try { self.state.keepToolAppBackPreviewDuringPop = false; } catch(eKeepPreview2) {}
try {
var rootAfter = self.state.toolAppRoot;
if (rootAfter) {
rootAfter.post(new java.lang.Runnable({ run: function() {
try {
rootAfter.post(new java.lang.Runnable({ run: function() {
try {
if (self.state.toolAppRoot) self.state.toolAppRoot.setTranslationX(0);
if (self.state.toolAppBody) {
self.state.toolAppBody.setTranslationX(0);
self.state.toolAppBody.setAlpha(1);
self.state.toolAppBody.setScaleX(1);
self.state.toolAppBody.setScaleY(1);
}
} catch(eResetViewLater) {}
try { self.clearToolAppBackPreview(true); } catch (eClearLater) {}
}}));
} catch(ePost2) {
try { self.clearToolAppBackPreview(true); } catch (eClearPostFail) {}
}
}}));
} else {
try { self.clearToolAppBackPreview(true); } catch (eClearNoRoot) {}
}
} catch(eLater) {
try { self.clearToolAppBackPreview(true); } catch (eClearLaterFail) {}
}
} }
})).start(); })).start();
return; return;
} }
if (body) { if (body) {
var cancelInterp = new android.view.animation.AccelerateDecelerateInterpolator(); var cancelInterp = new android.view.animation.AccelerateDecelerateInterpolator();
try { if (prev) prev.animate().translationX(-dir * self.dp(24)).alpha(0.88).scaleX(0.975).scaleY(0.975).setDuration(200).setInterpolator(cancelInterp).start(); } catch(ePrev2) {} try { if (prev) prev.animate().translationX(0).alpha(1).scaleX(1).scaleY(1).setDuration(160).setInterpolator(cancelInterp).start(); } catch(ePrev2) {}
body.animate().translationX(0).alpha(1).scaleX(1).scaleY(1).setDuration(200).setInterpolator(cancelInterp).withEndAction(new java.lang.Runnable({ body.animate().translationX(0).alpha(1).scaleX(1).scaleY(1).setDuration(200).setInterpolator(cancelInterp).withEndAction(new java.lang.Runnable({
run: function() { run: function() {
try { self.resetToolAppBackWindowFollow(); } catch(eResetFollow2) {} try { self.resetToolAppBackWindowFollow(); } catch(eResetFollow2) {}
@@ -1262,10 +1373,8 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
var spec = this.getToolAppResponsiveSpec ? this.getToolAppResponsiveSpec() : null; var spec = this.getToolAppResponsiveSpec ? this.getToolAppResponsiveSpec() : null;
var shellPad = spec ? spec.shellPadding : this.dp(6); var shellPad = spec ? spec.shellPadding : this.dp(6);
var shellTopPad = shellPad; var shellTopPad = shellPad;
try { // ToolApp 是 WindowManager 浮层卡片,不需要额外叠加状态栏 inset
var topInset = this.getToolAppStatusBarInsetPx ? this.getToolAppStatusBarInsetPx() : 0; // 否则一级/二级页面标题上方会出现一块空白。
if (!isNaN(topInset) && topInset > 0) shellTopPad = shellPad + topInset;
} catch(eTopInset) {}
var outerRadius = spec ? spec.outerRadius : this.dp(26); var outerRadius = spec ? spec.outerRadius : this.dp(26);
var topBarHeight = spec ? spec.topBarHeight : this.dp(56); var topBarHeight = spec ? spec.topBarHeight : this.dp(56);
var rootDownX = 0; var rootDownX = 0;
@@ -1474,7 +1583,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {} try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {}
bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38))); bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38)));
var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight); var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight);
barLp.setMargins(this.dp(8), this.dp(8), this.dp(8), this.dp(4)); barLp.setMargins(this.dp(8), this.dp(2), this.dp(8), this.dp(4));
body.addView(bar, barLp); body.addView(bar, barLp);
var host = new android.widget.FrameLayout(context); var host = new android.widget.FrameLayout(context);
@@ -1536,6 +1645,11 @@ FloatBallAppWM.prototype.setToolAppContent = function(contentView) {
try { contentView.setBackground(null); } catch(eBg) { safeLog(null, 'e', "catch " + String(eBg)); } try { contentView.setBackground(null); } catch(eBg) { safeLog(null, 'e', "catch " + String(eBg)); }
try { contentView.setElevation(0); } catch(eEl) { safeLog(null, 'e', "catch " + String(eEl)); } try { contentView.setElevation(0); } catch(eEl) { safeLog(null, 'e', "catch " + String(eEl)); }
host.addView(contentView, new android.widget.FrameLayout.LayoutParams(-1, -1)); host.addView(contentView, new android.widget.FrameLayout.LayoutParams(-1, -1));
try {
var st = this.state.toolAppNavStack || [];
var top = st.length ? st[st.length - 1] : null;
if (this.restoreToolAppScrollLater) this.restoreToolAppScrollLater(contentView, top, true);
} catch(eRestoreScroll) {}
return true; return true;
} catch (e) { } catch (e) {
safeLog(this.L, 'e', "setToolAppContent fail: " + String(e)); safeLog(this.L, 'e', "setToolAppContent fail: " + String(e));
@@ -1617,6 +1731,7 @@ FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
this.state.pendingUserCfg = null; this.state.pendingUserCfg = null;
this.state.pendingDirty = false; this.state.pendingDirty = false;
this.state.previewMode = false; this.state.previewMode = false;
this.state.toolAppScrollY = 0;
} }
if (resetStack || !this.state.toolAppNavStack || !this.state.toolAppNavStack.length) { if (resetStack || !this.state.toolAppNavStack || !this.state.toolAppNavStack.length) {
this.state.toolAppNavStack = [this.makeToolAppStackEntry(r)]; this.state.toolAppNavStack = [this.makeToolAppStackEntry(r)];
@@ -1658,6 +1773,7 @@ FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
FloatBallAppWM.prototype.pushToolAppPage = function(route) { FloatBallAppWM.prototype.pushToolAppPage = function(route) {
if (!this.isToolAppRoute(route)) return; if (!this.isToolAppRoute(route)) return;
try { if (this.saveToolAppCurrentStackScroll) this.saveToolAppCurrentStackScroll(); } catch(eSaveScrollPush) {}
if (!this.state.toolAppNavStack) this.state.toolAppNavStack = []; if (!this.state.toolAppNavStack) this.state.toolAppNavStack = [];
if (this.state.toolAppNavStack.length <= 0) { if (this.state.toolAppNavStack.length <= 0) {
this.state.toolAppNavStack.push(this.makeToolAppStackEntry(this.state.toolAppRoute || "settings")); this.state.toolAppNavStack.push(this.makeToolAppStackEntry(this.state.toolAppRoute || "settings"));

View File

@@ -54,24 +54,24 @@
"size": 22308 "size": 22308
}, },
"th_14_color_picker.js": { "th_14_color_picker.js": {
"sha256": "d3ca89146e31e611e17d703ae9834d82794321e651b6ef156ade006227d231e7", "sha256": "2f6378fbb718d4b31cf11d3047cee6b32af93cfbb932442da29dd05225881432",
"size": 23587 "size": 23870
}, },
"th_14_icon_picker.js": { "th_14_icon_picker.js": {
"sha256": "1df5c4bf82e68de24d7b89c852a4d9878768451effe086e8ddb86620ecc98c35", "sha256": "1df5c4bf82e68de24d7b89c852a4d9878768451effe086e8ddb86620ecc98c35",
"size": 23906 "size": 23906
}, },
"th_14_panels.js": { "th_14_panels.js": {
"sha256": "1af01633720865a83a10a1e5261e3738d91be310195b014e17e579893eae90ee", "sha256": "33ed77006c3857a9eb93a72363a557ad87c425531e9d9d88eebc4bc8b4b28065",
"size": 267207 "size": 276124
}, },
"th_14_schema_editor.js": { "th_14_schema_editor.js": {
"sha256": "5669d0b5a16f770bed24eedee24203df57f7cbc7910c840931e533adac1ef146", "sha256": "5669d0b5a16f770bed24eedee24203df57f7cbc7910c840931e533adac1ef146",
"size": 20484 "size": 20484
}, },
"th_15_extra.js": { "th_15_extra.js": {
"sha256": "5b39db203b748f9d9919b6e19fc9fae5b5b5dcce6f46ade0f8e5cd2729f223aa", "sha256": "2c4e560b793ee54f9338cc44bd41a056cbf8688560f9494920322c88d04be219",
"size": 123635 "size": 128697
}, },
"th_16_entry.js": { "th_16_entry.js": {
"sha256": "652aa70214a9419923785e528a067d3828094fde48fc9c8c57cfda1e08206e25", "sha256": "652aa70214a9419923785e528a067d3828094fde48fc9c8c57cfda1e08206e25",
@@ -80,5 +80,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260522191219 "version": 20260522222851
} }

View File

@@ -1 +1 @@
c7CEARIR2JCXq4UkAGC9hQ2L7q9fM1CbSAZY5RazdoIijLBSNuXEmEgYVmlJNuJ4+hfU5vTCXgU8AkTL/NG4Q17fm27uFqQLkzqwb/9stQHvs3I4By9fWm11w9pcr17620vHe8alqjLslmLijUy0GSKy3r+nVQKFYWmveem5tdZNm6dXxbMG+2ePuPkc+CP02/vOQz9kx8C2eCFBjlcS+x/lOlpVYWXAqGGV7SseymOLe4RkGozz84p3fl8snFNOjA9F7CThAdEbd/UrC6UAM4VhVoaRuwHblWyxjTiRL4+OluIHWRnFDMpB7R3u/kp+mnon0v5gNlN4QIIVo4/jgVbWirsRAzw5+UM6Zft7T1P/j8MXAv8BrQzmMElU71vzReWyEmWvu6BjUi88R55IlZ8Fu5xjm6ttXCW0fB2miZ0Je52E3PdYKGzLMULtv2M07X71oSdqjd60g25R1ulGph6izuWKwxnabcSRLnvjgdhGl2qJUvMAn0LjCeARPFNC Wt5bWVBi/YnfXYbb1pEQNWYaD0nbZ6QqW1NKp791UCjKV+Qlen6Tt9NNDYCDYwITODZAp3FPJfAzVkKawUX/ROh9u81UExKBvXGJJ5JQT9PwbrbP1JJ3oPV3CDFUTfwMCc7ev2c5R6hL+29CXEksRYXH+bqGW9fTvOAYeHsa8rRZTPXyAvH2f36XTuc7wXHvKpwLjmPZt68Ep4ICEJFi+wHZPS3r2qljsYFUmbSRCRPuv/E4pqVIz33vWaF3gepQgeNVAjnJ8c2dmQhjKiZ0wIOmOQqwNbDXjNYZNUrXoaPzJV/6OIzJRK8MyDWQbiWBwZwm3CJCZKfdI1qR1TGScInWOBBGhJ7IDilElGTHd6wwQsw4Y4h6KBqLaWT5bzxp2x+vRTXYYdc6RGR0yaauWVTqCCE2zf4Z6IfRNht/2+BZ4VG4W7WhEN72zdbPihfsJcxPUjQklN5gp3PScEI9PwymlecsDHOHmDSYFER3TUai3Uds7DQiTYjVxSTpce0f