Release v0.6.7: add DPI-aware cursors for screenshot tools
This commit is contained in:
+79
-23
@@ -123,6 +123,44 @@ bool chooseTextFont(HWND owner,Mark &m)
|
||||
m.fontWeight=font.lfWeight; m.fontItalic=font.lfItalic!=0;
|
||||
return true;
|
||||
}
|
||||
constexpr int CursorTools[]{10,11,22,23,24,25,19};
|
||||
HCURSOR makeToolCursor(int tool,int size)
|
||||
{
|
||||
// Monochrome AND/XOR cursor: white outline keeps the glyph visible on dark images.
|
||||
auto bitmap=CreateBitmap(size,size*2,1,1,nullptr);
|
||||
auto dc=CreateCompatibleDC(nullptr);
|
||||
if(!bitmap || !dc) { if(bitmap) DeleteObject(bitmap); if(dc) DeleteDC(dc); return nullptr; }
|
||||
auto oldBitmap=SelectObject(dc,bitmap);
|
||||
PatBlt(dc,0,0,size,size,WHITENESS); PatBlt(dc,0,size,size,size,BLACKNESS);
|
||||
auto oldBrush=SelectObject(dc,GetStockObject(NULL_BRUSH));
|
||||
auto draw=[&](int offset,int width,COLORREF color) {
|
||||
auto pen=CreatePen(PS_SOLID,width,color); auto oldPen=SelectObject(dc,pen);
|
||||
auto x=[&](int v){return MulDiv(v,size,32);};
|
||||
auto y=[&](int v){return offset+MulDiv(v,size,32);};
|
||||
auto line=[&](int a,int b,int c,int d){MoveToEx(dc,x(a),y(b),nullptr); LineTo(dc,x(c),y(d));};
|
||||
line(1,7,13,7); line(7,1,7,13); // The cross center is the exact drawing point.
|
||||
switch(tool) {
|
||||
case 10:
|
||||
Ellipse(dc,x(17),y(17),x(30),y(30));
|
||||
line(22,21,24,20); line(24,20,24,27); line(21,27,27,27); break;
|
||||
case 11: line(18,29,29,18); line(22,18,29,18); line(29,18,29,25); break;
|
||||
case 22: line(18,29,29,18); break;
|
||||
case 23: Rectangle(dc,x(17),y(18),x(30),y(29)); break;
|
||||
case 24: Ellipse(dc,x(17),y(17),x(30),y(30)); break;
|
||||
case 25: Ellipse(dc,x(16),y(20),x(31),y(28)); break;
|
||||
case 19:
|
||||
line(18,25,25,18); line(25,18,29,22); line(29,22,22,29);
|
||||
line(22,29,18,25); line(18,25,17,30); line(17,30,22,29); break;
|
||||
}
|
||||
SelectObject(dc,oldPen); DeleteObject(pen);
|
||||
};
|
||||
int outline=std::max(3,MulDiv(4,size,32)),stroke=std::max(1,MulDiv(2,size,32));
|
||||
draw(0,outline,RGB(0,0,0));
|
||||
draw(size,outline,RGB(255,255,255)); draw(size,stroke,RGB(0,0,0));
|
||||
SelectObject(dc,oldBrush); SelectObject(dc,oldBitmap); DeleteDC(dc);
|
||||
ICONINFO info{}; info.fIcon=FALSE; info.xHotspot=info.yHotspot=MulDiv(7,size,32); info.hbmMask=bitmap;
|
||||
auto cursor=(HCURSOR)CreateIconIndirect(&info); DeleteObject(bitmap); return cursor;
|
||||
}
|
||||
struct Capture
|
||||
{
|
||||
ScreenshotHost &app;
|
||||
@@ -159,6 +197,7 @@ struct Capture
|
||||
HICON toolIcons[3][15]{};
|
||||
int iconSize{};
|
||||
HCURSOR pencilCursor{};
|
||||
HCURSOR drawingCursors[std::size(CursorTools)]{};
|
||||
int pencilCursorSize{};
|
||||
int unit = 1, panelHeight = 1, statusHeight = 0;
|
||||
int hoveredTool=0;
|
||||
@@ -167,6 +206,7 @@ struct Capture
|
||||
explicit Capture(ScreenshotHost &a) : app(a), autoOpenEditor(a.autoOpenEditor) {}
|
||||
~Capture() {
|
||||
if(pencilCursor) DestroyCursor(pencilCursor);
|
||||
for(auto cursor:drawingCursors) if(cursor) DestroyCursor(cursor);
|
||||
if (font) DeleteObject(font);
|
||||
if(inlineFont) DeleteObject(inlineFont);
|
||||
for(auto &variant:toolIcons) for(auto icon:variant) if(icon) DestroyIcon(icon);
|
||||
@@ -523,6 +563,11 @@ void layout(HWND w, Capture &c)
|
||||
int dpi = GetDpiForWindow(w);
|
||||
int cursorSize=GetSystemMetricsForDpi(SM_CXCURSOR,dpi);
|
||||
if(cursorSize!=c.pencilCursorSize) {
|
||||
for(size_t i=0;i<std::size(CursorTools);++i) {
|
||||
auto cursor=makeToolCursor(CursorTools[i],cursorSize);
|
||||
if(c.drawingCursors[i]) DestroyCursor(c.drawingCursors[i]);
|
||||
c.drawingCursors[i]=cursor;
|
||||
}
|
||||
auto cursor=(HCURSOR)LoadImageW(c.app.instance,MAKEINTRESOURCEW(IDC_SCREENSHOT_PENCIL),IMAGE_CURSOR,cursorSize,cursorSize,0);
|
||||
if(cursor) {
|
||||
if(c.pencilCursor) DestroyCursor(c.pencilCursor);
|
||||
@@ -618,30 +663,41 @@ std::pair<int,MarkPart> markAt(Capture &c, POINT p)
|
||||
}
|
||||
return {-1,MarkPart::None};
|
||||
}
|
||||
HCURSOR toolCursor(const Capture &c)
|
||||
{
|
||||
if(c.pointer) return LoadCursorW(nullptr,IDC_ARROW);
|
||||
if(c.textTool) return LoadCursorW(nullptr,IDC_IBEAM);
|
||||
if(c.freehand && c.pencilCursor) return c.pencilCursor;
|
||||
int tool=c.highlight?19:c.straight?22:c.shape!=ScreenshotShape::None?22+int(c.shape):c.arrow?11:10;
|
||||
for(size_t i=0;i<std::size(CursorTools);++i)
|
||||
if(CursorTools[i]==tool && c.drawingCursors[i]) return c.drawingCursors[i];
|
||||
return LoadCursorW(nullptr,IDC_CROSS);
|
||||
}
|
||||
HCURSOR actionCursor(const Capture &c,std::pair<int,MarkPart> hit,bool inside)
|
||||
{
|
||||
if(!c.editing) return LoadCursorW(nullptr,IDC_CROSS);
|
||||
LPCWSTR cursor=nullptr;
|
||||
if(hit.second==MarkPart::Body || hit.second==MarkPart::Label) cursor=IDC_SIZEALL;
|
||||
else if(hit.second==MarkPart::Rotation) cursor=IDC_HAND;
|
||||
else if(hit.second==MarkPart::Radius) cursor=IDC_SIZEWE;
|
||||
else if(hit.second==MarkPart::Start || hit.second==MarkPart::End || hit.second==MarkPart::Thickness) {
|
||||
const auto &mark=c.marks[hit.first];
|
||||
auto [from,to]=hit.second==MarkPart::Thickness?thicknessSegment(mark):std::pair{mark.from,mark.to};
|
||||
double angle=std::atan2(double(to.y-from.y),double(to.x-from.x));
|
||||
if(hit.second==MarkPart::Thickness) angle+=3.141592653589793/2;
|
||||
int axis=(int(std::lround(angle/(3.141592653589793/4)))+8)%4;
|
||||
const LPCWSTR cursors[]{IDC_SIZEWE,IDC_SIZENWSE,IDC_SIZENS,IDC_SIZENESW};
|
||||
cursor=cursors[axis];
|
||||
}
|
||||
return cursor?LoadCursorW(nullptr,cursor):inside?toolCursor(c):LoadCursorW(nullptr,IDC_ARROW);
|
||||
}
|
||||
void captureCursor(HWND w, Capture &c)
|
||||
{
|
||||
LPCWSTR cursor=c.editing && c.pointer?IDC_ARROW:IDC_CROSS;
|
||||
if(c.editing)
|
||||
{
|
||||
POINT p; GetCursorPos(&p); ScreenToClient(w,&p);
|
||||
auto hit=c.dragging?std::pair{c.selected,c.part}:markAt(c,imagePoint(c,p,false));
|
||||
if(!c.dragging && !PtInRect(&c.view,p) && hit.second!=MarkPart::Thickness && hit.second!=MarkPart::Rotation) hit={-1,MarkPart::None};
|
||||
if(!c.dragging && !PtInRect(&c.view,p)) cursor=IDC_ARROW;
|
||||
if(hit.second==MarkPart::Body || hit.second==MarkPart::Label) cursor=IDC_SIZEALL;
|
||||
else if(hit.second==MarkPart::Rotation) cursor=IDC_HAND;
|
||||
else if(hit.second==MarkPart::Radius) cursor=IDC_SIZEWE;
|
||||
else if(hit.second==MarkPart::Start || hit.second==MarkPart::End || hit.second==MarkPart::Thickness)
|
||||
{
|
||||
const auto &mark=c.marks[hit.first];
|
||||
auto [from,to]=hit.second==MarkPart::Thickness?thicknessSegment(mark):std::pair{mark.from,mark.to};
|
||||
double angle=std::atan2(double(to.y-from.y),double(to.x-from.x));
|
||||
if(hit.second==MarkPart::Thickness) angle+=3.141592653589793/2;
|
||||
int axis=(int(std::lround(angle/(3.141592653589793/4)))+8)%4;
|
||||
const LPCWSTR cursors[]{IDC_SIZEWE,IDC_SIZENWSE,IDC_SIZENS,IDC_SIZENESW};
|
||||
cursor=cursors[axis];
|
||||
}
|
||||
}
|
||||
SetCursor(c.editing && c.freehand && cursor==IDC_CROSS && c.pencilCursor?c.pencilCursor:LoadCursorW(nullptr,cursor));
|
||||
POINT p; GetCursorPos(&p); ScreenToClient(w,&p);
|
||||
bool inside=PtInRect(&c.view,p)!=FALSE;
|
||||
auto hit=c.editing?(c.dragging?std::pair{c.selected,c.part}:markAt(c,imagePoint(c,p,false))):std::pair{-1,MarkPart::None};
|
||||
if(!c.dragging && !inside && hit.second!=MarkPart::Thickness && hit.second!=MarkPart::Rotation) hit={-1,MarkPart::None};
|
||||
SetCursor(actionCursor(c,hit,inside || c.dragging));
|
||||
if(c.editing) {
|
||||
POINT p; GetCursorPos(&p); ScreenToClient(w,&p);
|
||||
c.hoveredPart=markAt(c,imagePoint(c,p,false)).second;
|
||||
@@ -987,7 +1043,7 @@ LRESULT CALLBACK captureProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
||||
for(int id=22;id<=27;++id) InvalidateRect(GetDlgItem(w,id),nullptr,FALSE);
|
||||
}
|
||||
|
||||
SetFocus(w); InvalidateRect(w,nullptr,FALSE); return 0;
|
||||
SetFocus(w); captureCursor(w,*c); InvalidateRect(w,nullptr,FALSE); return 0;
|
||||
case WM_KEYDOWN:
|
||||
if (wp==VK_ESCAPE) {
|
||||
if(!c->editing) DestroyWindow(w); // Cancel region selection only.
|
||||
|
||||
Reference in New Issue
Block a user