win10上想用一个便签工具,系统上也没带。安装微软官方的便签工具让修复这修得哪的-放弃。干脆让AI写一个, 经过几次改错——不完善但凑活能用(自己入门小白能力有限,希望有能力有兴趣的大佬继续完善)。 ---感谢AI(用到了deepseek\workbuddy 智能体)
// 便签工具 - V1.1
// 保留:托盘常驻 + Ctrl+Alt+N 热键新建;SQLite 本地存储;置顶/编辑自动保存/启动恢复。
// 行为:「关」按钮=隐藏窗口(便签保留在库,下次启动恢复内容);右键菜单「删除便签」=软删除(真正移除)。
// 数据库 notes.db 生成在同目录。
import win.ui;
import win.util.tray;
import key.hotkey;
import sqlite;
import gdi;
// ---------------- 配置 ----------------
var DB_PATH = io.fullpath("notes.db");
var PALETTE = {"#FFF59D";"#C8E6C9";"#F8BBD0";"#BBDEFB";"#FFCC80";"#FFFFFF";"#E1BEE7"};
var SIZES = {12; 14; 16; 18; 20; 24}; // 字号选项
// ---------------- 颜色工具 ----------------
hexToColor = function(hex){
if(!hex) hex = "#FFF59D";
var m = string.match(hex, "#[0-9A-Fa-f]+");
if(!m) hex = "#FFF59D"; else hex = m;
var r = tonumber("0x" ++ string.sub(hex,2,3));
var g = tonumber("0x" ++ string.sub(hex,4,5));
var b = tonumber("0x" ++ string.sub(hex,6,7));
return gdi.RGB(r,g,b);
}
darker = function(hex){
if(!hex) hex = "#FFF59D";
var m = string.match(hex, "#[0-9A-Fa-f]+");
if(!m) hex = "#FFF59D"; else hex = m;
var r = tonumber("0x" ++ string.sub(hex,2,3));
var g = tonumber("0x" ++ string.sub(hex,4,5));
var b = tonumber("0x" ++ string.sub(hex,6,7));
r = math.floor(r*0.82); g = math.floor(g*0.82); b = math.floor(b*0.82);
return gdi.RGB(r,g,b);
}
newId = function(){
return tostring(time.now()) ++ tostring(math.random(100000));
}
// ---------------- 存储层 (SQLite) ----------------
var db = sqlite(DB_PATH);
if(!db.existsTable("notes")){
db.exec("CREATE TABLE notes(id TEXT PRIMARY KEY, content TEXT, color TEXT, x INTEGER, y INTEGER, w INTEGER, h INTEGER, pinned INTEGER, fontSize INTEGER, createdAt TEXT, updatedAt TEXT, deletedAt TEXT)");
} else {
// 数据迁移:旧表可能没有 fontSize 列,补上,避免 REPLACE 报错
var cols = db.getTable("PRAGMA table_info(notes)");
var hasFs = false;
for(i=1; #cols){ if(cols[i].name == "fontSize") hasFs = true; }
if(!hasFs) db.exec("ALTER TABLE notes ADD COLUMN fontSize INTEGER");
}
saveNote = function(n){
n.updatedAt = tostring(time.now());
if(n.fontSize == null) n.fontSize = 14;
var cmd = db.prepare("REPLACE INTO notes(id,content,color,x,y,w,h,pinned,fontSize,createdAt,updatedAt,deletedAt) VALUES(@id,@content,@color,@x,@y,@w,@h,@pinned,@fontSize,@createdAt,@updatedAt,@deletedAt)");
cmd.step(
id = n.id; content = n.content; color = n.color;
x = n.x; y = n.y; w = n.w; h = n.h;
pinned = n.pinned; fontSize = n.fontSize;
createdAt = n.createdAt; updatedAt = n.updatedAt; deletedAt = n.deletedAt
);
cmd.finalize();
}
loadNotes = function(){
return db.getTable("SELECT * FROM notes WHERE deletedAt IS NULL");
}
// ---------------- 创建便签窗口 ----------------
var noteCount = 0;
var openCount = 0; // 当前已打开的便签窗口数(用于"最后一个关闭则退出程序")
createNote = function(data){
if(!data){
data = {
id = newId(); content = ""; color = PALETTE[1];
x = 200 + noteCount*24; y = 150 + noteCount*24;
w = 220; h = 180; pinned = 0; fontSize = 14;
createdAt = tostring(time.now())
};
}
noteCount++;
openCount++;
var x = data.x or 200, y = data.y or 150;
var w = data.w or 220, h = data.h or 180;
var fs = data.fontSize or 14;
var winform = win.form(
text = "便签";
left = x; top = y; right = x + w; bottom = y + h;
border = "none";
bgcolor = hexToColor(data.color)
);
winform.add(
caption = { cls="plus"; text="便签"; left=0; top=0; right=w-98; bottom=24;
background = darker(data.color); color = 0x333333;
font = LOGFONT(h=-12; name="Microsoft YaHei"); notify=1; dt=1; dl=1; dr=1; z=1 };
btnColor = { cls="button"; text="色"; left=w-92; top=3; right=w-66; bottom=21; dr=1; dt=1; z=2 };
btnPin = { cls="button"; text="钉"; left=w-64; top=3; right=w-38; bottom=21; dr=1; dt=1; z=3 };
btnClose = { cls="button"; text="关"; left=w-36; top=3; right=w-8; bottom=21; dr=1; dt=1; z=4 };
edit = { cls="edit"; text=data.content or ""; left=4; top=26; right=w-4; bottom=h-16;
edge=1; multiline=1; vscroll=1; dl=1; dt=1; dr=1; db=1; z=5;
font = LOGFONT(h=-fs; name="Microsoft YaHei") };
resizeHandle = { cls="plus"; left=w-16; top=h-16; right=w; bottom=h;
background = darker(data.color); dr=1; db=1; notify=1; z=6 }
);
if(data.pinned) win.setTopmost(winform.hwnd, true);
winform.edit.bgcolor = hexToColor(data.color); // 编辑区背景同步便签色
// 拖动:在标题栏按下即移动窗口
winform.caption.onMouseDown = function(wParam,lParam){ winform.hitCaption(); };
// 改色:循环切换(保留快速换色)
winform.btnColor.oncommand = function(){
var idx = 1;
for(i=1; #PALETTE){ if(PALETTE[i] == data.color) idx = i; }
data.color = PALETTE[(idx % #PALETTE) + 1];
winform.bgcolor = hexToColor(data.color);
winform.caption.background = darker(data.color);
winform.resizeHandle.background = darker(data.color);
winform.edit.bgcolor = hexToColor(data.color);
saveNote(data);
};
// 钉住 / 取消置顶
winform.btnPin.oncommand = function(){
if(data.pinned) data.pinned = 0; else data.pinned = 1; // aardio 无 iif;0/null/false 均为假值,故 if(data.pinned) 即"已置顶"
win.setTopmost(winform.hwnd, data.pinned);
saveNote(data);
};
// 关闭 = 仅隐藏窗口(内容已在 onClose 中保存进数据库,便签保留,下次启动恢复)
// 真正的删除请用右键菜单的「删除便签」
winform.btnClose.oncommand = function(){
winform.close();
};
// 编辑自动保存
winform.edit.onChange = function(){
data.content = winform.edit.text;
saveNote(data);
};
// 关闭时保存位置 / 尺寸 / 字号;若这是最后一个窗口,则整个程序退出
winform.onClose = function(){
data.content = winform.edit.text; // 关闭前强制读取最新文字,确保最后输入的内容不丢
var px, py, w0, h0 = winform.getPos(true); // getPos 返回 x,y,宽,高(true=屏幕坐标);aardio 无 getSize 方法
data.x = px; data.y = py; data.w = w0; data.h = h0;
saveNote(data);
openCount--;
if(openCount <= 0){
win.quitMessage(); // 最后一个便签关闭 → 消息循环退出 → 进程结束
}
};
// ---------- 右键菜单:颜色子菜单 + 字号子菜单(借鉴参考代码的子菜单与打勾)----------
var colorMenu = win.ui.popmenu(winform);
for(i=1; #PALETTE){
var c = PALETTE[i];
colorMenu.add(c, function(){
data.color = c;
winform.bgcolor = hexToColor(c);
winform.caption.background = darker(c);
winform.resizeHandle.background = darker(c);
winform.edit.bgcolor = hexToColor(c);
saveNote(data);
});
}
var fontMenu = win.ui.popmenu(winform);
for(i=1; #SIZES){
var s = SIZES[i];
fontMenu.add(tostring(s), function(){
data.fontSize = s;
winform.edit.font = LOGFONT(h=-s; name="Microsoft YaHei");
saveNote(data);
});
}
var ctx = win.ui.popmenu(winform);
ctx.add("颜色", colorMenu);
ctx.add("字号", fontMenu);
ctx.add(); // 分隔线
ctx.add("置顶/取消置顶", function(){
if(data.pinned) data.pinned = 0; else data.pinned = 1; // aardio 无 iif;0/null/false 均为假值,故 if(data.pinned) 即"已置顶"
win.setTopmost(winform.hwnd, data.pinned);
saveNote(data);
});
ctx.add("删除便签", function(){
data.deletedAt = tostring(time.now());
saveNote(data);
winform.close();
});
// 右键弹出(WM_RBUTTONUP)—— 弹出前刷新打勾(参考代码的 check + 0x400)
winform.wndproc = function(hwnd, msg, wParam, lParam){
if(msg == 0x205/*_WM_RBUTTONUP*/){
for(i=1; #PALETTE){ colorMenu.check(i, data.color == PALETTE[i], 0x400); }
for(i=1; #SIZES){ fontMenu.check(i, (data.fontSize or 14) == SIZES[i], 0x400); }
var sx, sy = win.getCursorPos();
ctx.popup(sx, sy, true);
return true;
}
if(msg == 0x7B/*_WM_CONTEXTMENU*/){ return true; } // 阻止 edit 默认右键菜单
};
// ---------- 右下角 resize 手柄:拖拽调节窗口大小 + 调整光标(借鉴参考代码的 resize + 光标切换)----------
var resizing = false;
var startX, startY, startPx, startPy, startW, startH;
winform.resizeHandle.onMouseDown = function(wParam, lParam){
resizing = true;
startX, startY = win.getMessagePos(lParam);
startPx, startPy, startW, startH = winform.getPos(true); // getPos 返回 x,y,宽,高;替代不存在的 getSize
win.setCapture(winform.resizeHandle.hwnd);
};
winform.resizeHandle.onMouseMove = function(wParam, lParam){
if(resizing){
var mx, my = win.getMessagePos(lParam);
var nw = startW + (mx - startX);
var nh = startH + (my - startY);
if(nw < 180) nw = 180; // 最小尺寸限制(借鉴参考代码边界处理)
if(nh < 140) nh = 140;
win.setPos(winform.hwnd, startPx, startPy, nw, nh);
data.w = nw; data.h = nh;
} else {
::SetCursor(::LoadCursor(null, topointer(32642))); // 悬停手柄显示右下调整光标
}
};
winform.resizeHandle.onMouseUp = function(wParam, lParam){
if(resizing){
resizing = false;
win.releaseCapture();
saveNote(data); // 保存新尺寸
}
};
winform.resizeHandle.onMouseLeave = function(){
if(!resizing) ::SetCursor(::LoadCursor(null, topointer(32512))); // 恢复默认箭头
};
winform.show();
return winform;
}
// ---------------- 主程序:托盘 + 全局热键 ----------------
var mainForm = win.form(text="便签工具"; right=0; bottom=0; border="none");
mainForm.show(false); // 主窗口隐藏,仅留托盘
// 托盘
mainForm.tray = win.util.tray(mainForm);
mainForm.tray.tip = "便签工具(左键新建 / 右键菜单)";
mainForm.tray.onClick = function(){ createNote(); }; // 左键:新建便签
mainForm.tray.onRButtonUp = function(){ // 右键:菜单
win.setForeground(mainForm.hwnd); // 前置主窗口,避免菜单点了不消失(aardio 官方范例要求)
var x, y = win.getCursorPos();
var m = win.ui.popmenu(mainForm);
m.add('新建便签', function(){ createNote() });
m.add();
m.add('退出', function(){ win.quitMessage() });
m.popup(x, y, true);
};
// 全局热键新建便签:同时注册 Ctrl+N 与 Ctrl+Alt+N
// 注意:Ctrl+N 是全局热键,会覆盖其它软件的 Ctrl+N(如浏览器新建标签页),介意的话只用 Ctrl+Alt+N
var hk = key.hotkey();
hk.regStr("Ctrl+Alt+N", function(hwnd,...){ createNote() });
hk.regStr("Ctrl+N", function(hwnd,...){ createNote() });
// 启动时恢复已有便签;若库为空(首次运行),自动建一张便签,保证一启动就有可见窗口
var notes = loadNotes();
if(#notes == 0){
createNote();
} else {
for(i=1; #notes){ createNote(notes[i]); }
}
// 启动气泡提示:即使你没注意到托盘图标,也能看到这条反馈
mainForm.tray.pop("便签工具已启动\n左键托盘 / Ctrl+Alt+N 新建便签", "便签工具");
win.loopMessage();
最新回复 (0)