media:cpp --uinext win32xx 2/2

This commit is contained in:
lovebird 2026-04-15 21:01:39 +02:00
parent c18f4878e8
commit 3a1ac7ff00
9 changed files with 267 additions and 2 deletions

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,7 @@
#include "Mainfrm.h" #include "Mainfrm.h"
#include "Resource.h" #include "Resource.h"
#include "core/resize.hpp" #include "core/resize.hpp"
#include "core/transform.hpp"
#include <shlobj.h> #include <shlobj.h>
#include <filesystem> #include <filesystem>
@ -36,6 +37,7 @@ STDMETHODIMP CMainFrame::Execute(UINT32 cmdID, UI_EXECUTIONVERB verb,
case IDC_CMD_ADD_FOLDER: OnAddFolder(); break; case IDC_CMD_ADD_FOLDER: OnAddFolder(); break;
case IDC_CMD_CLEAR: OnClearQueue(); break; case IDC_CMD_CLEAR: OnClearQueue(); break;
case IDC_CMD_RESIZE: OnResize(); break; case IDC_CMD_RESIZE: OnResize(); break;
case IDC_CMD_TRANSFORM: OnTransform(); break;
case IDC_CMD_ABOUT: OnHelp(); break; case IDC_CMD_ABOUT: OnHelp(); break;
case IDC_CMD_EXIT: OnExit(); break; case IDC_CMD_EXIT: OnExit(); break;
case IDC_RIBBONHELP: OnHelp(); break; case IDC_RIBBONHELP: OnHelp(); break;
@ -256,6 +258,201 @@ void CMainFrame::OnResize()
GetStatusBar().SetPartText(0, L"Resizing\u2026"); GetStatusBar().SetPartText(0, L"Resizing\u2026");
} }
std::vector<int> CMainFrame::GetSelectedQueueItems()
{
std::vector<int> sel;
if (!m_pDockQueue) return sel;
auto& lv = m_pDockQueue->GetQueueContainer().GetListView();
int idx = -1;
while ((idx = lv.GetNextItem(idx, LVNI_SELECTED)) != -1)
sel.push_back(idx);
if (sel.empty()) {
int total = lv.QueueCount();
for (int i = 0; i < total; ++i) sel.push_back(i);
}
return sel;
}
static INT_PTR CALLBACK PromptDlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_INITDIALOG: {
::SetWindowLongPtr(hwnd, GWLP_USERDATA, lp);
auto* prompt = reinterpret_cast<std::wstring*>(lp);
if (!prompt->empty())
::SetDlgItemTextW(hwnd, IDC_EDIT_PROMPT, prompt->c_str());
RECT rc;
::GetClientRect(hwnd, &rc);
int w = rc.right - rc.left, h = rc.bottom - rc.top;
int margin = 10;
HWND hLabel = ::GetDlgItem(hwnd, IDC_STATIC_PROMPT_LABEL);
::SetWindowPos(hLabel, nullptr, margin, margin, w - 2*margin, 20, SWP_NOZORDER);
HWND hEdit = ::GetDlgItem(hwnd, IDC_EDIT_PROMPT);
::SetWindowPos(hEdit, nullptr, margin, 35, w - 2*margin, h - 80, SWP_NOZORDER);
HWND hOk = ::GetDlgItem(hwnd, IDOK);
HWND hCancel = ::GetDlgItem(hwnd, IDCANCEL);
int btnW = 80, btnH = 28;
::SetWindowPos(hOk, nullptr, w - 2*(btnW + margin), h - btnH - margin, btnW, btnH, SWP_NOZORDER);
::SetWindowPos(hCancel, nullptr, w - btnW - margin, h - btnH - margin, btnW, btnH, SWP_NOZORDER);
HFONT hFont = static_cast<HFONT>(::GetStockObject(DEFAULT_GUI_FONT));
::SendMessage(hLabel, WM_SETFONT, (WPARAM)hFont, TRUE);
::SendMessage(hEdit, WM_SETFONT, (WPARAM)hFont, TRUE);
::SendMessage(hOk, WM_SETFONT, (WPARAM)hFont, TRUE);
::SendMessage(hCancel, WM_SETFONT, (WPARAM)hFont, TRUE);
::SetFocus(hEdit);
return FALSE;
}
case WM_COMMAND:
if (LOWORD(wp) == IDOK) {
auto* prompt = reinterpret_cast<std::wstring*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
wchar_t buf[4096]{};
::GetDlgItemTextW(hwnd, IDC_EDIT_PROMPT, buf, 4095);
*prompt = buf;
::EndDialog(hwnd, IDOK);
return TRUE;
}
if (LOWORD(wp) == IDCANCEL) {
::EndDialog(hwnd, IDCANCEL);
return TRUE;
}
break;
case WM_CLOSE:
::EndDialog(hwnd, IDCANCEL);
return TRUE;
}
return FALSE;
}
static HWND CreatePromptDialog(HWND parent, std::wstring& prompt)
{
// Build a dialog template in memory
alignas(DWORD) BYTE buf[2048]{};
DLGTEMPLATE* dlg = reinterpret_cast<DLGTEMPLATE*>(buf);
dlg->style = DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE;
dlg->cdit = 4; // label, edit, OK, Cancel
dlg->cx = 280; dlg->cy = 160;
WORD* p = reinterpret_cast<WORD*>(dlg + 1);
*p++ = 0; // menu
*p++ = 0; // class
// title "AI Transform - Enter Prompt"
const wchar_t title[] = L"AI Transform - Enter Prompt";
memcpy(p, title, sizeof(title));
p += (sizeof(title) / sizeof(WORD));
// Align to DWORD
if (reinterpret_cast<uintptr_t>(p) % 4) p++;
auto addItem = [&](DWORD style, short x, short y, short cx, short cy, WORD id, const wchar_t* cls, const wchar_t* text) {
if (reinterpret_cast<uintptr_t>(p) % 4) p++;
DLGITEMTEMPLATE* item = reinterpret_cast<DLGITEMTEMPLATE*>(p);
item->style = style | WS_CHILD | WS_VISIBLE;
item->x = x; item->y = y; item->cx = cx; item->cy = cy;
item->id = id;
p = reinterpret_cast<WORD*>(item + 1);
// class
size_t clen = wcslen(cls) + 1;
memcpy(p, cls, clen * 2); p += clen;
// text
size_t tlen = wcslen(text) + 1;
memcpy(p, text, tlen * 2); p += tlen;
*p++ = 0; // extra
};
// Label
addItem(SS_LEFT, 6, 4, 268, 10, IDC_STATIC_PROMPT_LABEL,
L"Static", L"Describe the transformation:");
// Multiline edit
addItem(ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_BORDER | WS_VSCROLL | WS_TABSTOP,
6, 18, 268, 110, IDC_EDIT_PROMPT, L"Edit", L"");
// OK button
addItem(BS_DEFPUSHBUTTON | WS_TABSTOP, 150, 134, 56, 18, IDOK, L"Button", L"Transform");
// Cancel button
addItem(WS_TABSTOP, 212, 134, 56, 18, IDCANCEL, L"Button", L"Cancel");
INT_PTR result = ::DialogBoxIndirectParam(
::GetModuleHandle(nullptr),
dlg, parent, PromptDlgProc,
reinterpret_cast<LPARAM>(&prompt));
return (result == IDOK) ? parent : nullptr;
}
void CMainFrame::OnTransform()
{
if (m_processing) {
::MessageBox(GetHwnd(), L"Already processing.", L"pm-image", MB_ICONWARNING);
return;
}
if (!m_pDockQueue) return;
auto indices = GetSelectedQueueItems();
if (indices.empty()) {
::MessageBox(GetHwnd(), L"Queue is empty \u2014 drop files first.", L"pm-image", MB_ICONINFORMATION);
return;
}
std::wstring prompt = m_lastPrompt;
if (!CreatePromptDialog(GetHwnd(), prompt))
return;
if (prompt.empty()) {
::MessageBox(GetHwnd(), L"Prompt cannot be empty.", L"pm-image", MB_ICONWARNING);
return;
}
m_lastPrompt = prompt;
std::string promptUtf8 = wide_to_utf8(prompt);
// Resolve API key
std::string api_key;
const char* env_key = std::getenv("IMAGE_TRANSFORM_GOOGLE_API_KEY");
if (env_key && env_key[0] != '\0') api_key = env_key;
if (api_key.empty()) {
::MessageBox(GetHwnd(),
L"API key not found.\nSet IMAGE_TRANSFORM_GOOGLE_API_KEY in your .env file.",
L"pm-image", MB_ICONERROR);
return;
}
auto& lv = m_pDockQueue->GetQueueContainer().GetListView();
std::vector<std::pair<int, std::string>> items;
for (int idx : indices) {
CString p = lv.GetItemPath(idx);
items.emplace_back(idx, wide_to_utf8(std::wstring(p.c_str())));
}
m_processing = true;
HWND hwnd = GetHwnd();
m_worker = std::thread([items = std::move(items), promptUtf8, api_key, hwnd]() {
int ok = 0, fail = 0;
for (auto& [idx, input] : items) {
::PostMessage(hwnd, UWM_TRANSFORM_PROGRESS, (WPARAM)idx, 1);
media::TransformOptions topts;
topts.prompt = promptUtf8;
topts.api_key = api_key;
std::string output = media::default_transform_output(input, promptUtf8);
auto result = media::transform_image(input, output, topts, nullptr);
::PostMessage(hwnd, UWM_TRANSFORM_PROGRESS, (WPARAM)idx, result.ok ? 2 : 3);
if (result.ok) ++ok; else ++fail;
}
::PostMessage(hwnd, UWM_TRANSFORM_DONE, (WPARAM)ok, (LPARAM)fail);
});
m_worker.detach();
GetStatusBar().SetPartText(0, L"Transforming\u2026");
}
void CMainFrame::OnExit() void CMainFrame::OnExit()
{ {
Close(); Close();
@ -298,13 +495,43 @@ LRESULT CMainFrame::OnQueueDone(WPARAM wparam, LPARAM lparam)
return 0; return 0;
} }
LRESULT CMainFrame::OnTransformProgress(WPARAM wparam, LPARAM lparam)
{
if (!m_pDockQueue) return 0;
auto& lv = m_pDockQueue->GetQueueContainer().GetListView();
int idx = static_cast<int>(wparam);
int state = static_cast<int>(lparam);
if (state == 1)
lv.SetItemStatus(idx, L"Transforming\u2026");
else if (state == 2)
lv.SetItemStatus(idx, L"\u2713 Transformed");
else
lv.SetItemStatus(idx, L"\u2717 AI Error");
return 0;
}
LRESULT CMainFrame::OnTransformDone(WPARAM wparam, LPARAM lparam)
{
m_processing = false;
int ok = static_cast<int>(wparam);
int fail = static_cast<int>(lparam);
CString s;
s.Format(L"Transform done: %d succeeded, %d failed", ok, fail);
GetStatusBar().SetPartText(0, s);
if (fail > 0)
::MessageBox(GetHwnd(), s, L"pm-image", MB_ICONWARNING);
return 0;
}
LRESULT CMainFrame::WndProc(UINT msg, WPARAM wparam, LPARAM lparam) LRESULT CMainFrame::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{ {
try { try {
switch (msg) { switch (msg) {
case WM_GETMINMAXINFO: return OnGetMinMaxInfo(msg, wparam, lparam); case WM_GETMINMAXINFO: return OnGetMinMaxInfo(msg, wparam, lparam);
case UWM_QUEUE_PROGRESS: return OnQueueProgress(wparam, lparam); case UWM_QUEUE_PROGRESS: return OnQueueProgress(wparam, lparam);
case UWM_QUEUE_DONE: return OnQueueDone(wparam, lparam); case UWM_QUEUE_DONE: return OnQueueDone(wparam, lparam);
case UWM_TRANSFORM_PROGRESS: return OnTransformProgress(wparam, lparam);
case UWM_TRANSFORM_DONE: return OnTransformDone(wparam, lparam);
case UWM_QUEUE_ITEM_CLICKED: { case UWM_QUEUE_ITEM_CLICKED: {
int idx = static_cast<int>(wparam); int idx = static_cast<int>(wparam);
if (m_pDockQueue) { if (m_pDockQueue) {

View File

@ -32,11 +32,16 @@ private:
void OnAddFolder(); void OnAddFolder();
void OnClearQueue(); void OnClearQueue();
void OnResize(); void OnResize();
void OnTransform();
void OnExit(); void OnExit();
LRESULT OnGetMinMaxInfo(UINT msg, WPARAM wparam, LPARAM lparam); LRESULT OnGetMinMaxInfo(UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT OnQueueProgress(WPARAM wparam, LPARAM lparam); LRESULT OnQueueProgress(WPARAM wparam, LPARAM lparam);
LRESULT OnQueueDone(WPARAM wparam, LPARAM lparam); LRESULT OnQueueDone(WPARAM wparam, LPARAM lparam);
LRESULT OnTransformProgress(WPARAM wparam, LPARAM lparam);
LRESULT OnTransformDone(WPARAM wparam, LPARAM lparam);
std::vector<int> GetSelectedQueueItems();
CImagePreview m_view; CImagePreview m_view;
IUIRibbon* m_pIUIRibbon = nullptr; IUIRibbon* m_pIUIRibbon = nullptr;
@ -45,6 +50,7 @@ private:
std::thread m_worker; std::thread m_worker;
bool m_processing = false; bool m_processing = false;
std::wstring m_lastPrompt;
}; };
#endif // PM_UI_MAINFRM_H #endif // PM_UI_MAINFRM_H

View File

@ -25,10 +25,17 @@
#define IDC_CHK_AUTOROT 608 #define IDC_CHK_AUTOROT 608
#define IDC_CHK_STRIP 609 #define IDC_CHK_STRIP 609
// AI Transform dialog controls
#define IDD_TRANSFORM_PROMPT 700
#define IDC_EDIT_PROMPT 701
#define IDC_STATIC_PROMPT_LABEL 702
// User messages // User messages
#define UWM_QUEUE_PROGRESS (WM_USER + 100) #define UWM_QUEUE_PROGRESS (WM_USER + 100)
#define UWM_QUEUE_DONE (WM_USER + 101) #define UWM_QUEUE_DONE (WM_USER + 101)
#define UWM_IMAGELOADED (WM_USER + 102) #define UWM_IMAGELOADED (WM_USER + 102)
#define UWM_QUEUE_ITEM_CLICKED (WM_USER + 103) #define UWM_QUEUE_ITEM_CLICKED (WM_USER + 103)
#define UWM_TRANSFORM_PROGRESS (WM_USER + 104)
#define UWM_TRANSFORM_DONE (WM_USER + 105)
#endif // PM_UI_RESOURCE_H #endif // PM_UI_RESOURCE_H

Binary file not shown.

View File

@ -30,6 +30,14 @@
#define IDC_CMD_EXIT_LabelTitle_RESID 3041 #define IDC_CMD_EXIT_LabelTitle_RESID 3041
#define IDC_CMD_ABOUT 305 #define IDC_CMD_ABOUT 305
#define IDC_CMD_ABOUT_LabelTitle_RESID 3051 #define IDC_CMD_ABOUT_LabelTitle_RESID 3051
#define cmdTabAI 410
#define cmdTabAI_LabelTitle_RESID 4101
#define cmdGroupTransform 411
#define cmdGroupTransform_LabelTitle_RESID 4111
#define IDC_CMD_TRANSFORM 310
#define IDC_CMD_TRANSFORM_LabelTitle_RESID 3101
#define IDC_CMD_TRANSFORM_SmallImages_RESID 3103
#define IDC_CMD_TRANSFORM_LargeImages_RESID 3102
#define cmdAppMenu 710 #define cmdAppMenu 710
#define IDC_RIBBONHELP 700 #define IDC_RIBBONHELP 700
#define IDC_QAT 701 #define IDC_QAT 701

View File

@ -57,4 +57,21 @@ BEGIN
IDC_CMD_ABOUT_LabelTitle_RESID L"About" /* LabelTitle IDC_CMD_ABOUT_LabelTitle_RESID: (null) */ IDC_CMD_ABOUT_LabelTitle_RESID L"About" /* LabelTitle IDC_CMD_ABOUT_LabelTitle_RESID: (null) */
END END
APPLICATION_RIBBON UIFILE "C:/Users/zx/Desktop/polymech/polymech-mono/packages/media/cpp/src/win/ui_next/Ribbon.bml" STRINGTABLE
BEGIN
cmdTabAI_LabelTitle_RESID L"AI Transform" /* LabelTitle cmdTabAI_LabelTitle_RESID: (null) */
END
STRINGTABLE
BEGIN
cmdGroupTransform_LabelTitle_RESID L"Transform" /* LabelTitle cmdGroupTransform_LabelTitle_RESID: (null) */
END
STRINGTABLE
BEGIN
IDC_CMD_TRANSFORM_LabelTitle_RESID L"Transform" /* LabelTitle IDC_CMD_TRANSFORM_LabelTitle_RESID: (null) */
END
IDC_CMD_TRANSFORM_SmallImages_RESID BITMAP "res\\TransformS.bmp" /* SmallImages IDC_CMD_TRANSFORM_SmallImages_RESID: (null) */
IDC_CMD_TRANSFORM_LargeImages_RESID BITMAP "res\\TransformL.bmp" /* LargeImages IDC_CMD_TRANSFORM_LargeImages_RESID: (null) */
APPLICATION_RIBBON UIFILE "C:\\Users\\zx\\Desktop\\polymech\\polymech-mono\\packages\\media\\cpp\\src\\win\\ui_next\\RibbonUI.bml"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

After

Width:  |  Height:  |  Size: 266 KiB