feat: 弹出式颜色选择器 + 图标预览旁颜色入口
- showColorPickerPopup: 新增独立弹窗颜色选择器,支持最近使用颜色(8个)、RGB滑块实时调色、常用色网格、透明度滑块 - 图标预览区实时反馏,确定后自动保存到最近列表 - 在 ShortX 图标预览卡片旁新增“颜色”按钮,直接打开颜色选择器 - 更新 README 文档 - 修复调色板卡片未加入 form 的问题
This commit is contained in:
30
README.md
30
README.md
@@ -130,7 +130,6 @@ ToolHub/
|
||||
|
||||
按钮编辑页里的 ShortX 图标选择器现已改为:
|
||||
- **ShortX 图标名称编辑框已取消**,改为预览卡片 + 图标库点选
|
||||
- **图标颜色支持折叠式完整调色板**,支持常用色、最近 5 色、透明度调节、RGB 三色调色器(0-255),并可切回跟随主题
|
||||
- **分页模式**,不再一次性塞入大批图标
|
||||
- **图标列表按当前可用宽度自动排列列数,并结合可见高度计算每页容量**
|
||||
- 保留 **搜索 / 分类 / 上一页 / 下一页**
|
||||
@@ -138,12 +137,28 @@ ToolHub/
|
||||
- 收起后再次点击 **展开图标库** 可正常重新打开
|
||||
|
||||
当前交互要点:
|
||||
1. 图标库与调色板默认收起,点击 **展开图标库 / 展开调色板** 后打开
|
||||
2. 常用颜色会按色相自动排序,主题色固定在最前,并像 ShortX 图标列表一样按当前可用宽度自动排布
|
||||
3. 列数按当前可用宽度自动计算,屏幕更宽时一页可显示更多图标
|
||||
4. 每页容量按自动列数 × 当前可见行数实时计算
|
||||
5. 搜索、切分类、翻页时都会回到顶部,减少卡顿感和误触
|
||||
6. 选中图标后自动回填并收起图标库
|
||||
1. 图标库默认收起,点击 **展开图标库** 后打开
|
||||
2. 列数按当前可用宽度自动计算,屏幕更宽时一页可显示更多图标
|
||||
3. 每页容量按自动列数 × 当前可见行数实时计算
|
||||
4. 搜索、切分类、翻页时都会回到顶部,减少卡顿感和误触
|
||||
5. 选中图标后自动回填并收起图标库
|
||||
|
||||
## 弹出式颜色选择器
|
||||
|
||||
点击 **选择颜色** 按钮弹出独立颜色选择器弹窗:
|
||||
- **图标预览区**:实时显示当前图标着色效果
|
||||
- **最近使用**:最多 8 个,点击直接复用,自动去重并置顶
|
||||
- **常用颜色**:21 色固定网格(7 列),点击直接选中
|
||||
- **RGB 三色调色器**:红/绿/蓝各 0-255 拖动进度条,实时同步预览
|
||||
- **透明度滑块**:A 通道 0-255,支持半透明图标着色
|
||||
- **清空按钮**:一键恢复跟随主题
|
||||
- **确定按钮**:确认选择并将颜色加入最近使用列表
|
||||
|
||||
交互要点:
|
||||
1. 拖动 RGB 滑块时,预览图标实时变色,当前颜色值文本实时更新
|
||||
2. 点击常用色或最近色后,RGB 滑块自动同步到对应值
|
||||
3. 清空后滑块重置为 255/255/255/255(白/不透明)
|
||||
4. 最近颜色持久化存储,跨会话保留
|
||||
|
||||
---
|
||||
|
||||
@@ -208,3 +223,4 @@ shortx.getShortXDir() + "/ToolHub/logs/init.log"
|
||||
- 优化入口返回信息格式
|
||||
- **设置面板改造**:取消“执行与查看器”、“悬浮球文字/大小/颜色”设置项
|
||||
- **悬浮球图标配置** 支持 ShortX 图标选择器(弹窗列表+搜索)和调色板拖拽选色
|
||||
- **弹出式颜色选择器**:新增独立弹窗,支持最近使用颜色(8 个)、RGB 实时调色、常用色网格、透明度滑块,最近颜色持久化存储
|
||||
|
||||
@@ -914,7 +914,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
|
||||
shortxBtnGap2.setLayoutParams(new android.widget.LinearLayout.LayoutParams(self.dp(8), 1));
|
||||
shortxQuickRow.addView(shortxBtnGap2);
|
||||
|
||||
var btnClearShortXIcon = self.ui.createFlatButton(self, "清空", subTextColor, function() {
|
||||
var btnClearShortXIcon = self.ui.createFlatButton(self, "\u6e05\u7a7a", subTextColor, function() {
|
||||
self.touchActivity();
|
||||
currentShortXIconName = "";
|
||||
updateShortXIconPreview();
|
||||
@@ -922,6 +922,26 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
|
||||
shortxPickerState.clearBtn = btnClearShortXIcon;
|
||||
shortxQuickRow.addView(btnClearShortXIcon);
|
||||
|
||||
var shortxBtnGap3 = new android.view.View(context);
|
||||
shortxBtnGap3.setLayoutParams(new android.widget.LinearLayout.LayoutParams(self.dp(8), 1));
|
||||
shortxQuickRow.addView(shortxBtnGap3);
|
||||
|
||||
var btnColorShortX = self.ui.createFlatButton(self, "\u989c\u8272", C.primary, function() {
|
||||
self.touchActivity();
|
||||
var currentTint = (inputShortXIconTint && inputShortXIconTint.input) ? String(inputShortXIconTint.input.getText() || "") : "";
|
||||
self.showColorPickerPopup({
|
||||
currentColor: currentTint,
|
||||
currentIconName: currentShortXIconName,
|
||||
onSelect: function(colorHex) {
|
||||
if (inputShortXIconTint && inputShortXIconTint.input) {
|
||||
inputShortXIconTint.input.setText(colorHex);
|
||||
}
|
||||
try { if (tintPaletteState.toggleBtn) tintPaletteState.toggleBtn.setText(colorHex || "\u9009\u62e9\u989c\u8272"); } catch(e) {}
|
||||
}
|
||||
});
|
||||
});
|
||||
shortxQuickRow.addView(btnColorShortX);
|
||||
|
||||
var shortxPickerWrap = new android.widget.LinearLayout(context);
|
||||
shortxPickerWrap.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
shortxPickerWrap.setPadding(0, 0, 0, self.dp(8));
|
||||
@@ -1449,8 +1469,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
|
||||
tintPaletteWrap.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
tintPaletteWrap.setPadding(0, 0, 0, self.dp(12));
|
||||
tintPaletteWrap.setBackground(self.ui.createRoundDrawable(self.withAlpha(cardColor, 0.92), self.dp(14)));
|
||||
// [Popup] tint palette moved to showColorPickerPopup() — no longer embedded
|
||||
// form.addView(tintPaletteWrap);
|
||||
form.addView(tintPaletteWrap);
|
||||
tintPaletteState.pickerWrap = tintPaletteWrap;
|
||||
|
||||
var tintPaletteHead = new android.widget.LinearLayout(context);
|
||||
@@ -4232,6 +4251,43 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
"#FFE91E63", "#FF795548", "#FF9E9E9E", "#FF607D8B", "#FF000000", "#FFFFFFFF"
|
||||
];
|
||||
|
||||
// ========== 最近使用颜色 ==========
|
||||
var RECENT_COLORS_KEY = "color_picker_recent";
|
||||
var MAX_RECENT_COLORS = 8;
|
||||
var recentColors = [];
|
||||
try {
|
||||
var recentSaved = self.loadPanelState(RECENT_COLORS_KEY);
|
||||
if (recentSaved && recentSaved.colors && recentSaved.colors.length) {
|
||||
var rc;
|
||||
for (rc = 0; rc < recentSaved.colors.length && rc < MAX_RECENT_COLORS; rc++) {
|
||||
var rn = normalizeTintColorValue(recentSaved.colors[rc], false);
|
||||
if (rn) recentColors.push(rn);
|
||||
}
|
||||
}
|
||||
} catch(eRecentLoad) {}
|
||||
|
||||
function saveRecentColors() {
|
||||
try {
|
||||
self.savePanelState(RECENT_COLORS_KEY, { colors: recentColors.slice(0, MAX_RECENT_COLORS) });
|
||||
} catch(eRecentSave) {}
|
||||
}
|
||||
|
||||
function pushRecentColor(hex) {
|
||||
var normalized = normalizeTintColorValue(hex, false);
|
||||
if (!normalized) return;
|
||||
var next = [normalized];
|
||||
var i;
|
||||
for (i = 0; i < recentColors.length; i++) {
|
||||
if (recentColors[i] !== normalized) {
|
||||
next.push(recentColors[i]);
|
||||
}
|
||||
if (next.length >= MAX_RECENT_COLORS) break;
|
||||
}
|
||||
recentColors = next;
|
||||
saveRecentColors();
|
||||
refreshRecentGrid();
|
||||
}
|
||||
|
||||
var selectedColor = currentColor;
|
||||
var isFollowTheme = !currentColor;
|
||||
var currentBaseRgbHex = extractTintRgbHex(currentColor);
|
||||
@@ -4284,7 +4340,97 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
}
|
||||
updatePreview();
|
||||
|
||||
// 21 色快捷网格
|
||||
// ========== 最近使用颜色 ==========
|
||||
var recentTitle = new android.widget.TextView(context);
|
||||
recentTitle.setText("最近使用");
|
||||
recentTitle.setTextColor(subTextColor);
|
||||
recentTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
recentTitle.setPadding(self.dp(12), self.dp(8), self.dp(12), self.dp(4));
|
||||
content.addView(recentTitle);
|
||||
|
||||
var recentGrid = new android.widget.GridLayout(context);
|
||||
recentGrid.setColumnCount(8);
|
||||
recentGrid.setPadding(self.dp(8), self.dp(4), self.dp(8), self.dp(4));
|
||||
content.addView(recentGrid);
|
||||
|
||||
var recentEmptyTv = new android.widget.TextView(context);
|
||||
recentEmptyTv.setText("暂无最近颜色");
|
||||
recentEmptyTv.setTextColor(subTextColor);
|
||||
recentEmptyTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 11);
|
||||
recentEmptyTv.setPadding(self.dp(12), self.dp(4), self.dp(12), self.dp(8));
|
||||
recentEmptyTv.setVisibility(android.view.View.GONE);
|
||||
content.addView(recentEmptyTv);
|
||||
|
||||
function refreshRecentGrid() {
|
||||
try {
|
||||
recentGrid.removeAllViews();
|
||||
if (!recentColors.length) {
|
||||
recentEmptyTv.setVisibility(android.view.View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
recentEmptyTv.setVisibility(android.view.View.GONE);
|
||||
var ri;
|
||||
for (ri = 0; ri < recentColors.length && ri < MAX_RECENT_COLORS; ri++) {
|
||||
(function(hex) {
|
||||
var cell = new android.widget.FrameLayout(context);
|
||||
var margin = self.dp(3);
|
||||
try {
|
||||
var lp = new android.widget.GridLayout.LayoutParams();
|
||||
lp.width = self.dp(28);
|
||||
lp.height = self.dp(28);
|
||||
lp.setMargins(margin, margin, margin, margin);
|
||||
cell.setLayoutParams(lp);
|
||||
} catch(e) {}
|
||||
|
||||
var swatch = new android.view.View(context);
|
||||
swatch.setLayoutParams(new android.widget.FrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams.MATCH_PARENT, android.widget.FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
try {
|
||||
var bg = new android.graphics.drawable.GradientDrawable();
|
||||
bg.setColor(android.graphics.Color.parseColor(hex));
|
||||
bg.setCornerRadius(self.dp(5));
|
||||
swatch.setBackground(bg);
|
||||
} catch(e) {}
|
||||
cell.addView(swatch);
|
||||
|
||||
if (selectedColor === hex) {
|
||||
try {
|
||||
var border = new android.graphics.drawable.GradientDrawable();
|
||||
border.setColor(android.graphics.Color.TRANSPARENT);
|
||||
border.setCornerRadius(self.dp(5));
|
||||
border.setStroke(self.dp(2), C.primary);
|
||||
cell.setForeground(border);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
cell.setOnClickListener(new android.view.View.OnClickListener({
|
||||
onClick: function() {
|
||||
self.touchActivity();
|
||||
isFollowTheme = false;
|
||||
selectedColor = hex;
|
||||
currentBaseRgbHex = extractTintRgbHex(hex);
|
||||
currentAlphaByte = extractTintAlphaByte(hex);
|
||||
updatePreview();
|
||||
updateValueTv();
|
||||
refreshRecentGrid();
|
||||
refreshCommonGrid();
|
||||
syncRgbSeeks();
|
||||
}
|
||||
}));
|
||||
recentGrid.addView(cell);
|
||||
})(recentColors[ri]);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
refreshRecentGrid();
|
||||
|
||||
// 21 色常用颜色
|
||||
var commonTitle = new android.widget.TextView(context);
|
||||
commonTitle.setText("常用颜色");
|
||||
commonTitle.setTextColor(subTextColor);
|
||||
commonTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
commonTitle.setPadding(self.dp(12), self.dp(8), self.dp(12), self.dp(4));
|
||||
content.addView(commonTitle);
|
||||
|
||||
var commonGrid = new android.widget.GridLayout(context);
|
||||
commonGrid.setColumnCount(7);
|
||||
commonGrid.setPadding(self.dp(8), self.dp(4), self.dp(8), self.dp(8));
|
||||
@@ -4292,7 +4438,6 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
for (ci = 0; ci < commonTintHexValues.length; ci++) {
|
||||
(function(hex) {
|
||||
var cell = new android.widget.FrameLayout(context);
|
||||
cell.setLayoutParams(new android.widget.GridLayout.LayoutParams(self.dp(32), self.dp(32)));
|
||||
var margin = self.dp(4);
|
||||
try {
|
||||
var lp = new android.widget.GridLayout.LayoutParams();
|
||||
@@ -4331,7 +4476,9 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
currentAlphaByte = extractTintAlphaByte(hex);
|
||||
updatePreview();
|
||||
updateValueTv();
|
||||
refreshRecentGrid();
|
||||
refreshCommonGrid();
|
||||
syncRgbSeeks();
|
||||
}
|
||||
}));
|
||||
commonGrid.addView(cell);
|
||||
@@ -4348,7 +4495,6 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
if (!cell) continue;
|
||||
try { cell.setForeground(null); } catch(e) {}
|
||||
}
|
||||
// 找到匹配的子项设置边框
|
||||
var idx = commonTintHexValues.indexOf(selectedColor);
|
||||
if (idx >= 0 && idx < count) {
|
||||
var matchedCell = commonGrid.getChildAt(idx);
|
||||
@@ -4380,6 +4526,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
// RGB 滑块
|
||||
var rgbLabels = ["R", "G", "B"];
|
||||
var rgbSeeks = [];
|
||||
var rgbValTvs = [];
|
||||
var ri;
|
||||
for (ri = 0; ri < 3; ri++) {
|
||||
(function(idx) {
|
||||
@@ -4408,6 +4555,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
valTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 11);
|
||||
valTv.setMinWidth(self.dp(28));
|
||||
row.addView(valTv);
|
||||
rgbValTvs.push(valTv);
|
||||
|
||||
seek.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener({
|
||||
onProgressChanged: function(s, progress, fromUser) {
|
||||
@@ -4422,6 +4570,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
selectedColor = buildArgbHex(currentAlphaByte, currentBaseRgbHex);
|
||||
updatePreview();
|
||||
updateValueTv();
|
||||
refreshRecentGrid();
|
||||
refreshCommonGrid();
|
||||
},
|
||||
onStartTrackingTouch: function() {},
|
||||
@@ -4432,15 +4581,20 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
})(ri);
|
||||
}
|
||||
|
||||
// 初始化 RGB 滑块值
|
||||
try {
|
||||
var initR = parseInt(currentBaseRgbHex.slice(0, 2), 16) || 0;
|
||||
var initG = parseInt(currentBaseRgbHex.slice(2, 4), 16) || 0;
|
||||
var initB = parseInt(currentBaseRgbHex.slice(4, 6), 16) || 0;
|
||||
rgbSeeks[0].setProgress(initR);
|
||||
rgbSeeks[1].setProgress(initG);
|
||||
rgbSeeks[2].setProgress(initB);
|
||||
} catch(e) {}
|
||||
function syncRgbSeeks() {
|
||||
try {
|
||||
var initR = parseInt(currentBaseRgbHex.slice(0, 2), 16) || 0;
|
||||
var initG = parseInt(currentBaseRgbHex.slice(2, 4), 16) || 0;
|
||||
var initB = parseInt(currentBaseRgbHex.slice(4, 6), 16) || 0;
|
||||
rgbSeeks[0].setProgress(initR);
|
||||
rgbSeeks[1].setProgress(initG);
|
||||
rgbSeeks[2].setProgress(initB);
|
||||
rgbValTvs[0].setText(String(initR));
|
||||
rgbValTvs[1].setText(String(initG));
|
||||
rgbValTvs[2].setText(String(initB));
|
||||
} catch(e) {}
|
||||
}
|
||||
syncRgbSeeks();
|
||||
|
||||
// 透明度滑块
|
||||
var alphaRow = new android.widget.LinearLayout(context);
|
||||
@@ -4477,6 +4631,7 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
selectedColor = buildArgbHex(currentAlphaByte, currentBaseRgbHex);
|
||||
updatePreview();
|
||||
updateValueTv();
|
||||
refreshRecentGrid();
|
||||
refreshCommonGrid();
|
||||
},
|
||||
onStartTrackingTouch: function() {},
|
||||
@@ -4499,13 +4654,21 @@ FloatBallAppWM.prototype.showColorPickerPopup = function(opts) {
|
||||
selectedColor = "";
|
||||
updatePreview();
|
||||
updateValueTv();
|
||||
refreshRecentGrid();
|
||||
refreshCommonGrid();
|
||||
syncRgbSeeks();
|
||||
alphaSeek.setProgress(255);
|
||||
alphaValTv.setText("255");
|
||||
currentAlphaByte = 255;
|
||||
});
|
||||
actionRow.addView(btnClear);
|
||||
|
||||
var btnOk = self.ui.createSolidButton(self, "确定", C.primary, android.graphics.Color.WHITE, function() {
|
||||
self.touchActivity();
|
||||
var finalColor = isFollowTheme ? "" : selectedColor;
|
||||
if (!isFollowTheme && selectedColor) {
|
||||
pushRecentColor(selectedColor);
|
||||
}
|
||||
if (typeof onSelect === "function") onSelect(finalColor);
|
||||
closePopup();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user