# aardio 范例: 农历悬浮窗



```aardio
import win.ui;
import win.ui.layered;
import win.ui.menu;
import win.clip;
import time.lunar;
import time.almanac;
import time.festival;
/*DSG{{*/
var winform = win.form(text="桌面农历";left=-50;top=-100;right=238;bottom=48;border="none";exmode="toolwindow";mode="popup";topmost=1)
winform.add(
canvas={cls="canvas";left=0;top=0;right=288;bottom=148;z=1}
)
/*}}*/

// 日黄历按日期缓存，时柱按小时缓存；所有文字保留完整原文。
var dayDate,dayData,lunarText,hourKey,hourData;
var readData = function(moment){
    if(moment === null) moment=time.almanac.nowBeijing();
    else moment=time(moment);
    
    var date=tostring(moment,"%Y-%m-%d");
    if(date !== dayDate){
        dayData=time.almanac.getDay(moment);
        lunarText=tostring(time.lunar(moment),"%Lm%Ld");
        dayDate=date;
    }
    
    var key=tostring(moment,"%Y-%m-%d %H");
    if(key !== hourKey){hourData=time.almanac.getHour(moment);hourKey=key;}
    
    return {
        date=date;
        minuteKey=tostring(moment,"%Y-%m-%d %H:%M");
        lunar=lunarText;
        meta=tostring(moment,"%Y.%m.%d · %a","chs") ++ " · 北京 " ++ tostring(moment,"%H:%M") ++ " · " ++ hourData.name;
        pillars=[dayData.year.name,dayData.month.name,dayData.day.name,hourData.hour.name];
        yi=string.join(dayData.yi,"、");
        ji=string.join(dayData.ji,"、");
        festival = time.festival.getMain(moment);
    };
}

var data=readData();
var pinned=true,hoverClose=false,pressedClose=false;
var fonts={
    date=gdip.font("SimSun",18,1);
    pillar=gdip.font("SimSun",13,1);
    body=gdip.font("Microsoft YaHei",12);
    small=gdip.font("Microsoft YaHei",10);
};
var brush=gdip.solidBrush();
var pen=gdip.pen(0x38C7B48F,1);
var card=gdip.path();card.addRoundRect(4,4,280,140,10);
var gradient=gdip.lineBrush(::POINTF(0,4),::POINTF(0,144),0xE8233033,0xE8182025);

var format=gdip.stringFormat(0x1000/*NoWrap*/);
format.lineAlign=1;
format.trimming=3/*EllipsisCharacter：超长由GDI+直接显示省略号*/;

var closeRect=::RECTF(252,9,26,26);
var labels=["年","月","日","时"];
var text=function(g,s,font,x,y,w,h,color){
    brush.color=color;
    g.drawString(s,font,::RECTF(x,y,w,h),format,brush);
}

var paint=function(g,snapshot,sx,sy){
    var saved=g.save();g.scale(sx,sy);
    g.smoothingMode=4;g.textRenderingHint=4;g.compositingQuality=0;
    g.fillPath(gradient,card);
    pen.color=0x38C7B48F;g.drawPath(pen,card);
    
    if(snapshot.festival) text(g,snapshot.festival,fonts.small,110,10,150,26,0xFF97A3A0);
    text(g,snapshot.lunar,fonts.date,14,11,232,26,0xFFF5E8CF);
    text(g,snapshot.meta,fonts.small,14,38,260,17,0xFF97A3A0);
    
    for(i=1;4){
        var x=14+(i-1)*65;
        text(g,labels[i],fonts.small,x,58,15,22,0xFF97A3A0);
        text(g,snapshot.pillars[i],fonts.pillar,x+15,58,47,22,0xFFE2D1B4);
    }
    
    pen.color=0x20FFFFFF;g.drawLine(pen,14,83,274,83);
    text(g,"宜：",fonts.body,14,89,32,22,0xFFA8C9AD);
    text(g,snapshot.yi,fonts.body,46,89,228,22,0xFFD2D9D3);
    text(g,"忌：",fonts.body,14,113,32,22,0xFFE0ADA3);
    text(g,snapshot.ji,fonts.body,46,113,228,22,0xFFD2D9D3);
    
    if(hoverClose){brush.color=0x24DB8F88;g.fillRoundRect(brush,closeRect,5);}
    pen.color=0xFF97A3A0;
    g.drawLine(pen,262,19,268,25).drawLine(pen,268,19,262,25);
    g.restore(saved);
}

var refresh=function(force=false){
    var now=time.almanac.nowBeijing();
    if(!force && tostring(now,"%Y-%m-%d %H:%M") === data.minuteKey) return;
    data=readData(now);winform.canvas.redraw();
}

var copyText=function(){
    var pillars=[];
    for(i=1;4) table.push(pillars,labels[i] ++ " " ++ data.pillars[i]);
    return string.join([data.lunar ++ "  " ++ data.meta,string.join(pillars,"  "),"宜：" ++ data.yi,"忌：" ++ data.ji],'\r\n');
}

var togglePinned=function(){
    pinned=!pinned;
    win.setTopmost(winform.hwnd,pinned);
    win.setTopmost(winform.canvas.hwnd,pinned);
}

var isClose=function(lParam){
    var x,y=win.getMessagePos(lParam);
    var sx,sy=winform.dpiScale(1,1);
    return closeRect.contains(x/sx,y/sy);
}

winform.canvas.onDrawForeground=function(g,rc){
    var sx,sy=winform.dpiScale(1,1);paint(g,data,sx,sy);
}

winform.canvas.onMouseMove=function(wParam,lParam){
    var hit=isClose(lParam);
    if(hit !== hoverClose){hoverClose=hit;winform.canvas.redraw();}
}

winform.canvas.onMouseLeave=function(){
    hoverClose=false;pressedClose=false;winform.canvas.redraw();
}

winform.canvas.onMouseDown=function(wParam,lParam){
    pressedClose=isClose(lParam);
    if(!pressedClose) winform.hitCaption();
}

winform.canvas.onMouseUp=function(wParam,lParam){
    var closing=pressedClose && isClose(lParam);pressedClose=false;
    if(closing) winform.close();
}

var menu=win.ui.popmenu(winform);
var pinId=menu.add("始终置顶",togglePinned);
menu.add("复制完整信息",function(){win.clip.write(copyText());});
menu.add("刷新",function(){refresh(true);});
menu.add();
menu.add("退出",function(){winform.close();});
winform.canvas.onContextMenu=function(){menu.check(pinId,pinned,0);menu.popup();}

winform.canvas.orphanWindow(true);
var layered=win.ui.layered(winform,-1);
layered.backgroundColor=0x00000000;layered.background=null;
var timerId=winform.setInterval(function(){refresh();},1000);

winform.onDestroy=function(){
    winform.clearInterval(timerId);
    for(k,font in fonts) font.delete();
    brush.delete();pen.delete();card.delete();gradient.delete();format.delete();
}

winform.show();
winform.canvas.redraw();
win.loopMessage();
```

