Skip to content

Commit dd22d55

Browse files
Functional changes for optional update UI (#108)
* Initial commit adding optional update UI Add '--details' parameter to command line parser, display UI with update info for user to update or wait. * Code review feedback
1 parent d6fe431 commit dd22d55

File tree

3 files changed

+133
-5
lines changed

3 files changed

+133
-5
lines changed

Diff for: src/cli-parser.cc

+6-3
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,20 @@ bool su_parse_command_line(int argc, char **argv, struct update_parameters *para
176176

177177
struct arg_lit *restart_arg = arg_lit0(NULL, "restart-after-fail", "Start Streamlabs Desktop after update fail with option to skip update");
178178

179+
struct arg_str *details_arg = arg_str1("d", "details", "<details>", "The details of the update");
180+
179181
struct arg_end *end_arg = arg_end(255);
180182

181-
void *arg_table[] = {help_arg, dump_args_arg, force_arg, base_uri_arg, app_dir_arg, exec_arg, cwd_arg,
182-
temp_dir_arg, version_arg, pids_arg, interactive_arg, restart_arg, end_arg};
183+
void *arg_table[] = {help_arg, dump_args_arg, force_arg, base_uri_arg, app_dir_arg, exec_arg, cwd_arg,
184+
temp_dir_arg, version_arg, pids_arg, interactive_arg, restart_arg, details_arg, end_arg};
183185

184186
const int arg_table_sz = sizeof(arg_table) / sizeof(arg_table[0]);
185187

186188
int num_errors = arg_parse(argc, argv, arg_table);
187189

188190
/* We need type information to dump parameters generically */
189191
enum arg_type arg_table_types[arg_table_sz] = {ARG_LITERAL, ARG_LITERAL, ARG_LITERAL, ARG_STRING, ARG_STRING, ARG_STRING, ARG_STRING,
190-
ARG_STRING, ARG_STRING, ARG_INTEGER, ARG_INTEGER, ARG_LITERAL, ARG_END};
192+
ARG_STRING, ARG_STRING, ARG_INTEGER, ARG_INTEGER, ARG_LITERAL, ARG_STRING, ARG_END};
191193

192194
/* Here we assume that stdout is setup correctly, otherwise --help is pointless */
193195
if (help_arg->count > 0) {
@@ -295,6 +297,7 @@ bool su_parse_command_line(int argc, char **argv, struct update_parameters *para
295297

296298
params->pids = make_vector_from_arg(pids_arg);
297299
params->version.assign(version_arg->sval[0]);
300+
params->details.assign(details_arg->sval[0]);
298301

299302
if (interactive_arg->count > 0) {
300303
params->interactive = interactive_arg->ival[0];

Diff for: src/main.cc

+126-2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ struct callbacks_impl : public install_callbacks,
9494
HWND kill_button{NULL};
9595
HWND continue_button{NULL};
9696
HWND cancel_button{NULL};
97+
std::wstring update_btn_label;
98+
std::wstring remind_btn_label;
99+
std::wstring continue_label;
100+
std::wstring cancel_label;
97101

98102
HFONT main_font{NULL};
99103
RECT progress_label_rect{0};
@@ -116,6 +120,7 @@ struct callbacks_impl : public install_callbacks,
116120
bool should_continue{false};
117121
bool notify_restart{false};
118122
bool finished_downloading{false};
123+
bool prompting{false};
119124
LPCWSTR label_format{L"Downloading {} of {} - {:.2f} MB/s"};
120125

121126
callbacks_impl(const callbacks_impl &) = delete;
@@ -164,6 +169,8 @@ struct callbacks_impl : public install_callbacks,
164169
void update_file(std::string &filename) final {}
165170
void update_finished(std::string &filename) final {}
166171
void updater_complete() final {}
172+
173+
bool prompt_user(const char *pVersion, const char *pDetails);
167174
};
168175

169176
callbacks_impl::callbacks_impl(HINSTANCE hInstance, int nCmdShow)
@@ -233,8 +240,10 @@ callbacks_impl::callbacks_impl(HINSTANCE hInstance, int nCmdShow)
233240
std::wstring checking_packages_label = ConvertToUtf16WS(boost::locale::translate("Checking packages..."));
234241
std::wstring blockers_list_label = ConvertToUtf16WS(boost::locale::translate("Blockers list"));
235242
std::wstring stop_all_label = ConvertToUtf16WS(boost::locale::translate("Stop all"));
236-
std::wstring continue_label = ConvertToUtf16WS(boost::locale::translate("Continue"));
237-
std::wstring cancel_label = ConvertToUtf16WS(boost::locale::translate("Cancel"));
243+
continue_label = ConvertToUtf16WS(boost::locale::translate("Continue"));
244+
cancel_label = ConvertToUtf16WS(boost::locale::translate("Cancel"));
245+
update_btn_label = ConvertToUtf16WS(boost::locale::translate("Upgrade"));
246+
remind_btn_label = ConvertToUtf16WS(boost::locale::translate("Later"));
238247

239248
progress_label = CreateWindow(WC_STATIC, checking_packages_label.c_str(), WS_CHILD | WS_VISIBLE, x_pos, ui_padding, x_size, ui_basic_height, frame,
240249
NULL, NULL, NULL);
@@ -766,6 +775,116 @@ void callbacks_impl::updater_start()
766775
SetWindowTextW(progress_label, copying_label.c_str());
767776
}
768777

778+
bool callbacks_impl::prompt_user(const char *pVersion, const char *pDetails)
779+
{
780+
const wchar_t *wc_dash = L"-";
781+
const wchar_t *wc_hash = L"#";
782+
const wchar_t *wc_bullet = L"\r\n \u2022 ";
783+
const wchar_t *wc_break = L"\r\n ";
784+
std::wstring wc_version = ConvertToUtf16WS(boost::locale::translate(pVersion));
785+
std::wstring wc_label = ConvertToUtf16WS(boost::locale::translate("There's an update available to install: "));
786+
wc_label.insert(wc_label.size() - 1, wc_version); //account for null terminator
787+
std::wstring wc_details = ConvertToUtf16WS(boost::locale::translate(pDetails));
788+
789+
//tag to heading conversion
790+
std::list<std::pair<std::wstring, std::wstring>> tagsToHeadings;
791+
tagsToHeadings.push_back(std::make_pair(L"#hotfixes", L"Hotfix Changes"));
792+
tagsToHeadings.push_back(std::make_pair(L"#features", L"New Features"));
793+
tagsToHeadings.push_back(std::make_pair(L"#generalfixes", L"General Fixes"));
794+
795+
//format detail string: insert 2 breaks at start -> 1 to have heading on its own line, 1 for spacing, one after heading for spacing
796+
size_t replacePos = 0;
797+
for (const auto &tagHeadingPair : tagsToHeadings) {
798+
replacePos = wc_details.find(tagHeadingPair.first);
799+
if (replacePos != std::wstring::npos) {
800+
wc_details.replace(replacePos, tagHeadingPair.first.size(), tagHeadingPair.second);
801+
if (replacePos != 0) {
802+
wc_details.insert(replacePos, wc_break);
803+
replacePos += +wcslen(wc_break);
804+
}
805+
wc_details.insert(replacePos, wc_break);
806+
wc_details.insert(replacePos + wcslen(wc_break) + tagHeadingPair.second.size(), wc_break);
807+
}
808+
}
809+
//if # isn't followed by a known heading, leave as-is to display custom heading
810+
int endHeading = 0;
811+
replacePos = wc_details.find(wc_hash);
812+
while (replacePos != std::wstring::npos) {
813+
wc_details.insert(replacePos, wc_break);
814+
wc_details.replace(replacePos + wcslen(wc_break), wcslen(wc_hash), wc_break);
815+
endHeading = wc_details.find(wc_dash, replacePos);
816+
if (endHeading != std::wstring::npos) {
817+
wc_details.insert(endHeading, wc_break);
818+
}
819+
replacePos = wc_details.find(wc_hash, replacePos + wcslen(wc_break));
820+
}
821+
//replace '-' with end line + spacing + bullet + spacing
822+
replacePos = wc_details.find(wc_dash);
823+
while (replacePos != std::wstring::npos) {
824+
wc_details.replace(replacePos, wcslen(wc_dash), wc_bullet);
825+
replacePos = wc_details.find(wc_dash, replacePos + wcslen(wc_bullet));
826+
}
827+
//line break at end to look nicer
828+
wc_details.insert(wc_details.length(), wc_break);
829+
830+
prompting = true;
831+
ShowWindow(frame, SW_SHOWNORMAL);
832+
ShowWindow(progress_worker, SW_HIDE);
833+
HDC hdc = GetDC(frame);
834+
HFONT hfontOld = (HFONT)SelectObject(hdc, main_font);
835+
836+
DrawText(hdc, wc_label.c_str(), -1, &progress_label_rect, DT_CALCRECT | DT_NOCLIP);
837+
progress_label_rect.right -= progress_label_rect.left;
838+
blockers_list_rect = {0};
839+
DrawText(hdc, wc_details.c_str(), -1, &blockers_list_rect, DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL);
840+
841+
ReleaseDC(frame, hdc);
842+
SelectObject(hdc, hfontOld);
843+
844+
ShowWindow(blockers_list, SW_SHOW);
845+
ShowWindow(continue_button, SW_SHOW);
846+
ShowWindow(cancel_button, SW_SHOW);
847+
repostionUI();
848+
849+
SetWindowTextW(progress_label, wc_label.c_str());
850+
SetWindowTextW(blockers_list, wc_details.c_str());
851+
SetWindowTextW(continue_button, update_btn_label.c_str());
852+
SetWindowTextW(cancel_button, remind_btn_label.c_str());
853+
854+
MSG msg;
855+
bool shouldUpdate = false;
856+
while (GetMessage(&msg, NULL, 0, 0)) {
857+
if (!IsDialogMessage(frame, &msg)) {
858+
TranslateMessage(&msg);
859+
DispatchMessage(&msg);
860+
}
861+
if (should_cancel) {
862+
should_cancel = false;
863+
shouldUpdate = false;
864+
break;
865+
}
866+
if (should_continue) {
867+
should_continue = false;
868+
shouldUpdate = true;
869+
break;
870+
}
871+
}
872+
873+
//reset UI elements
874+
ShowWindow(frame, SW_HIDE);
875+
ShowWindow(blockers_list, SW_HIDE);
876+
ShowWindow(continue_button, SW_HIDE);
877+
ShowWindow(cancel_button, SW_HIDE);
878+
SetWindowTextW(blockers_list, L"");
879+
SetWindowTextW(progress_label, L"");
880+
SetWindowTextW(continue_button, continue_label.c_str());
881+
SetWindowTextW(cancel_button, cancel_label.c_str());
882+
883+
prompting = false;
884+
return shouldUpdate;
885+
}
886+
887+
769888
LRESULT CALLBACK ProgressLabelWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
770889
{
771890
switch (msg) {
@@ -956,6 +1075,11 @@ extern "C" int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpC
9561075
return 0;
9571076
}
9581077

1078+
if (!cb_impl.prompt_user(params.version.c_str(), params.details.c_str())) {
1079+
handle_exit();
1080+
return 1;
1081+
}
1082+
9591083
auto client_deleter = [](struct update_client *client) { destroy_update_client(client); };
9601084

9611085
std::unique_ptr<struct update_client, decltype(client_deleter)> client(create_update_client(&params), client_deleter);

Diff for: src/update-parameters.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct update_parameters {
2525
bool interactive = true;
2626
bool restart_on_fail = false;
2727
bool enable_removing_old_files = false;
28+
std::string details;
2829

2930
~update_parameters()
3031
{

0 commit comments

Comments
 (0)