win32_progress_callback.cpp (7135B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "win32_progress_callback.h" 5 6 #include "common/log.h" 7 #include "common/string_util.h" 8 9 #include <CommCtrl.h> 10 11 Log_SetChannel(Win32ProgressCallback); 12 13 Win32ProgressCallback::Win32ProgressCallback() : ProgressCallback() 14 { 15 Create(); 16 } 17 18 void Win32ProgressCallback::PushState() 19 { 20 ProgressCallback::PushState(); 21 } 22 23 void Win32ProgressCallback::PopState() 24 { 25 ProgressCallback::PopState(); 26 Redraw(true); 27 } 28 29 void Win32ProgressCallback::SetCancellable(bool cancellable) 30 { 31 ProgressCallback::SetCancellable(cancellable); 32 Redraw(true); 33 } 34 35 void Win32ProgressCallback::SetTitle(const std::string_view title) 36 { 37 SetWindowTextW(m_window_hwnd, StringUtil::UTF8StringToWideString(title).c_str()); 38 } 39 40 void Win32ProgressCallback::SetStatusText(const std::string_view text) 41 { 42 ProgressCallback::SetStatusText(text); 43 Redraw(true); 44 } 45 46 void Win32ProgressCallback::SetProgressRange(u32 range) 47 { 48 ProgressCallback::SetProgressRange(range); 49 Redraw(false); 50 } 51 52 void Win32ProgressCallback::SetProgressValue(u32 value) 53 { 54 ProgressCallback::SetProgressValue(value); 55 Redraw(false); 56 } 57 58 bool Win32ProgressCallback::Create() 59 { 60 static const char* CLASS_NAME = "DSWin32ProgressCallbackWindow"; 61 static bool class_registered = false; 62 63 if (!class_registered) 64 { 65 InitCommonControls(); 66 67 WNDCLASSEX wc = {}; 68 wc.cbSize = sizeof(WNDCLASSEX); 69 wc.lpfnWndProc = WndProcThunk; 70 wc.hInstance = GetModuleHandle(nullptr); 71 // wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)); 72 // wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)); 73 wc.hCursor = LoadCursor(NULL, IDC_WAIT); 74 wc.hbrBackground = (HBRUSH)COLOR_WINDOW; 75 wc.lpszClassName = CLASS_NAME; 76 if (!RegisterClassExA(&wc)) 77 { 78 ERROR_LOG("Failed to register window class"); 79 return false; 80 } 81 82 class_registered = true; 83 } 84 85 m_window_hwnd = 86 CreateWindowExA(WS_EX_CLIENTEDGE, CLASS_NAME, "Win32ProgressCallback", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 87 CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, nullptr, nullptr, GetModuleHandle(nullptr), this); 88 if (!m_window_hwnd) 89 { 90 ERROR_LOG("Failed to create window"); 91 return false; 92 } 93 94 SetWindowLongPtr(m_window_hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); 95 ShowWindow(m_window_hwnd, SW_SHOW); 96 PumpMessages(); 97 return true; 98 } 99 100 void Win32ProgressCallback::Destroy() 101 { 102 if (!m_window_hwnd) 103 return; 104 105 DestroyWindow(m_window_hwnd); 106 m_window_hwnd = {}; 107 m_text_hwnd = {}; 108 m_progress_hwnd = {}; 109 } 110 111 void Win32ProgressCallback::PumpMessages() 112 { 113 MSG msg; 114 while (PeekMessageA(&msg, m_window_hwnd, 0, 0, PM_REMOVE)) 115 { 116 TranslateMessage(&msg); 117 DispatchMessageA(&msg); 118 } 119 } 120 121 void Win32ProgressCallback::Redraw(bool force) 122 { 123 const int percent = 124 static_cast<int>((static_cast<float>(m_progress_value) / static_cast<float>(m_progress_range)) * 100.0f); 125 if (percent == m_last_progress_percent && !force) 126 { 127 PumpMessages(); 128 return; 129 } 130 131 m_last_progress_percent = percent; 132 133 SendMessageA(m_progress_hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, m_progress_range)); 134 SendMessageA(m_progress_hwnd, PBM_SETPOS, static_cast<WPARAM>(m_progress_value), 0); 135 SetWindowTextA(m_text_hwnd, m_status_text.c_str()); 136 RedrawWindow(m_text_hwnd, nullptr, nullptr, RDW_INVALIDATE); 137 PumpMessages(); 138 } 139 140 LRESULT CALLBACK Win32ProgressCallback::WndProcThunk(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 141 { 142 Win32ProgressCallback* cb; 143 if (msg == WM_CREATE) 144 { 145 const CREATESTRUCTA* cs = reinterpret_cast<CREATESTRUCTA*>(lparam); 146 cb = static_cast<Win32ProgressCallback*>(cs->lpCreateParams); 147 } 148 else 149 { 150 cb = reinterpret_cast<Win32ProgressCallback*>(GetWindowLongPtrA(hwnd, GWLP_USERDATA)); 151 } 152 153 return cb->WndProc(hwnd, msg, wparam, lparam); 154 } 155 156 LRESULT CALLBACK Win32ProgressCallback::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 157 { 158 switch (msg) 159 { 160 case WM_CREATE: 161 { 162 const CREATESTRUCTA* cs = reinterpret_cast<CREATESTRUCTA*>(lparam); 163 HFONT default_font = reinterpret_cast<HFONT>(GetStockObject(ANSI_VAR_FONT)); 164 SendMessageA(hwnd, WM_SETFONT, WPARAM(default_font), TRUE); 165 166 int y = WINDOW_MARGIN; 167 168 m_text_hwnd = CreateWindowExA(0, "Static", nullptr, WS_VISIBLE | WS_CHILD, WINDOW_MARGIN, y, SUBWINDOW_WIDTH, 16, 169 hwnd, nullptr, cs->hInstance, nullptr); 170 SendMessageA(m_text_hwnd, WM_SETFONT, WPARAM(default_font), TRUE); 171 y += 16 + WINDOW_MARGIN; 172 173 m_progress_hwnd = CreateWindowExA(0, PROGRESS_CLASSA, nullptr, WS_VISIBLE | WS_CHILD, WINDOW_MARGIN, y, 174 SUBWINDOW_WIDTH, 32, hwnd, nullptr, cs->hInstance, nullptr); 175 y += 32 + WINDOW_MARGIN; 176 177 m_list_box_hwnd = 178 CreateWindowExA(0, "LISTBOX", nullptr, WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_BORDER | LBS_NOSEL, 179 WINDOW_MARGIN, y, SUBWINDOW_WIDTH, 170, hwnd, nullptr, cs->hInstance, nullptr); 180 SendMessageA(m_list_box_hwnd, WM_SETFONT, WPARAM(default_font), TRUE); 181 y += 170; 182 } 183 break; 184 185 default: 186 return DefWindowProcA(hwnd, msg, wparam, lparam); 187 } 188 189 return 0; 190 } 191 192 void Win32ProgressCallback::DisplayError(const std::string_view message) 193 { 194 ERROR_LOG(message); 195 SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, 196 reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); 197 SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); 198 PumpMessages(); 199 } 200 201 void Win32ProgressCallback::DisplayWarning(const std::string_view message) 202 { 203 WARNING_LOG(message); 204 SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, 205 reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); 206 SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); 207 PumpMessages(); 208 } 209 210 void Win32ProgressCallback::DisplayInformation(const std::string_view message) 211 { 212 INFO_LOG(message); 213 SendMessageW(m_list_box_hwnd, LB_ADDSTRING, 0, 214 reinterpret_cast<LPARAM>(StringUtil::UTF8StringToWideString(message).c_str())); 215 SendMessageW(m_list_box_hwnd, WM_VSCROLL, SB_BOTTOM, 0); 216 PumpMessages(); 217 } 218 219 void Win32ProgressCallback::DisplayDebugMessage(const std::string_view message) 220 { 221 DEV_LOG(message); 222 } 223 224 void Win32ProgressCallback::ModalError(const std::string_view message) 225 { 226 PumpMessages(); 227 MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Error", MB_ICONERROR | MB_OK); 228 PumpMessages(); 229 } 230 231 bool Win32ProgressCallback::ModalConfirmation(const std::string_view message) 232 { 233 PumpMessages(); 234 bool result = MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Confirmation", 235 MB_ICONQUESTION | MB_YESNO) == IDYES; 236 PumpMessages(); 237 return result; 238 } 239 240 void Win32ProgressCallback::ModalInformation(const std::string_view message) 241 { 242 MessageBoxW(m_window_hwnd, StringUtil::UTF8StringToWideString(message).c_str(), L"Information", 243 MB_ICONINFORMATION | MB_OK); 244 }