- 后台托盘(mac/Windows):window_manager 拦截关窗改隐藏,服务持续运行; 托盘单击恢复窗口、右键菜单(显示主界面/退出),退出入口仅放托盘菜单 - 品牌:App 更名"资安信插件助手"(mac PRODUCT_NAME/xib、win main.cpp+Runner.rc(code_page 65001)、 MaterialApp/侧栏/托盘 tooltip/日志);图标全家桶换资安信盾牌 logo (tool/gen_app_icons.dart 生成 mac AppIcon 7 尺寸/win ICO 7 尺寸/托盘白底圆盘版/assets logo) - 修复启动布局断言:SliverList 内 Row(stretch) 需 IntrinsicHeight 包裹, 否则子项收到 h=Infinity 约束(用户实测报错,test/ui_smoke_test.dart 守护复现) - Agent.stop 幂等(托盘退出与窗口销毁竞态安全) - 测试修复:smoke 测试改 tester.runAsync 包真实 socket I/O(fake async zone 里会永久挂起)、 显式 pump 替代 pumpAndSettle;13 个测试全过,analyze 零问题,release 构建实测通过
60 lines
2.2 KiB
Dart
60 lines
2.2 KiB
Dart
// 生成托盘图标(32x32 PNG)
|
||
// dart run tool/gen_tray_icon.dart
|
||
// - assets/tray_icon_template.png:macOS template 图标(纯黑+alpha,系统按菜单栏深浅自动反色)
|
||
// - assets/tray_icon_win.png:Windows 彩色图标(绿底+深色卡片剪影)
|
||
import 'dart:io';
|
||
|
||
import 'package:image/image.dart';
|
||
|
||
final _black = ColorRgba8(0, 0, 0, 255);
|
||
final _green = ColorRgba8(74, 222, 128, 255); // 主操作绿 #4ADE80
|
||
final _navy = ColorRgba8(15, 23, 42, 255); // 深蓝黑 #0F172A
|
||
|
||
bool _inRoundRect(int x, int y, int x0, int y0, int x1, int y1, int r) {
|
||
if (x < x0 || x > x1 || y < y0 || y > y1) return false;
|
||
final nearLeft = x < x0 + r, nearRight = x > x1 - r;
|
||
final nearTop = y < y0 + r, nearBottom = y > y1 - r;
|
||
if (!(nearLeft || nearRight) || !(nearTop || nearBottom)) return true; // 十字区
|
||
final cx = nearLeft ? x0 + r : x1 - r;
|
||
final cy = nearTop ? y0 + r : y1 - r;
|
||
final dx = x - cx, dy = y - cy;
|
||
return dx * dx + dy * dy <= r * r;
|
||
}
|
||
|
||
/// 卡片剪影(含磁条挖空)画到 img 上
|
||
void _drawCard(Image img, int x0, int y0, int x1, int y1, ColorUint8 color) {
|
||
const r = 2;
|
||
final stripeY0 = y0 + (y1 - y0 + 1) ~/ 3;
|
||
final stripeY1 = stripeY0 + 2;
|
||
for (var y = y0; y <= y1; y++) {
|
||
for (var x = x0; x <= x1; x++) {
|
||
if (!_inRoundRect(x, y, x0, y0, x1, y1, r)) continue;
|
||
if (y >= stripeY0 && y <= stripeY1) continue; // 磁条挖空
|
||
img.setPixel(x, y, color);
|
||
}
|
||
}
|
||
}
|
||
|
||
void main() {
|
||
// macOS template:透明底 + 黑卡片
|
||
final mac = Image(width: 32, height: 32, numChannels: 4);
|
||
_drawCard(mac, 5, 8, 26, 23, _black);
|
||
|
||
// Windows:绿色圆角底 + 深色卡片
|
||
final win = Image(width: 32, height: 32, numChannels: 4);
|
||
for (var y = 1; y <= 30; y++) {
|
||
for (var x = 1; x <= 30; x++) {
|
||
if (_inRoundRect(x, y, 1, 1, 30, 30, 6)) win.setPixel(x, y, _green);
|
||
}
|
||
}
|
||
_drawCard(win, 7, 9, 24, 22, _navy);
|
||
|
||
for (final entry in [('assets/tray_icon_template.png', mac), ('assets/tray_icon_win.png', win)]) {
|
||
final file = File(entry.$1);
|
||
file.parent.createSync(recursive: true);
|
||
file.writeAsBytesSync(encodePng(entry.$2));
|
||
// ignore: avoid_print
|
||
print('written ${entry.$1}');
|
||
}
|
||
}
|