Release v0.5.0: isolate screenshot editor and support manifest-based updates

This commit is contained in:
2026-10-28 22:52:52 +02:00
parent d1eabfca88
commit ccaa8dad3f
24 changed files with 498 additions and 51 deletions
+30 -15
View File
@@ -1,4 +1,3 @@
#include "app.h"
#include "screenshot.h"
#include "../resources/resource.h"
#include <algorithm>
@@ -13,14 +12,21 @@ namespace
{
// Only selection is exclusive; each completed capture owns an independent editor.
HWND activeCapture{};
// Keep titles stable and distinct for every editor opened during this session.
unsigned long long nextEditorIndex=1;
bool confirmScreenshotClose(HWND owner)
{
TASKDIALOGCONFIG dialog{sizeof(dialog)};
dialog.hwndParent=owner;
dialog.dwFlags=TDF_POSITION_RELATIVE_TO_WINDOW|TDF_ALLOW_DIALOG_CANCELLATION;
dialog.dwFlags=TDF_POSITION_RELATIVE_TO_WINDOW|TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_HICON_MAIN;
const int iconSize=MulDiv(32,GetDpiForWindow(owner),96);
auto icon=(HICON)LoadImageW(GetModuleHandleW(nullptr),MAKEINTRESOURCEW(IDI_SCREENSHOT_QUESTION),
IMAGE_ICON,iconSize,iconSize,0);
dialog.hMainIcon=icon;
dialog.dwCommonButtons=TDCBF_YES_BUTTON|TDCBF_NO_BUTTON;
dialog.pszWindowTitle=L"KSwitcher";
dialog.pszContent=L"Close without copying this screenshot to the clipboard?";
dialog.pszMainInstruction=L"Close without copying?";
dialog.pszContent=L"Your latest screenshot changes have not been copied to the clipboard.";
dialog.nDefaultButton=IDNO;
dialog.lpCallbackData=(LONG_PTR)owner;
dialog.pfCallback=[](HWND window,UINT notification,WPARAM,LPARAM,LONG_PTR data)->HRESULT {
@@ -40,6 +46,7 @@ bool confirmScreenshotClose(HWND owner)
};
int button=IDNO;
auto result=TaskDialogIndirect(&dialog,&button,nullptr,nullptr);
if(icon) DestroyIcon(icon);
// If Windows cannot display the confirmation, keep the screenshot open.
return SUCCEEDED(result) && button==IDYES;
}
@@ -115,7 +122,7 @@ bool chooseTextFont(HWND owner,Mark &m)
}
struct Capture
{
App &app;
ScreenshotHost &app;
std::unique_ptr<Surface> image;
std::unique_ptr<Surface> dimmed, frame;
bool editing{}, dragging{}, arrow{}, owned{}, copied{};
@@ -153,7 +160,7 @@ struct Capture
int hoveredTool=0;
MarkPart hoveredPart=MarkPart::None;
std::wstring statusText;
explicit Capture(App &a) : app(a) {}
explicit Capture(ScreenshotHost &a) : app(a) {}
~Capture() {
if(pencilCursor) DestroyCursor(pencilCursor);
if (font) DeleteObject(font);
@@ -162,6 +169,7 @@ struct Capture
}
void remember(const std::vector<Mark> &previous)
{
copied=false;
history.remember(previous);
syncHistory();
}
@@ -720,14 +728,13 @@ LRESULT CALLBACK toolHover(HWND w, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR, DWO
if(msg==WM_MOUSELEAVE) InvalidateRect(w,nullptr,FALSE);
return DefSubclassProc(w,msg,wp,lp);
}
void copy(HWND w, Capture &c)
void copy(HWND w, Capture &c, DWORD (*publish)(HWND,const Clip &)=writeClipboard)
{
finishInlineText(w,c,true);
// The resident message window owns the clipboard after this editor closes.
// The resident message window keeps ownership if the editor is closed later.
auto clip=screenshotClip(c.image->dc,c.image->width,c.image->height,c.marks);
writeClipboard(c.app.window, clip);
publish(c.app.window, clip);
c.copied=true;
DestroyWindow(w);
}
void edit(HWND w, Capture &c)
{
@@ -745,8 +752,11 @@ void edit(HWND w, Capture &c)
POINT cursor; GetCursorPos(&cursor);
GetMonitorInfoW(MonitorFromPoint(cursor,MONITOR_DEFAULTTOPRIMARY), &monitor);
ShowWindow(w,SW_HIDE);
SetWindowLongPtrW(w,GWLP_HWNDPARENT,0);
SetWindowLongPtrW(w,GWL_EXSTYLE,(GetWindowLongPtrW(w,GWL_EXSTYLE)|WS_EX_APPWINDOW)&~WS_EX_TOOLWINDOW);
SetWindowLongPtrW(w,GWL_STYLE,WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN);
SetWindowTextW(w,L"KSwitcher — Screenshot");
auto title=L"KSwitcher — Screenshot #"+std::to_wstring(nextEditorIndex++);
SetWindowTextW(w,title.c_str());
INITCOMMONCONTROLSEX statusControls{sizeof(statusControls),ICC_BAR_CLASSES};
InitCommonControlsEx(&statusControls);
CreateWindowExW(0,STATUSCLASSNAMEW,L"",WS_CHILD|WS_VISIBLE|SBARS_TOOLTIPS,
@@ -806,7 +816,8 @@ LRESULT CALLBACK captureProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
switch (msg)
{
case WM_CLOSE:
if(c->editing && !c->copied && !confirmScreenshotClose(w)) return 0;
finishInlineText(w,*c,true);
if(c->editing && (!c->copied || c->dragging) && !confirmScreenshotClose(w)) return 0;
DestroyWindow(w); return 0;
case FinishTextMessage:
if(c->inlineText && (HWND)lp==c->inlineText && (wp!=0 || GetFocus()!=c->inlineText)) {
@@ -908,7 +919,7 @@ LRESULT CALLBACK captureProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
if (LOWORD(wp)==12 || LOWORD(wp)==18)
{
bool changed=LOWORD(wp)==12?c->history.undo(c->marks):c->history.redo(c->marks);
if(changed) { c->selected=-1; c->selectedLabel=false; c->wheelDelta=0; c->wheelTarget=-1; }
if(changed) { c->copied=false; c->selected=-1; c->selectedLabel=false; c->wheelDelta=0; c->wheelTarget=-1; }
c->syncHistory();
}
if (LOWORD(wp)==10 || LOWORD(wp)==11 || LOWORD(wp)==19 || (LOWORD(wp)>=21 && LOWORD(wp)<=26))
@@ -1226,7 +1237,11 @@ LRESULT CALLBACK captureProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
case WM_NCDESTROY:
if (activeCapture == w) activeCapture=nullptr;
SetWindowLongPtrW(w,GWLP_USERDATA,0);
if (c->owned) delete c;
if (c->owned) {
auto &host=c->app;
if (--host.windows==0) PostMessageW(host.window,ScreenshotIdle,0,0);
delete c;
}
return DefWindowProcW(w,msg,wp,lp);
}
}
@@ -1498,10 +1513,9 @@ Clip screenshotClip(HDC source, int width, int height, const std::vector<Screens
memcpy(bytes.data()+sizeof(header),output.pixels,bytes.size()-sizeof(header));
return Clip{{{CF_DIB,L"",std::move(bytes)}}};
}
void showScreenshot(App &app)
void showScreenshot(ScreenshotHost &app)
{
if (activeCapture) { showRequestedWindow(activeCapture); SetForegroundWindow(activeCapture); return; }
app.resetInput();
auto c=std::make_unique<Capture>(app);
int x=GetSystemMetrics(SM_XVIRTUALSCREEN),y=GetSystemMetrics(SM_YVIRTUALSCREEN);
int width=GetSystemMetrics(SM_CXVIRTUALSCREEN),height=GetSystemMetrics(SM_CYVIRTUALSCREEN);
@@ -1528,6 +1542,7 @@ void showScreenshot(App &app)
WS_POPUP,x,y,width,height,app.window,nullptr,app.instance,c.get());
if (!activeCapture) throw std::runtime_error("Cannot open screen capture");
c->owned=true;
++app.windows;
c.release();
showRequestedWindow(activeCapture); SetForegroundWindow(activeCapture); SetFocus(activeCapture);
}