Release v0.6.17: constrained pencil strokes and snippet editor states

This commit is contained in:
2026-10-30 14:13:52 +02:00
parent a830f55575
commit 4eb9f6c671
11 changed files with 227 additions and 28 deletions
+99
View File
@@ -24,6 +24,105 @@ int main()
require(c.pencilCursor!=nullptr,"pencil drawing cursor loads");
require(c.freehand && !c.pointer && ks::toolCursor(c)==c.pencilCursor,
"new screenshot editor starts with the pencil tool");
{
BYTE savedKeys[256]{},keys[256]{}; GetKeyboardState(savedKeys);
std::copy(savedKeys,savedKeys+256,keys); keys[VK_SHIFT]=0; SetKeyboardState(keys);
auto mouse=[&](UINT message,POINT p,bool shift=false) {
int x=c.view.left+MulDiv(p.x,c.view.right-c.view.left,c.image->width);
int y=c.view.top+MulDiv(p.y,c.view.bottom-c.view.top,c.image->height);
SendMessageW(window,message,(message==WM_LBUTTONUP?0:MK_LBUTTON)|(shift?MK_SHIFT:0),MAKELPARAM(x,y));
};
mouse(WM_LBUTTONDOWN,{40,40},true);
mouse(WM_MOUSEMOVE,{100,50},true);
require(c.stroke.to.x==100 && c.stroke.to.y==40,"Shift constrains pencil horizontally");
mouse(WM_MOUSEMOVE,{100,90},true);
require(c.stroke.to.x==100 && c.stroke.to.y==40 && c.stroke.points.size()==2,
"50 pixel deviation keeps the current axis");
mouse(WM_MOUSEMOVE,{100,91},true);
require(c.stroke.to.x==100 && c.stroke.to.y==91 && c.stroke.points.size()==3 && c.stroke.points[1].y==40,
"51 pixel deviation turns vertically and preserves the horizontal segment");
mouse(WM_MOUSEMOVE,{70,140});
require(c.stroke.to.x==70 && c.stroke.to.y==140 && c.stroke.points.size()==4,
"releasing Shift resumes freehand in the same stroke");
SendMessageW(window,WM_KEYDOWN,VK_SHIFT,0);
mouse(WM_MOUSEMOVE,{140,150},true);
require(c.stroke.to.x==140 && c.stroke.to.y==140 && c.stroke.points[3].x==70,
"pressing Shift mid-stroke anchors at the last drawn point");
SendMessageW(window,WM_KEYUP,VK_SHIFT,0);
SendMessageW(window,WM_KEYDOWN,VK_SHIFT,0);
mouse(WM_MOUSEMOVE,{150,200},true);
mouse(WM_LBUTTONUP,{150,210},true);
SetKeyboardState(savedKeys);
require(c.marks.size()==1 && c.marks[0].to.x==140 && c.marks[0].to.y==210,
"repressing Shift without mouse movement starts a new segment and constrains mouse-up");
SendMessageW(window,WM_COMMAND,12,0);
require(c.marks.empty(),"mixed freehand and constrained segments undo as one stroke");
SendMessageW(window,WM_COMMAND,18,0);
require(c.marks.size()==1 && c.marks[0].points.size()==6,"redo preserves constrained stroke geometry");
c.marks.clear(); c.selected=-1;
for(int sx:{-1,1}) for(int sy:{-1,1}) {
mouse(WM_LBUTTONDOWN,{160,120},true);
mouse(WM_MOUSEMOVE,{160+sx,120},true);
for(int step=2;step<=90;++step)
mouse(WM_MOUSEMOVE,{160+sx*step,120+sy*step},true);
require(c.stroke.points.size()==2 && c.stroke.to.x==160+sx*90 && c.stroke.to.y==120+sy*90,
"smooth diagonal after one-pixel horizontal jitter stays diagonal without stairs");
mouse(WM_LBUTTONUP,{160+sx*90,120+sy*90},true);
c.marks.clear(); c.selected=-1;
mouse(WM_LBUTTONDOWN,{160-sx*40,120-sy*70},true);
mouse(WM_MOUSEMOVE,{160,120-sy*70},true);
for(int step=1;step<=90;++step)
mouse(WM_MOUSEMOVE,{160+sx*step,120-sy*70+sy*step},true);
require(c.stroke.points.size()==3 && c.strokeAxis>=3 &&
std::abs(c.stroke.to.x-c.stroke.points[1].x)==std::abs(c.stroke.to.y-c.stroke.points[1].y),
"smooth turn from horizontal into diagonal uses raw mouse direction without stairs");
mouse(WM_LBUTTONUP,{160+sx*90,120-sy*70+sy*90},true);
c.marks.clear(); c.selected=-1;
}
for(int sx:{-1,1}) for(int sy:{-1,1}) {
mouse(WM_LBUTTONDOWN,{160,120},true);
mouse(WM_MOUSEMOVE,{160+sx*60,120+sy*50},true);
require(c.stroke.to.x==160+sx*55 && c.stroke.to.y==120+sy*55,
"Shift snaps to exact 45 degree diagonals in all four directions");
mouse(WM_LBUTTONUP,{160+sx*60,120+sy*50},true);
c.marks.clear(); c.selected=-1;
}
mouse(WM_LBUTTONDOWN,{40,40},true);
mouse(WM_MOUSEMOVE,{100,40},true);
mouse(WM_MOUSEMOVE,{160,100},true);
require(c.stroke.points.size()==3 && c.stroke.to.x==160 && c.stroke.to.y==100 &&
c.stroke.points[1].x==100 && c.stroke.points[1].y==40,
"large deviation turns a horizontal segment diagonally without losing its corner");
mouse(WM_MOUSEMOVE,{200,100},true);
require(c.stroke.to.x==180 && c.stroke.to.y==120 && c.stroke.points.size()==3,
"small perpendicular deviation keeps the diagonal axis");
mouse(WM_MOUSEMOVE,{260,120},true);
require(c.stroke.to.x==260 && c.stroke.to.y==120 && c.stroke.points.size()==4,
"diagonal deviation beyond 50 pixels turns back to horizontal");
mouse(WM_LBUTTONUP,{260,120},true);
c.marks.clear(); c.selected=-1;
for(int sx:{-1,1}) for(int sy:{-1,1}) {
POINT start{sx>0?310L:9L,120};
mouse(WM_LBUTTONDOWN,start,true);
mouse(WM_MOUSEMOVE,{start.x+sx*8,start.y+sy*8},true);
mouse(WM_MOUSEMOVE,{sx>0?319L:0L,start.y+sy*25},true);
auto end=c.stroke.to;
require(end.x>=0 && end.x<320 && end.y>=0 && end.y<240 &&
std::abs(end.x-start.x)==std::abs(end.y-start.y),
"diagonal stays at 45 degrees and inside image boundaries");
mouse(WM_LBUTTONUP,{sx>0?319L:0L,start.y+sy*25},true);
c.marks.clear(); c.selected=-1;
}
keys[VK_CONTROL]=0x80; SetKeyboardState(keys);
mouse(WM_LBUTTONDOWN,{40,40});
mouse(WM_MOUSEMOVE,{100,70});
mouse(WM_LBUTTONUP,{120,90});
SetKeyboardState(savedKeys);
require(c.marks.size()==1 && c.marks[0].points[1].x==100 && c.marks[0].points[1].y==70 &&
c.marks[0].to.x==120 && c.marks[0].to.y==90,
"Ctrl keeps its existing drawing behavior without constraining the pencil");
c.marks.clear(); c.selected=-1;
}
for(int size:{32,48,64,96}) {
auto cursor=(HCURSOR)LoadImageW(app.instance,MAKEINTRESOURCEW(IDC_SCREENSHOT_PENCIL),IMAGE_CURSOR,size,size,0);
ICONINFO info{};